Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell——使用StateT monad转换器链接两个状态_Haskell_Monad Transformers_State Monad - Fatal编程技术网

Haskell——使用StateT monad转换器链接两个状态

Haskell——使用StateT monad转换器链接两个状态,haskell,monad-transformers,state-monad,Haskell,Monad Transformers,State Monad,我在一个Haskell应用程序中要跟踪两个或多个独立状态 我使用 type MonadTuple m = MonadState (Int, Int) m type MonadBool m = MonadState Bool m monad转换器堆栈声明为 type Stack = StateT (Int, Int) (StateT Bool IO) () 我打算这样使用堆栈 ret :: Stack ret = apply apply :: (MonadTuple m, MonadBool

我在一个Haskell应用程序中要跟踪两个或多个独立状态

我使用

type MonadTuple m = MonadState (Int, Int) m
type MonadBool m = MonadState Bool m
monad转换器堆栈声明为

type Stack = StateT (Int, Int) (StateT Bool IO) ()
我打算这样使用堆栈

ret :: Stack
ret = apply

apply :: (MonadTuple m, MonadBool m) => m ()
apply = undefined
编译器不满意,因为在尝试检查
堆栈是否符合
MonadBool
时,它无法将
Bool
(Int,Int)
匹配

我知道报告中给出的解决方案。除了箭头带镜头的全局状态之外,还有其他更简单的解决方案吗

附件: 完整的代码块是

{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}

import Control.Monad.State.Class
import Control.Monad.State.Lazy

type MonadTuple m = MonadState (Int, Int) m
type MonadBool m = MonadState Bool m

type Stack = StateT (Int, Int) (StateT Bool IO) ()

ret :: Stack
ret = apply

apply :: (MonadTuple m, MonadBool m) => m ()
apply = undefined

具有函数依赖项
m->s
,这意味着一个monad
m
最多必须有一个
MonadState s m
的实例。或者,更简单地说,对于两个不同的状态,同一个monad不能有两个
MonadState
实例,这正是您要做的。

有一个更简单的解决方案:

apply :: (MonadTuple (t m), MonadBool m, MonadTrans t) => t m ()
apply = undefined
您可以使用
get
put
apply
中触摸
(Int,Int)
状态,以及
lift get
lift。将
置于触摸
Bool
状态

但是,这要求
StateT(Int,Int)
是顶级转换器。如果低于顶部,则需要在您的类型中添加适当数量的附加变压器来对深度进行编码;e、 如果这是第三件事,那么你需要

apply :: (MonadTuple (t1 (t2 (t3 m))), MonadBool m, MonadTrans t1, MonadTrans t2, MonadTrans t3) => t1 (t2 (t3 m)) ()
apply = undefined
而且每次访问
Bool
状态都需要使用三个
lift
s,这很快就会变得笨拙,并真正失去mtl风格的类多态编程的魅力

一种常见的替代样式是公开一个涉及这两种状态但不是类多态的API。比如说,

type Stack = StateT (Int, Int) (StateT Bool IO)

getTuple :: Stack (Int, Int)
getTuple = get

getBool :: Stack Bool
getBool = lift get
(类似地,您将添加一个
putTuple
putBool

我猜想,在现代扩展的情况下,你也可以考虑引入你自己的类,它没有“代码> MunAdStAs/CODE”的基础知识;e、 g

class MonadState2 s m where
    get2 :: m s
    put2 :: s -> m ()
然后,您可以使用一个newtype来给出两个实例,通过类型来消除歧义:

newtype Stack a = Stack (StateT (Int, Int) (StateT Bool IO) a)
instance MonadState2 Bool Stack where
    get2 = Stack (lift get)
    put2 = Stack . lift . put

instance MonadState2 (Int, Int) Stack where
    get2 = Stack get
    put2 = Stack . put

如果类型推断没有足够的信息来选择要使用的实例,则调用者将编写例如
get2@Bool
get2@(Int,Int)
。但是我怀疑这会很快变老。

你为什么命名这个
堆栈
?我把它命名为
堆栈
,建议使用monad转换器
堆栈
。在这种情况下,它不应该有硬编码的
()
作为值类型。我同意。如果以多态方式使用转换器,则非硬编码将更加清晰。