Haskellのモナド変圧器スタックおよびタイプの署名
-
21-09-2019 - |
質問
私はモナド変圧器のスタックを作成しようとしていますとのトラブル私の機能のための正しい型シグネチャを取得したのです。
(私は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 s m)
のOKとエラーのように見えますが、私の代わりに、上記と同様の[a]
それエラーのs
でそれをしようとします。 (最初は[ライン]とINTが単一の状態でtupledたが、私は、私は別々の状態でそれらを配置しようと思ったので、私はこのエラーを得ていた)。
GHC 6.10.4、Kubuntuの
だから、缶誰もがこれまでに役立っている唯一のものは、「モナド変圧器のステップだった(何が起こっている私に言うと説明を与える/私の右の型シグネチャを示し、またはこのようなもので良い参考の誰のノウハウを行いますステップによって」、しかし、それはただ)1つのAUX状態関数と1 StateTを使用しています!
事前に感謝します。
編集
ここでJFTのとエドワードの提案を取り入れコンパイルするコードがあります:
{-# 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
に I組み合わせるpopLine
とnextLine
私はまだ仕事にライターモナド部分を取得する必要がありますが、私はどこここから行くする知っていると思います。おかげで、みんなます。
解決
あなたのコードスニペットを持つ多くの問題がありました。私はあなたが気にしている場合破壊され、いくつかのスタイルのアドバイスを追加したものととして説明を追加し、あなたのスニペットを固定します。
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セントの一部ではありません... あなたがいない場合、あなたの状態のためのコレクションを変更したいことを言います あなたはそれを使用しeverwhereハントする必要がありますタイプの別名を使用しています。だけではなく、 必要に応じて、これらの定義を変更します - }
type Lines = [Line]
type Addresses = [Address]
type Messages = [Msg]
data Msg = Error String
| Warning String
{ - StateTのIntのそれのIntとは何ですか?約理由を読みやすいという名前を付けます そして、変更します。 FTWの代わりに行番号を使ってみましょう、宣言 - }
--type Pass1 a = WriterT [Msg] (StateT Int (StateT [Line] (StateT [Address] Identity))) a
{ - インスタンスが派生することができますので、のは、「本当の」タイプを使用してみましょう。 パス1、すなわちパス1 M A、として定義されていないモナド転送ないので ない点が最深StateTすなわちStateT [住所] IDのStateTを使用して そうちょうど国家[アドレス]を使用してみましょう - }
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)
{ - 最も外側からのスタックが(宣言でlefmost)ことレッツ・皮 最も内側までは、元の宣言でのアイデンティティでした。 runWriterTが開始状態になりませんので注意してください... runStateT(およびrunState)の最初のパラメータは、初期状態ではありません しかし、モナドは...そうのフリップをしましょう! - }
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とに追加する最初のログ。 それはモナド変換子であるので、ここではいくつかのトリックを行うだろう - }
-- 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
{ - あなたはパス1スタックから直接popLineを呼び出すするつもりですか? あなたは「MonadState行」と「ティーチ」パス1に必要そうならば それでは、パス1派生させないために(私たちはの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 ::パス1(たぶんライン) - }
popLine :: (MonadState Lines m) => m (Maybe Line)
popLine = do
ls <- get
case ls of
x:xs -> do
put xs
return $ Just x
[] -> return Nothing
{ - [OK]を、今私はのInt =>行番号を取得します.... 私たちは、パス1とMonadState LINENUMBERが、行番号のインスタンスを作ることができます 混乱してはならないので、代わりに私が直接傾斜をコーディングしたいです 必要な場合やコンサルテーションのためMonadReaderインスタンスを提供することになる。
check ":t incLineNum and :t curLineNum"
- }
incLineNum = Pass1 . lift $ modify (+1)
curLineNum = Pass1 $ lift get
evalr = do l <- popLine
incLineNum
return l
それはあなたが最初に挑戦しています見るように長いったらしい応答が、モナドとモナドスタックがあります。私は、コードを固定ではなく、私はプレーして上で起こっているとあなたの元と比較するかを理解するために、さまざまな機能の種類を検査することをお勧めします。 (曖昧さを取り除くためにしない限り)通常の注釈を入力しHaskellの型推論手段は不要です。一般的に、我々は関数に与えると思い型は、型注釈に良いとは限りませんので、推論されたもの以下総称です。型注釈決定的にかかわらず、技術をデバッグ良いです;)
乾杯
P.S。モナド変圧器の実世界Haskellの章では優れています: http://book.realworldhaskell.org/read/monad-transformers.htmlする
他のヒント
一般的に、あなたはコードがあなたが必要とする状態のビットのすべてのためのより大きな複合構造で1 StateTを使用してより明確に巻き取ることがわかります。一つの良い理由は、あなたはあなたが常に一つのフィールドで構造を成長することができ、あなたは、単一のフィールドの更新を書き出すために、レコードの砂糖を使用するか、fclabelsまたはデータ・アクセサのようなものに変えることができます忘れてしまった状態の作品を思い付くとき状態を操作するためのパッケージます。
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