Jadi cara terbaik untuk memahaminya adalah dengan melakukannya. Di bawah ini ada implementasi foldlM
penggunaanfoldl
alih-alih foldr
. Ini latihan yang bagus, coba dan dapatkan solusi yang saya sarankan. Contoh menjelaskan semua alasan yang saya lakukan untuk mencapainya, yang mungkin berbeda dari Anda, dan mungkin bias karena saya sudah tahu tentang menggunakan fungsi akumulator.
Langkah 1 : Mari kita coba menulis foldlM
dalam bentukfoldl
-- this doesn't compile because f returning type is (m b) and not just (b)
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f z0 xs
-- So let substitute f by some undefined f'
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' z0 xs
where f' = undefined
-- cool, but f' should use f somehow in order to get the monadic behaviour
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' z0 xs
where f' b a = f somethingIDontkNow
Di sini Anda menyadari bahwa f'
itu murni dan Anda harus mengekstraksi hasil f
mengetik. Satu-satunya cara untuk 'mengekstraksi' nilai monadik adalah dengan>>=
operator, tetapi operator seperti itu harus dibungkus tepat setelah digunakan.
Jadi sebagai kesimpulan: Setiap kali Anda berakhir dengan saya ingin sepenuhnya membuka monad ini , menyerah saja. Bukan jalan yang benar
Langkah 2 : Mari kita coba menulis foldlM
dalam bentuk foldl
tetapi pertama menggunakan []
sebagai lipat, karena mudah untuk mencocokkan pola (yaitu kita tidak benar-benar perlu menggunakan fold
)
-- This is not very hard. It is pretty standard recursion schema. :)
foldlM' :: (Monad m) => (b -> a -> m b) -> b -> [a] -> m b
foldlM' f z0 [] = return z0
foldlM' f z0 (x:xs) = f z0 x >>= \c -> foldlM' f c xs
Oke, itu mudah. Mari kita bandingkan definisi dengan foldl
definisi biasa untuk daftar
foldlM' :: (Monad m) => (b -> a -> m b) -> b -> [a] -> m b
foldlM' f z0 [] = return z0
foldlM' f z0 (x:xs) = f z0 x >>= \c -> foldlM' f c xs
myfoldl :: (b -> a -> b) -> b -> [a] -> b
myfoldl f z0 [] = z0
myfoldl f z0 (x:xs) = foldl f (f z0 x) xs
Keren!! mereka hampir sama. Kasus sepele adalah tentang hal yang persis sama. Kasus rekursif adalah sedikit berbeda sedikit, Anda ingin menulis sesuatu yang lebih seperti: foldlM' f (f z0 x) xs
. Tetapi tidak dikompilasi seperti pada langkah 1, jadi Anda mungkin berpikir OK, saya tidak ingin menerapkan f
, hanya untuk memegang perhitungan seperti itu dan menyusunnya >>=
. Saya ingin menulis sesuatu yang lebih suka foldlM' f (f z0 x >>=) xs
jika itu masuk akal ...
Langkah 3 Sadarilah bahwa yang ingin Anda kumpulkan adalah komposisi fungsi dan bukan hasil. (di sini saya mungkin bias oleh fakta bahwa saya sudah mengetahuinya karena Anda telah mempostingnya ).
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' initFunc xs
where initFunc = undefined :: b -> m b
f' = undefined :: (b -> m b) -> a -> (b -> m b) -- This type signature can be deduce because f' should be applied to initFunc and a's from t a.
Dengan tipe initFunc
dan menggunakan pengetahuan kita dari langkah 2 (definisi rekursif) kita dapat menyimpulkan itu initFunc = return
. Definisi f'
dapat diselesaikan mengetahui yang f'
harus digunakan f
dan >>=
.
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' return xs z0
-- ^^^^^^
-- |- Initial value
where f' b a = \bvalue -> b bvalue >>= \bresult -> f bresult a -- this is equivalent to (b >=> \result -> f result a) which captures the sequence behaviour of the implementation
-- ^ ^^^^^^ ^^^^^^^
-- | | |- This is the result of previous computation
-- | |- f' should return a function b -> m b. Any time you have to return a function, start writing a lambda
-- |- This b is the accumulated value and has type b -> m b
-- Following the types you can write this with enough practise
Seperti yang Anda lihat, tidak sangaaaat sulit untuk melakukannya. Itu perlu latihan, tapi saya bukan pengembang haskell profesional dan saya bisa melakukannya sendiri, ini soal latihan