Mengangkat lebih merupakan pola desain daripada konsep matematika (meskipun saya berharap seseorang di sekitar sini sekarang akan membantah saya dengan menunjukkan bagaimana lift adalah sebuah kategori atau sesuatu).
Biasanya Anda memiliki beberapa tipe data dengan parameter. Sesuatu seperti
data Foo a = Foo { ...stuff here ...}
Misalkan Anda menemukan banyak penggunaan Foo
tipe numerik take ( Int
, Double
dll) dan Anda tetap harus menulis kode yang membuka bungkus angka-angka ini, menambah atau mengalikannya, dan kemudian membungkusnya kembali. Anda dapat melakukan hubungan pendek ini dengan menulis kode unwrap-and-wrap sekali. Fungsi ini secara tradisional disebut "lift" karena terlihat seperti ini:
liftFoo2 :: (a -> b -> c) -> Foo a -> Foo b -> Foo c
Dengan kata lain, Anda memiliki fungsi yang mengambil fungsi dua argumen (seperti (+)
operator) dan mengubahnya menjadi fungsi yang setara untuk Foos.
Jadi sekarang Anda bisa menulis
addFoo = liftFoo2 (+)
Edit: informasi lebih lanjut
Anda tentu saja bisa memiliki liftFoo3
, liftFoo4
dan seterusnya. Namun hal ini seringkali tidak diperlukan.
Mulailah dengan observasi
liftFoo1 :: (a -> b) -> Foo a -> Foo b
Tapi itu sama persis dengan fmap
. Jadi daripada liftFoo1
Anda akan menulis
instance Functor Foo where
fmap f foo = ...
Jika Anda benar-benar ingin keteraturan lengkap Anda dapat mengatakan
liftFoo1 = fmap
Jika Anda bisa menjadikannya Foo
functor, mungkin Anda bisa menjadikannya sebagai functor aplikatif. Faktanya, jika Anda dapat menulis liftFoo2
maka contoh aplikatif terlihat seperti ini:
import Control.Applicative
instance Applicative Foo where
pure x = Foo $ ...
(<*>) = liftFoo2 ($)
The (<*>)
Operator untuk Foo memiliki jenis
(<*>) :: Foo (a -> b) -> Foo a -> Foo b
Ini menerapkan fungsi dibungkus ke nilai yang dibungkus. Jadi jika Anda dapat menerapkan liftFoo2
maka Anda dapat menulis ini dalam istilah itu. Atau Anda bisa mengimplementasikannya secara langsung dan tidak repot liftFoo2
, karena Control.Applicative
termasuk modul
liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
dan juga ada liftA
dan liftA3
. Tetapi Anda sebenarnya tidak terlalu sering menggunakannya karena ada operator lain
(<$>) = fmap
Ini memungkinkan Anda menulis:
result = myFunction <$> arg1 <*> arg2 <*> arg3 <*> arg4
Istilah myFunction <$> arg1
mengembalikan fungsi baru yang dibungkus dengan Foo. Ini pada gilirannya dapat diterapkan ke argumen berikutnya menggunakan (<*>)
, dan seterusnya. Jadi, sekarang alih-alih memiliki fungsi lift untuk setiap wilayah, Anda hanya memiliki rangkaian aplikasi daisy chain.