۱۹ - ۴نمونههای Foldable
همونطور که گفتیم، یه نمونه ِ Foldable
باید حداقل یکی از متودهای foldr
یا foldMap
رو داشته باشه. هر تابعِ دیگه از تایپکلاس رو میشه با یکی از اون دو تابع بدست آورد. با توجه به این موضوع، بریم سراغ پیادهسازیِ نمونههای Foldable
برای تایپهای مختلف.
Identity
با نوشتن یه نمونه ِ Foldable
برای Identity
شروع میکنیم:
data Identity a =
Identity a
فقط کافیه foldr
یا foldMap
رو بنویسیم، اما برای یادگیری هردوی اونها، بعلاوهی foldl
رو تعریف میکنیم.
instance Foldable Identity where
foldr f z (Identity x) = f x z
foldl f z (Identity x) = f z x
foldMap f (Identity x) = f x
کاری که با foldr
و foldl
انجام دادیم در واقع یکیاند، فقط آرگومانهاشون جابجا شدن. برای foldMap
هم لازم نبود کارِ خاصی انجام بدیم.
فولد کردنِ فقط یک مقدار شاید عجیب به نظر برسه. قبلاً که از کاتامورفیسمها صحبت کردیم، همیشه تمرکزمون روی کاهش ِ چندتا مقدار به یک مقدار خلاصه بوده. ولی در موردِ این کاتامورفیسم ِ Identity
، محوریت بیشتر روی مصرف یا استفاده از مقداره، تا کاهش ِ مقادیرِ داخلِ ساختار به یک مقدار:
Prelude> foldr (*) 1 (Identity 5)
5
Prelude> foldl (*) 5 (Identity 5)
25
Prelude> let fm = foldMap (*5)
Prelude> type PI = Product Integer
Prelude> fm (Identity 100) :: PI
Product {getProduct = 500}
Maybe
این یکی یه ذره جذابتره چون برخلافِ Identity
، باید حالتهای Nothing
رو هم در نظر بگیریم. وقتی مقدارِ Maybe
ای که داریم فولد میکنیم Nothing
ِه، باید بتونیم بدون انجامِ کاری با تابعِ فولدینگ، یه جور مقدارِ "صفر" برگردونیم و ساختار ِ Maybe
رو هم حذف کنیم. این مقدارِ صفر در foldr
و foldl
همون مقدار اولیهست:
Prelude> foldr (+) 1 Nothing
1
از طرفِ دیگه برای foldMap
از مقدارِ همانی ِ Monoid
به عنوانِ اون صفر استفاده میکنیم:
Prelude> let fm = foldMap (+1)
Prelude> fm Nothing :: Sum Integer
Sum {getSum = 0}
وقتی مقدار، یه مقدارِ Just
ِه، باید تابع فولدینگ رو به مقدارِ داخلش اعمال کنیم و ساختار رو دور بندازیم:
Prelude> foldr (+) 1 (Just 3)
4
Prelude> fm $ Just 3 :: Sum Integer
Sum {getSum = 4}
حالا خودِ نمونه رو ببینیم، باز هم برای اجتناب از تداخل با نمونه ِ Maybe
که وجود داره، از یه تایپِ Maybe
ِ قلابی استفاده میکنیم:
instance Foldable Optional where
foldr _ z Nada = z
foldr f z (Yep x) = f x z
foldl _ z Nada = z
foldl f z (Yep x) = f z x
foldMap _ z Nada = mempty
foldMap f z (Yep a) = f a
دقت کنین که اگه نَگین کدوم Monoid
منظورتون بوده، از مبهم بودن تایپ ایراد میگیره:
Prelude> foldMap (+1) Nada
No instance for (Num a0) arising
from a use of ‘it’
The type variable ‘a0’ is ambiguous
(...شلوغی...)
پس اگه بخوایم کار کنه باید تایپی که Monoid
داره براش تعیین کنیم:
Prelude> import Data.Monoid
Prelude> foldMap (+1) Nada :: Sum Int
Sum {getSum = 0}
Prelude> foldMap (+1) Nada :: Product Int
Product {getProduct = 1}
Prelude> foldMap (+1) (Just 1) :: Sum Int
Sum {getSum = 2}
با مقدارِ Nada
و تعیینِ تایپِ Sum Int
(برای مشخص کردنِ Monoid
)، تابعِ foldMap
جوابِ Sum 0
داد چون mempty
یا مقدار همانی برای Sum
ِه. بطورِ مشابه برای Nada
و Product
، جواب Product 1
شد چون مقدارِ همانی ِ Product
بود.