理解 Monad Transformers 的困难

Sup*_*zel 3 monads haskell functional-programming monad-transformers

我希望获得一些有助于理解 Monad Transformer 的信息,以及与此相关的使用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

Run Code Online (Sandbox Code Playgroud)

据我了解,该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
Run Code Online (Sandbox Code Playgroud)

据我了解,Readerfrom insideProtected在 , 上运行ProtectedData以返回值。

只剩下函数access

access :: String -> Protected a a
access pass = do  
                pd <- lift ask
                let v = accessData pass pd
                MaybeT $ return v
Run Code Online (Sandbox Code Playgroud)

这是最让我头疼的一个。首先,我在把握效果和结果方面遇到了问题。

  • 该函数是否用于将密码和数据注入Reader
  • 是否用于访问数据,如果输入错误的密码就会失败?

其次,我无法理解第一行

pd <- lift ask
Run Code Online (Sandbox Code Playgroud)
  • 我明白,它ask用于通过 访问共享环境Reader,但为什么我必须使用liftMaybeT来获取其中的实际值?

chi*_*chi 6

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

No.应该被视为返回 type 值的程序Protected s a的类型。在计算过程中,程序只有在“知道”正确的密码时才能只读访问 类型的秘密值。as

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

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

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

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

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

签名

access :: String -> Protected a a
Run Code Online (Sandbox Code Playgroud)

如果我们把它写成

access :: String -> Protected s s
Run Code Online (Sandbox Code Playgroud)

表明它是一个在尝试密码时访问秘密值(或失败)的辅助函数。它的使用方法如下:

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

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

> run (ProtectedData "actual password" 42) myProg
Nothing
Run Code Online (Sandbox Code Playgroud)

如果密码正确,它将产生正确的布尔值:

> run (ProtectedData "123456" 42) myProg
Just True
Run Code Online (Sandbox Code Playgroud)

这里Just表示密码正确,True表示保护Int正确。