让我从我想解决的任务开始,可能是我走错了路。我将Snap框架用于玩具项目,主要是它在Snap
monad下的功能。我需要在它上面添加我的状态。我使用monad变压器:
type SnapApp a = StateT AppState Snap a
这在模块中定义,比如Base
。由于我需要在其他模块中使用它,我必须将其导出:
module Base
( ..
, SnapApp
) where
这很好,但我希望该模块不会导出SnapApp
是状态monad,因为我有一些复杂的处理来为状态设置一些属性。例如,会话。我必须在文件被更改时写入文件,因此仅get
而不是put
修改会话是错误的,应该调用特殊函数。所以,我使用newtype
而不是数据导出construstor隐藏它:
newtype SnapApp a = SnapApp (StateT AppState Snap a)
我创建了我的类的实例,其中包含用于修改会话等的函数。但问题出现了:我丢失了Monad
类的实例和其他新SnapApp
的实例。我坚持实施>>=
:
instance Monad SnapApp where
return = SnapApp . return
mx >>= fm = -- HOW?
谢谢!
答案 0 :(得分:16)
让类型指导你。 你需要
(>>=) :: SnapApp a -> (a -> SnapApp b) -> SnapApp b
你有
(>>=) :: StateT AppState Snap a -> (a -> StateT AppState Snap b) -> StateT AppState Snap b
您需要转换:
SnapApp a
至StateT AppState Snap a
a -> SnapApp b
至a -> StateT AppState Snap b
StateT AppState Snap b
至SnapApp b
1)使用模式匹配;定义:
fromSnapApp (SnapApp x) = x
2)撰写函数a -> SnapApp b
和SnapApp b -> StateT AppState b
3)使用SnapApp
最终结果:
x >>= f = SnapApp (fromSnapApp x >>= (fromSnapApp . f))
或:
SnapApp x >>= f = SnapApp (x >>= (fromSnapApp . f))
你不必写这个;如果您启用GeneralizedNewtypeDeriving
扩展程序,GHC可以派生实例:
newtype SnapApp a = SnapApp (StateT AppState Snap a) deriving (Monad)
答案 1 :(得分:3)
这种状态管理正是我们设计的snaplet为您处理的。我们的Handler monad实际上只是一个围绕StateT s Snap
的新类型包装器,为方便起见,内置了一些额外的东西。我们公开了一个MonadState实例,这是你试图避免的,但你可以通过将状态类型封装在模块中而不是为它导出任何访问器来处理这个问题。您只能导出执行所有复杂属性处理的所需Handler函数。