我正在嘗試為簡單的嵌入式腳本語言撰寫解釋器。
它的核心是 eval 函式,它具有以下簽名。
type EvalState = () --for later
type EvalResult = State EvalState (IO (Either T.Text Value))
eval :: Expr -> EvalResult
結果型別是這樣的,因為它是有狀態的,eval 應該可以做 IO,它可能會失敗。
有兩種資料型別:運算式和值,而 eval 將運算式轉換為值。對于簡單的運算式,原始資料型別的文字,它是這樣實作的:
ok :: Value -> EvalResult
ok val = state (return . Right $ val,)
eval :: Expr -> EvalResult
eval (StrLit t) = ok (StrVal t)
eval (SymLit t) = ok (SymbolVal t)
eval (IntLit i) = ok (IntVal i)
eval (FloatLit f) = ok (FloatVal f)
我現在的問題是為串列文字實作 eval 。目前我的代碼如下所示:
eval (ListLit elems) = do
elems <- mapM eval elems
do
opts <- sequence elems
return $ fmap (Right . ListVal . V.fromList) (sequence opts)
這會產生以下錯誤:
/home/felix/git/vmail/src/Interpreter/EvalAst.hs:37:13: error:
? Couldn't match type ‘IO’
with ‘StateT EvalState Data.Functor.Identity.Identity’
Expected: StateT
EvalState Data.Functor.Identity.Identity [Either T.Text Value]
Actual: IO [Either T.Text Value]
? In a stmt of a 'do' block: opts <- sequence elems
In a stmt of a 'do' block:
do opts <- sequence elems
fmap (Right . ListVal . V.fromList) (sequence opts)
In the expression:
do elems <- mapM eval elems
do opts <- sequence elems
fmap (Right . ListVal . V.fromList) (sequence opts)
|
37 | opts <- sequence elems
| ^^^^^^^^^^^^^^
我的問題是我不理解這個錯誤。我的想法是這樣的:第一個 do 把我放在 State monad 中,所以我應該能夠“提取”mapM eval elems我期望的結果[ IO (Either ...) ]下一個 do 應該把我放在 IO monad 中(因為這是下一個內部型別在結果型別中),所以我應該能夠提取 IO 值,據我了解,sequence elems應該給我一個IO [ Either ... ]然后我可以提取的值,那么我的錯誤是什么?
uj5u.com熱心網友回復:
一般而言,Monad 不作曲。我想你被這個咬了。
一般來說,如果m和n是單子,我們不能寫m (n a)一個混合了m和特征的單子動作n。一般來說,它甚至可能不是一個單子。
在您的情況下,您正在使用State A (IO B)希望能夠訪問 type 的狀態A并且仍然可以訪問的東西IO,但事實并非如此。
事實上,根據定義,我們有(最多一些包裝器):
State a b = a -> (a,b)
| | |-- result
| |-- new state
|-- old state
在你的情況下State A (IO B)它變成
State A (IO B) = A -> (A, IO B)
| | |-- result
| |-- new state
|-- old state
在這里,我們可以看到,新的狀態必須在完全不做 IO 的情況下生成!這是一個強制新狀態和 IO 效果完全分離的計算。它實際上表現得好像我們有兩個獨立的函式
A -> A -- state update, no IO here
A -> IO B -- IO depending on the old state
很可能,這不是你真正想要的。那可能是這樣的
A -> IO (A, B)
允許在 IO 完成后生成新狀態。
要獲得該型別,您不能嵌套 monad,但您需要一個monad 轉換器,例如StateT:
StateT A IO B = A -> IO (A, B)
這是一個單子。您可能需要使用lift或liftIO將 IO 操作轉換為這種型別,但它的行為應該符合需要。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/488476.html
上一篇:在Haskell中顯示結果
