[英]Haskell Monad Transformer Stack and Type Signatures
我正在嘗試創建一堆monad變換器,並且無法為我的函數獲取正確的類型簽名。 (我對Haskell還很新)
該堆棧結合了多個StateT變換器,因為我有多個狀態需要跟蹤(其中兩個可能是tupled,但我會在一秒內完成)和一個WriterT用於記錄。
這是我到目前為止所擁有的:
module Pass1 where
import Control.Monad.Identity
import Control.Monad.State
import Control.Monad.Writer
import Data.Maybe
import qualified Data.Map as Map
import Types
data Msg = Error String
| Warning String
type Pass1 a = WriterT [Msg] (StateT Int (StateT [Line] (StateT [Address] Identity))) a
runPass1 addrs instrs msgs = runIdentity (runStateT (runStateT (runStateT (runWriterT msgs) 1) instrs) addrs)
--popLine :: (MonadState s m) => m (Maybe s)
--popLine :: (Monad m) => StateT [Line] m (Maybe Line)
popLine :: (MonadState s m) => m (Maybe Line)
popLine = do
ls <- get
case ls of
x:xs -> do
put xs
return $ Just x
[] -> return Nothing
incLineNum :: (Num s, MonadState s m) => m ()
incLineNum = do
ln <- get
put $ ln + 1
curLineNum :: (MonadState s m) => m s
curLineNum = do
ln <- get
return ln
evalr = do l <- popLine
--incLineNum
return l
我希望popLine
弄亂[Line]
狀態和xLineNum
函數來影響Int
狀態。 evalr
是將傳遞給runPass1
的計算。
每當我加載代碼時,我都會遇到錯誤,這些錯誤通常有以下幾種:
Pass1.hs:23:14:
No instance for (MonadState [t] m)
arising from a use of `get' at Pass1.hs:23:14-16
Possible fix: add an instance declaration for (MonadState [t] m)
In a stmt of a 'do' expression: ls <- get
In the expression:
do ls <- get
case ls of {
x : xs -> do ...
[] -> return Nothing }
In the definition of `popLine':
popLine = do ls <- get
case ls of {
x : xs -> ...
[] -> return Nothing }
Pass1.hs:22:0:
Couldn't match expected type `s' against inferred type `[Line]'
`s' is a rigid type variable bound by
the type signature for `popLine' at Pass1.hs:21:23
When using functional dependencies to combine
MonadState [Line] m,
arising from a use of `get' at Pass1.hs:23:14-16
MonadState s m,
arising from the type signature for `popLine'
at Pass1.hs:(22,0)-(28,31)
When generalising the type(s) for `popLine'
Pass1.hs:23:14:
Could not deduce (MonadState [Line] m)
from the context (MonadState s m)
arising from a use of `get' at Pass1.hs:23:14-16
Possible fix:
add (MonadState [Line] m) to the context of
the type signature for `popLine'
or add an instance declaration for (MonadState [Line] m)
In a stmt of a 'do' expression: ls <- get
In the expression:
do ls <- get
case ls of {
x : xs -> do ...
[] -> return Nothing }
In the definition of `popLine':
popLine = do ls <- get
case ls of {
x : xs -> ...
[] -> return Nothing }
沒有任何簽名似乎是正確的,但popLine是第一個函數,因此它是唯一一個立即導致錯誤的函數。
我嘗試在類型簽名中添加它建議的內容(例如: popLine :: (MonadState [Line] m) => ...
但是它的錯誤如下:
Pass1.hs:21:0:
Non type-variable argument in the constraint: MonadState [Line] m
(Use -XFlexibleContexts to permit this)
In the type signature for `popLine':
popLine :: (MonadState [Line] m) => m (Maybe Line)
每當我嘗試做一些不是類型變量的事情時,我似乎總是得到這個消息。 它似乎喜歡(MonadState sm)
ok和其他的錯誤,但是當我用[a]
而不是s
嘗試它時,它的錯誤類似於上面的錯誤。 (最初[Line]和Int在一個狀態中被組合在一起,但我得到了這個錯誤,所以我想我會嘗試將它們置於不同的狀態)。
GHC 6.10.4,Kubuntu
所以,任何人都可以告訴我發生了什么並給出解釋/給我正確的類型簽名,或者有人知道這個東西的一個很好的參考(到目前為止唯一幫助的是“Monad變形金剛一步一步” ,但只使用一個輔助狀態函數和一個StateT)?
提前謝謝了。
編輯
這是包含JFT和Edward的建議的編譯代碼:
{-# LANGUAGE GeneralizedNewtypeDeriving #-} -- needed for: deriving (Functor,Monad)
{-# LANGUAGE MultiParamTypeClasses #-} -- needed for: MonadState instance
{-# LANGUAGE FlexibleContexts #-} -- needed for: (MonadState PassState m) => ...
module Pass1 where
import Control.Monad.State
import Control.Monad.Writer
import Data.Maybe
import Types
type Lines = [Line]
type Addresses = [Address]
type LineNum = Int
type Messages = [Msg]
data Msg = Error String
| Warning String
data PassState = PassState { passLineNum :: LineNum
, passLines :: Lines
, passAddresses :: Addresses
}
newtype Pass1 a = Pass1 { unPass1 :: WriterT Messages (State PassState) a
}
deriving (Functor,Monad)
instance MonadState PassState Pass1 where
get = Pass1 . lift $ get
put s = Pass1 . lift $ put s
runPass1 :: PassState -> Pass1 a -> ((a, Messages), PassState)
runPass1 state = flip runState state .
runWriterT .
unPass1
curLineNum :: (MonadState PassState m) => m LineNum
curLineNum = do
state <- get
return $ passLineNum state
nextLine :: (MonadState PassState m) => m (Maybe Line)
nextLine = do
state <- get
let c = passLineNum state
let l = passLines state
case l of
x:xs -> do
put state { passLines = xs, passLineNum = (c+1) }
return $ Just x
_ -> return Nothing
evalr :: Pass1 (Maybe Line,LineNum)
evalr = do
l <- nextLine
c <- curLineNum
--tell $ Warning "hello"
return (l,c)
我將incLineNum
和popLine
合並到nextLine
我仍然需要讓Writer monad部分工作,但我想我知道從哪里開始。 多謝你們。
您的代碼段存在許多問題。 我修復了你的片段,添加了關於什么被破壞的解釋,並在你關心的時候添加了一些風格建議。
module Pass1_JFT where
import Control.Monad.Identity
import Control.Monad.State
import Control.Monad.Writer
import Data.Maybe
import qualified Data.Map as Map
{ - 用簡單的定義替換導入類型 - }
--import Types
type Line = String
type Address = String
type LineNumber = Int
{ - 不是你問題的一部分,而是我的2美分......如果你不使用類型別名,你想要改變你的狀態的集合,你必須在你使用它的地方進行搜索。 相反,如果需要,只需更改這些定義 - }
type Lines = [Line]
type Addresses = [Address]
type Messages = [Msg]
data Msg = Error String
| Warning String
{ - StateT Int中的Int是什么? 命名更容易閱讀,推理和改變。 聲明性FTW讓我們改用LineNumber - }
--type Pass1 a = WriterT [Msg] (StateT Int (StateT [Line] (StateT [Address] Identity))) a
{ - 讓我們使用“真實”類型,以便可以派生實例。 由於Pass1不是monad傳輸,即未定義為Pass1 ma,因此使用StateT作為最深的StateT即StateT [Address] Identity,所以讓我們只使用一個State [Address] - }
newtype Pass1 a = Pass1 {
unPass1 :: WriterT Messages (StateT LineNumber (StateT Lines (State Addresses))) a
}
deriving (Functor,Monad)
--runIdentity (runStateT (runStateT (runStateT (runWriterT msgs) 1) instrs) addrs)
{ - 讓我們從原始聲明中的最外層(聲明中最左邊)到最里面剝離該堆棧。 請注意,runWriterT不采用起始狀態... runStateT(和runState)的第一個參數不是初始狀態,而是monad ...所以讓我們翻轉! - }
runPass1' :: Addresses -> Lines -> Messages -> Pass1 a -> ((((a, Messages), LineNumber), Lines), Addresses)
runPass1' addrs instrs msgs = flip runState addrs .
flip runStateT instrs .
flip runStateT 1 .
runWriterT . -- then get process the WriterT (the second outermost)
unPass1 -- let's peel the outside Pass1
{ - 現在最后一個函數沒有做你想要的,因為你想提供一個初始日志來附加到WriterT。 既然它是monad變換器,我們會在這里做一些技巧 - }
-- I keep the runStateT convention for the order of the arguments: Monad then state
runWriterT' :: (Monad m,Monoid w) => WriterT w m a -> w -> m (a,w)
runWriterT' writer log = do
(result,log') <- runWriterT writer
-- let's use the monoid generic append in case you change container...
return (result,log `mappend` log')
runPass1 :: Addresses -> Lines -> Messages -> Pass1 a -> ((((a, Messages), LineNumber), Lines), Addresses)
runPass1 addrs instrs msgs = flip runState addrs .
flip runStateT instrs .
flip runStateT 1 .
flip runWriterT' msgs . -- then get process the WriterT (the second outermost)
unPass1 -- let's peel the outside Pass1
{ - 您打算直接從Pass1堆棧調用popLine嗎? 如果是這樣你需要“教導”Pass1成為“MonadState Lines”這樣做讓我們派生出Pass1(這就是為什么我們用newtype聲明它!) - }
instance MonadState Lines Pass1 where
-- we need to dig inside the stack and "lift" the proper get
get = Pass1 . lift . lift $ get
put s = Pass1 . lift . lift $ put s
{ - 最好保持通用,但我們現在可以寫:popLine :: Pass1(可能是Line) - }
popLine :: (MonadState Lines m) => m (Maybe Line)
popLine = do
ls <- get
case ls of
x:xs -> do
put xs
return $ Just x
[] -> return Nothing
{ - 好了我現在得到了Int => LineNumber ....我們可以制作Pass1和MonadState LineNumber的實例,但LineNumber不應該被搞亂,所以我直接編碼incLine並且如果需要的話將提供一個MonadReader實例用於咨詢
check ":t incLineNum and :t curLineNum"
- }
incLineNum = Pass1 . lift $ modify (+1)
curLineNum = Pass1 $ lift get
evalr = do l <- popLine
incLineNum
return l
在那里,它是一個冗長的回應,但你看到monad和monad堆棧起初很有挑戰性。 我修改了代碼,但我鼓勵你玩和檢查各種功能的類型,以了解發生了什么,並與原始進行比較。 Haskell的類型推斷意味着通常類型注釋是多余的(除非消除歧義)。 一般來說,我們賦予函數的類型不那么通用,因此最好不要鍵入注釋。 雖然類型注釋絕對是一個很好的調試技術;)
干杯
關於Monad Transformer的PS Real World Haskell章節很棒: http : //book.realworldhaskell.org/read/monad-transformers.html
一般來說,你會發現使用一個具有更大復合結構的StateT可以更清晰地顯示所需的所有狀態位。 一個很好的理由是,當你提出一個狀態你忘記了你總是可以通過一個字段來增加結構,並且你可以使用記錄糖來寫出單個字段更新或轉向像fclabels或data-accessor這樣的東西用於操縱狀態的包。
data PassState = PassState { passLine :: Int, passLines :: [Line] }
popLine :: MonadState PassState m => m (Maybe Line).
popLine = do
state <- get
case passLines state of
x:xs -> do
put state { passLines = xs }
return (Just x)
_ -> return Nothing
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.