۱۹ - ۴نمونههای 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 بود.