理解 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)
类型。
我在使用类型变量 s
和 a
时遇到问题:
s
是否描述受保护数据的字符串(密码),a
受保护数据的类型?
s
是否描述了受保护数据的类型,如果是,是什么
a
描述?
在函数中运行:
run :: ProtectedData s -> Protected s a -> Maybe a
run ps psa = runReader (runMaybeT psa) ps
据我了解,Protected
内部的Reader
在ProtectedData
上运行,以返回值。
这里只剩下函数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 -> Protected [Int] [Int] giveEven s = do v <- access s return (filter even v)
@Superschnitzel 您的代码是正确的,但您并没有真正将[]
用作那里的单子。不过,你可以,但可以说它是“正交的”。 s
可以是任何类型,包括单子类型,但不会干扰主单子 Protected s
。以上是关于理解 Monad 变形金刚的困难的主要内容,如果未能解决你的问题,请参考以下文章