۲۰ - ۴تابع traverse
حالا تایپِ traverse
رو ببینیم:
traverse
:: (Applicative f, Traversable t)
=> (a -> f b) -> t a -> f (t b)
شاید متوجهِ شباهتِ بین تایپِ traverse
با تایپِ fmap
و (=<<)
(بایندی که جابجا شده) شده باشین:
fmap :: (a -> b) -> f a -> f b
(=<<) :: (a -> m b) -> m a -> m b
traverse :: (a -> f b) -> t a -> f (t b)
اینجا هم مثل fmap
داریم یه تابع رو از روی یه ساختار نگاشت میکنیم؛ و مثل (=<<)
، خودِ تابع هم ساختار تولید میکنه. اما برخلافِ (=<<)
، اون ساختاری که تابع تولید میکنه ممکنه متفاوت از ساختاری باشه که تابع رو از روش نگاشت کردیم. در نهایت هم مثل sequenceA
، دوتا ساختار رو با هم جابجا میکنه.
در حقیقت، همونطور که در تعریف تایپکلاس هم دیدیم، traverse
با ترکیب ِ fmap
با sequenceA
برابره:
traverse f = sequenceA . fmap f
در عمل ببینیم چطور کار میکنه:
Prelude> fmap Just [1, 2, 3]
[Just 1,Just 2,Just 3]
Prelude> sequenceA $ fmap Just [1, 2, 3]
Just [1,2,3]
Prelude> sequenceA . fmap Just $ [1, 2, 3]
Just [1,2,3]
Prelude> traverse Just [1, 2, 3]
Just [1,2,3]
جلوتر مثالهای طولانیتر هم میبینیم، ولی ایدهی کلی اینه که هر وقت داشتین از sequenceA . fmap f
استفاده میکردین، بدونین که با traverse
در یک مرحله به همون جواب میرسین.
mapM
همون traverse
ِه
ممکن هست قبلاً راهی یه ذره متفاوت برای بیانِ traverse
دیده باشین: با mapM
.
در نسخههای قبلتر از GHC 7.10، تایپ mapM
اینطوری بود:
mapM :: Monad m
=> (a -> m b) -> [a] -> m [b]
-- با این مقایسه کنین
traverse :: Applicative f
=> (a -> f b) -> t a -> f (t b)
میشه گفت traverse
از Traversable
در واقع []
در mapM
رو به هر ساختارِ داده ِ پیمایشیای انتزاعی میکنه، و همچنین محدودیت ِ Monad
رو تعمیم میده تا فقط Applicative
لازم باشه. ارزشِ اینها اینجاست که میشه از این الگو گستردهتر و در کُدهای بیشتری استفاده کرد. برای مثال، نوعداده ِ لیست برای تعدادِ کمِ مقادیر خوبه، اما در کاربردهایی که عملکرد خیلی مهمه، ممکنه نوعداده ِ Vector
از کتابخونه ِ vector
کاراتَر باشه. با traverse
دیگه لازم نیست کُد رو تغییری بدین، چون نوعداده ِ Vector
یه نمونه از Traversable
داره.
بطور مشابه، تایپ sequence
هم، در نسخههای قبلیِ GHC 7.10 کاربرد کمتری از sequenceA
داشته:
sequence :: Monad m
=> [m a]
-> m [a]
-- با این مقایسه کنین
sequenceA :: (Applicative f, Traversable t)
=> t (f a)
-> f (t a)
دوباره لیست به هر Traversable
ای تعمیم داده شده، سختگیریِ Monad
هم به Applicative
کاهیده شده.