gpt4 book ai didi

Haskell Monad Transformer 堆栈和类型签名

转载 作者:行者123 更新时间:2023-12-03 21:02:06 24 4
gpt4 key购买 nike

我正在尝试创建一堆 monad 转换器,但无法为我的函数获取正确的类型签名。 (我对 Haskell 还是很陌生)

该堆栈结合了多个 StateT 转换器,因为我需要跟踪多个状态(其中两个可以被元组化,但我会在一秒钟内完成)和一个用于日志记录的 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)好的和其他的错误,但是当我用 [a] 尝试它时而不是 s它的错误与上述类似。 (最初 [Line] 和 Int 在一个状态下进行元组处理,但我收到了这个错误,所以我想我会尝试将它们置于不同的状态)。

GHC 6.10.4,库本图

那么,谁能告诉我发生了什么并给出解释/告诉我正确的类型签名,或者有没有人知道关于这些东西的一个很好的引用(到目前为止唯一有帮助的是“Monad Transformers Step by Step” ,但只使用一个辅助状态函数和一个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)

我合并了 incLineNumpopLine进入 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 m a,
没有必要将 StateT 用于最深的 StateT,即 StateT [Address] Identity
所以让我们只使用一个状态 [地址]
-}
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)更改器(mutator),我们将在这里做一些技巧
-}
-- 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 (Maybe 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 stack 一开始是具有挑战性的。我修复了代码,但我鼓励您玩并检查各种函数的类型,以了解正在发生的事情并与您的原始代码进行比较。 Haskell 的类型推断意味着通常类型注释是多余的(除非消除歧义)。一般来说,我们赋予函数的类型不像 infer 那样通用,所以最好不要键入注释。类型注释绝对是一种很好的调试技术;)

干杯

附言关于 Monad Transformer 的真实世界 Haskell 章节非常棒:
http://book.realworldhaskell.org/read/monad-transformers.html

关于Haskell Monad Transformer 堆栈和类型签名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2082927/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com