Haskell Free Monad的学习记录

Haskell Free Monad的学习记录

看了一些博客,但是感觉都讲的好高大上,没看看懂,决定写写自己对Free Monad的理解。本人是学生,仅在课程中粗浅的使用到了Free Monad,因此写的很简单,都是个人的理解,大佬轻喷。

感觉可以从三个角度理解Free Monad:

  1. 这个Free Monad有什么用
  2. Free Monad的解耦能力
  3. 对Free Monad 的bind操作的理解

Free Moand有什么用?

个人感觉是两个用:

Free理解成“免费”的意思,它可以让我们在定义了一个Functor 后,直接当成Monad来使用。

这里证明当 e 是functor时, Free e a 是Monad。

在这里插入图片描述

Free Monad的解耦能力

Free Monad一般都有3部分的内容:

  1. 对外的构造函数 (输入形式自定,输出一个Free Monad)
  2. Free Monad的具体逻辑
  3. 解释器
-- 这里的是对Free Monad的基本的定义
data Free e a
  = Pure a
  | Free (e (Free e a))

instance (Functor e) => Functor (Free e) where
  fmap f (Pure x) = Pure $ f x
  fmap f (Free g) = Free $ fmap (fmap f) g

instance (Functor e) => Applicative (Free e) where
  pure = Pure
  (<*>) = ap

instance (Functor e) => Monad (Free e) where
  Pure x >>= f = f x
  Free g >>= f = Free $ h <$> g
    where
      h x = x >>= f

-- 这里的是Free e a 中的 e a
data EvalOp a
  = ReadOp (Env -> a)
  | ErrorOp Error
  | KvGetOp Val (Val -> a)
  | KvPutOp Val Val a

-- 定义Free e a 中e 作为一个Functor的操作
instance Functor EvalOp where
  fmap f (ReadOp c) = ReadOp $ f . c
  fmap f (KvGetOp key k) = KvGetOp key $ f . k
  fmap f (KvPutOp key val m) = KvPutOp key val $ f m
  fmap _ (ErrorOp e) = ErrorOp e

-- 对外的构造函数,可以从给定的形式转为Free Monad
evalKvGet :: Val -> EvalM Val
evalKvGet key = Free $ KvGetOp key $ \val -> pure val

evalKvPut :: Val -> Val -> EvalM ()
evalKvPut key val = Free $ KvPutOp key val $ pure ()

askEnv :: EvalM Env
askEnv = Free $ ReadOp $ \env -> pure env

如下是一个解释器的例子:

-- 一个解释器的例子,拿到一个Free Monad进行具体的操作
runEval :: EvalM a -> Either Error a
runEval = fmap fst $ runEval' envEmpty stateInitial
  where
    runEval' :: Env -> State -> EvalM a -> (Either Error a, State)
    runEval' _ s (Pure x) = (pure x, s)
    runEval' r s (Free (ReadOp k)) = runEval' r s $ k r
    runEval' r s (Free (KvGetOp key k)) =
      case lookup key s of
        Nothing -> (Left $ "Invalid key: " ++ show key, s)
        Just val -> runEval' r s $ k val
    runEval' r s (Free (KvPutOp key val m)) =
      let s' = (key, val) : filter ((/= key) . fst) s
       in runEval' r s' m
    runEval' _ s (Free (ErrorOp e)) = (Left e, s)

在这一整套的逻辑下:

  • 构造函数负责解析外界的内容为一个Free Monad。
  • Free Monad 中的内容为对要执行的操作的抽象。
    • 比如我需要执行一个“读取环境信息”的操作时,就写一个 ReadOp 的操作,来代表在此处需要进行一个“读取环境信息”的操作。
  • 解释器负责解析Free Monad中的内容,将其转为实际的操作。
    • Free Monad仅有抽象的操作行为,但是不会设计具体的实现,好处就是允许针对同一个Free Monad编写不同的解释器,使其在不同的使用环境中产生不同的效果。
    • 同样是ReadOp ,可以在一个解释器中被实际的执行为返回一个变量的值,可以在另一个解释器中被执行为读取特定的文件。

在这里插入图片描述

对Free Monad 的bind(>>=)操作的理解

我第一次学Free Monad的时候怎么都没看懂 >>= 写的那么怪异的原因,后来问了问ChatGPT才大概看懂。(这么抽象的写法属实是加深了我对Haskell 就是奇技淫巧的刻板印象
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值