理解 Monad 变形金刚的困难

Posted

技术标签:

【中文标题】理解 Monad 变形金刚的困难【英文标题】:Difficulties understanding Monad Transformers 【发布时间】:2021-11-02 21:47:05 【问题描述】:

我希望获得一些有助于理解 Monad Transformers 的信息,以及与此相关的使用 do 表示法会发生什么。我试图理解的示例如下:

data ProtectedData a = ProtectedData String a

accessData :: String -> ProtectedData a -> Maybe a
accessData s (ProtectedData pass v) =
    if s == pass then Just v else Nothing


type Protected s a = MaybeT (Reader (ProtectedData s)) a

-- untangles the monad construction
run :: ProtectedData s -> Protected s a -> Maybe a
run ps psa = runReader (runMaybeT psa) ps

access :: String -> Protected a a
access pass = do
                -- ask :: ReaderT (ProtectedData a) Identity (ProtectedData a) 
                -- lift :: ... -> MaybeT (ReaderT (ProtectedData a) Identity) (ProtectedData a)  
                pd <- lift ask
                -- as i understand it: ask returns the value inside the thing.
                -- the left arrow actually applies the monad
                let v = accessData pass pd
                -- return :: Maybe a -> Reader (ProtectedData a) (Maybe a)
                MaybeT $ return v

据我了解,Protected 类型描述了一些 受保护 数据,这些数据存储在共享环境 (Reader) 中,属于 Maybe (MaybeT) 类型。

我在使用类型变量 sa 时遇到问题:

s 是否描述受保护数据的字符串(密码),a 受保护数据的类型? s 是否描述了受保护数据的类型,如果是,是什么 a描述?

在函数中运行:

run :: ProtectedData s -> Protected s a -> Maybe a
run ps psa = runReader (runMaybeT psa) ps

据我了解,Protected 内部的ReaderProtectedData 上运行,以返回值。

这里只剩下函数access

access :: String -> Protected a a
access pass = do  
                pd <- lift ask
                let v = accessData pass pd
                MaybeT $ return v

哪个是最让我头疼的。首先,我在掌握效果和结果方面存在问题。

这个函数是用来向Reader注入密码和数据的吗? 是否用于访问数据,如果输入错误密码失败?

其次,我无法理解第一行

pd <- lift ask
我了解,ask 用于访问共享环境 通过Reader,但是为什么我要lift它到MaybeT才能得到 里面的实际值?

【问题讨论】:

【参考方案1】:

据我了解,受保护类型描述了一些“受保护”数据

没有。 Protected s a 应该被视为返回a 类型值的程序 的类型。在计算过程中,程序对s 类型的秘密值具有只读访问权限,并且只有在它“知道”正确密码的情况下。

这样的秘密值,与其密码配对,类型为ProtectedData s

s 是否描述了受保护数据的类型,如果是,a 描述了什么?

是的。这里a 是程序结果的通用类型。

举个例子,您可以考虑密码是String(必须是,在您的代码中字符串类型是硬编码的)并且秘密值的类型为s = Int 的情况。然后你编写一个访问秘密整数的程序,并检查它是否为正,返回一个Bool。在这里,a = Bool

请注意,我稍微简化了场景。因为我们也使用MaybeT,所以我们正在对一个并不总是返回a 类型的值的程序进行建模,但这也可能会失败。使用错误的密码可能会导致失败。在这种情况下,MaybeT 在程序执行过程中大致中止程序。

签名

access :: String -> Protected a a

如果我们写成这样可能会更好理解

access :: String -> Protected s s

表明它是一个辅助函数来访问秘密值(或失败),给定密码尝试。用法如下:

myProg :: Protected Int Bool
myProg = do
  v <- access "123456"  -- try accessing the protected int
  return (v > 0)

如果密码错误,上面的代码会导致失败(run会返回Nothing

> run (ProtectedData "actual password" 42) myProg
Nothing

如果密码正确,则会产生正确的布尔值:

> run (ProtectedData "123456" 42) myProg
Just True

这里Just表示密码正确,True表示受保护的Int是正数。

【讨论】:

谢谢,意识到将Protected s a 视为返回类型为a 的程序访问s 类型的受保护数据确实帮助我理解了这个概念。如果我理解正确,这意味着我什至可以在我的程序中使用其他 Monad:giveEven :: String -&gt; Protected [Int] [Int] giveEven s = do v &lt;- access s return (filter even v) @Superschnitzel 您的代码是正确的,但您并没有真正将[] 用作那里的单子。不过,你可以,但可以说它是“正交的”。 s 可以是任何类型,包括单子类型,但不会干扰主单子 Protected s

以上是关于理解 Monad 变形金刚的困难的主要内容,如果未能解决你的问题,请参考以下文章

Windows Phone开发(18):变形金刚第九季

了解拥抱脸变形金刚

CSS3中的变形处理

树状数组求逆序数及变形(个人理解)

lammps中fix deform的理解

《算法零基础100讲》(第58讲) 前缀和 线性前缀和变形