(Vi) So sánh functor, applicative và monad

Posted on December 7, 2022 by lk
Tags: haskell, monad, functor, applicative

Tóm tắt nội dung

Bài viết này sẽ trình bày về sự liên quan giữa các type class, bạn có thể tìm hiểu thêm về các type class này ở link video đính kèm trong bài viết.

Review về kiểu của các hàm ($), (<>), (<$>), (<*>), (>>=) hay flip bind (=<<)

Xét hàm số f :: a -> b, các phép biến đổi bên dưới là tương đương

f             :: a -> b
f a           :: b              -- apply a vào f
(a -> b) -> a :: b              -- thay f bằng (a -> b)
($)           :: (a->b)->a->b   -- chuyển vế b 

Thực tế ta sẽ thấy các đoạn code sau

print $ show a

Chúng ta có thể thấy rằng (<>) hay mappend nhận vào 2 tham số cùng kiểu (type),và trả về kết quả mà vẫn giữ nguyên cấu trúc (structure)

putStrLn $ "hello" <> " " <> "world!"

(\x -> x * 2) <$> [1..2]

getSum $ fold $ Sum <$> [1..5]
pure (\x -> x * 2) <*> [1..2]

getSum $ fold $ pure Sum <*> [1..5]

Ta thấy có nhiều trường hợp, cần phải lấy được giá trị a ra khỏi context tính toán nào đó (ký hiệu là m).

Ví dụ bên dưới minh hoạ về trường hợp chúng ta cần lấy giá trị a ra khỏi context Maybe sau đó dùng hàm + để tính tổng, sau đó để giá trị sau khi tính tổng vào lại trong context Maybe.

chú ý: trong các bài viết thì chúng ta thường thấy thuật ngữ monadic function, ở đây chính là hàm a -> m b, tức là một hàm số nhận vào một giá trị a (chưa nằm trong context tính toán nào, hoặc chưa nào trong một container nào có side effect), và trả về một giá trị b nào đó nằm trong context m nào đó.

maybePlus :: Maybe Int -> Maybe Int -> Maybe Int
maybePlus ma mb =
  case ma of
    Nothing -> Nothing
    Just a ->
      case mb of
        Nothing -> Nothing
        Just b  -> Just (a + b)

-- helper function
andThen :: Maybe a -> (a -> Maybe b) -> Maybe b
andThen ma f =
  case ma of
    Nothing -> Nothing
    Just a  -> f a

-- using helper function to refactor
maybePlus' :: Maybe Int -> Maybe Int -> Maybe Int
maybePlus' ma mb = ma `andThen` \a -> mb `andThen` \b -> Just (a + b)

Mối quan hệ giữa ($) (<>) <*>

($)   ::   (a -> b)   ->   a ->   b
(<>)  :: f            -> f   -> f 
(<*>) :: f (a -> b)   -> f a -> f b

Ta có thể thấy rằng, (<*>) là sự kết hợp giữa ($)(<>)

Mối quan hệ giữa <$> (=<<)

(<$>) :: (a -> b)  -> f a -> f b

(=<<) :: (a-> m b) -> m a -> m b

Ta có thể thấy rằng bản chất của flip bind (=<<) là sự kết hợp theo trình tự: hàm fmap(hay (<$>)) và sau đó là hàm join :: Monad m => m (m a) -> m a. Bạn có thể đọc thêm tại đây