۲۰ - ۴تابع 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 کاهیده شده.