haskell - 这个语句中的类型是如何解析的

标签 haskell types functional-programming monads monad-transformers

阅读“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 envMaybe IORefLispVal 。对于“也许”的“Just”分支,liftIO . readIORef将是m LispVal ,再次针对一些 monad m。给定整个函数返回 IOThrowsError LispVal (这只是 ErrorT LispError IO LispValIO 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

因为IOMaybe不在同一个monad中!最重要的是,main 被指定为 IO () 类型,因此您没有任何机会返回 IO 以外的内容> 行动。

如果您不确定这也可以泛化到什么类型,我建议将其加载到 GHCi 中并在不指定类型签名的情况下检查它。我相信它最终会成为类似的东西

getVar :: (Eq a, MonadIO m, MonadError LispVal m) => IORef [(a, IORef LispVal)] -> a -> m b

因为 liftIO $ readIORef envRefliftIO 。 readIORef 意味着 monad 必须是 MonadIO 的实例,为了执行查找,您需要您的键是 Eq 的实例,并且 throwError $UnboundVar "..." 意味着它必须是 MonadError LispVal 的实例。没有什么可以具体化签名的其余部分来返回 LispVal,或者是您正在使用的 IOThrowsError 的特定 monad。

关于haskell - 这个语句中的类型是如何解析的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25626867/

相关文章:

haskell - Haskell 中的类型与数据性能

c++ - 数组下标的无效类型 'double [100][double]'

c# - 在 C# 中生成函数的最佳生成函数的方法

math - 泛化算术运算符

haskell - 一次处理列表 1..n 个元素,无需显式递归

haskell - 如何使用 Happstack 创建 JSON Rest API? JSON 正文?

haskell - 这如何构成 Haskell 刚性类型错误?

functional-programming - SML/NJ 新手。从列表列表中添加数字

haskell - 构建相同的镜头

haskell - 应用参数可以是 Ints 或 Doubles 的函数