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