阅读“48小时内为自己写一个方案”,对此页感到困惑https://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_Hours/Adding_Variables_and_Assignment作者:
getVar :: Env -> String -> IOThrowsError LispVal
getVar envRef var = do env <- liftIO $ readIORef envRef
maybe (throwError $ UnboundVar "Getting an unbound variable" var)
(liftIO . readIORef)
(lookup var env)
我不完全清楚类型是如何解析的。这是我的推理:
envRef
类型为IORef [(String, IORef LispVal)]
,所以readIORef envRef
类型为IO [(String, IORef LispVal)]
.
现在LiftIO
定义于 http://hackage.haskell.org/package/transformers-0.4.1.0/docs/Control-Monad-IO-Class.html作为 liftIO :: IO a -> m a
类型并在 https://hackage.haskell.org/package/mtl-1.1.0.2/docs/src/Control-Monad-Error.html#ErrorT 实现。所以liftIO $ readIORef envRef
返回m [(String, IORef LispVal)]
对于某些 monad m (这并不重要,因为我们直接使用 <-
,所以忽略 monad 包装器)[1]。
这意味着 env 是 [(String, IORef LispVal)]
,所以lookup var env
是 Maybe IORefLispVal
。对于“也许”的“Just”分支,liftIO . readIORef
将是m LispVal
,再次针对一些 monad m。给定整个函数返回 IOThrowsError LispVal
(这只是 ErrorT LispError IO LispVal
即 IO Either LispError LispVal
),所以 m 必须是 IOThrowsError
[2].
现在,如果有不同的 monad 转换器,我猜想可能有多个 liftIO 在范围内具有不同的类型签名。实际上是这样吗?如果是这样,上面的推理链在多大程度上代表了 Haskell 实际上如何确定类型?我对 [1] 或 [2] 的推理不满意,所以还有一个次要问题,即 liftIO 的 monad 是如何确定的。最后,对于 do 语句, do 如何知道它在哪个 monad 中?这是由 <-
之后的第一个 monad 决定的吗? ?或者其他方式?
最佳答案
当您有 liftIO $ readIORef envRef
且类型为 MonadIO m => m [(String, IORef LispVal)]
时,m
当然很重要!它只能是一个实现 MonadIO
类型类的 monad,并且不能被忽略。它必须与 maybe ...
语句返回的 monad 相同。一般来说,当使用 do 表示法时,给定 block 内的所有语句都必须具有相同的 monad,因此您可以这样做
main :: IO ()
main = do
putStrLn "Testing"
x <- getLine
putStrLn x
但是你不能做
main :: IO ()
main = do
putStrLn "Testing"
x <- Just "Doesn't work!"
putStrLn x
因为IO
和Maybe
不在同一个monad中!最重要的是,main
被指定为 IO ()
类型,因此您没有任何机会返回 IO
以外的内容> 行动。
如果您不确定这也可以泛化到什么类型,我建议将其加载到 GHCi 中并在不指定类型签名的情况下检查它。我相信它最终会成为类似的东西
getVar :: (Eq a, MonadIO m, MonadError LispVal m) => IORef [(a, IORef LispVal)] -> a -> m b
因为 liftIO $ readIORef envRef
和 liftIO 。 readIORef
意味着 monad 必须是 MonadIO
的实例,为了执行查找
,您需要您的键是 Eq 的实例
,并且 throwError $UnboundVar "..."
意味着它必须是 MonadError LispVal
的实例。没有什么可以具体化签名的其余部分来返回 LispVal
,或者是您正在使用的 IOThrowsError
的特定 monad。
关于haskell - 这个语句中的类型是如何解析的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25626867/