閱讀解決型「自己寫的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來說也是如此。鑑於整個函數返回IOThrowsError LispVal
(這只是ErrorT LispError IO LispVal
,即IO Either LispError LispVal
),所以m必須是IOThrowsError
[2]。
現在,如果有不同的monad變換器,我想可能會有一個以上的liftIO具有不同類型的簽名。實際情況是這樣嗎?如果是這樣,上面的推理鏈在多大程度上代表了Haskell如何確定類型?我對[1]或[2]中的推理不滿意,因此還有一個關於如何確定liftIO的monad的第二個問題。最後,對於do語句,怎麼知道它在哪個monad中?這是由<-
之後的第一個monad決定的嗎?或者以其他方式?
謝謝。所以do子句的返回必須與語句的返回類型相匹配,這意味着'liftIO'需要返回'IOThrowsError'值(因爲一個是do子句的返回值,另一個是< - 應用於它的值)。但是第一個'liftIO'在'IO [(String,IORef LispVal)]'上運行,所以我不知道它的結果類型是什麼。 – 2014-09-02 15:56:39
'liftIO'的類型是'MonadIO m => IO a - > m a'。由於您的'IOThrowsError a'類型只是'ErrorT LispVal IO a',而'MonadIO m => ErrorT ema'是'MonadIO'的一個實例,所以可以將'''''''''''''''' - > ErrorT LispVal IO a'。 – bheklilr 2014-09-02 16:18:30