Haskell 涵盖升级数据类型的所有情况
因此,我最近想出了一个好主意,希望在严格和懒惰的Haskell 涵盖升级数据类型的所有情况,haskell,data-kinds,Haskell,Data Kinds,因此,我最近想出了一个好主意,希望在严格和懒惰的Statetransformer模块之间共享代码: {-# LANGUAGE FlexibleInstances, DataKinds, KindSignatures #-} module State where data Strictness = Strict | Lazy newtype State (t :: Strictness) s a = State (s -> (s, a)) returnState :: a -> S
State
transformer模块之间共享代码:
{-# LANGUAGE FlexibleInstances, DataKinds, KindSignatures #-}
module State where
data Strictness = Strict | Lazy
newtype State (t :: Strictness) s a = State (s -> (s, a))
returnState :: a -> State t s a
returnState x = State $ \s -> (s, x)
instance Monad (State Lazy s) where
return = returnState
State ma >>= amb = State $ \s -> case ma s of
~(s', x) -> runState (amb x) s'
instance Monad (State Strict s) where
return = returnState
State ma >>= amb = State $ \s -> case ma s of
(s', x) -> runState (amb x) s'
get :: State t s s
get = State $ \s -> (s, s)
put :: s -> State t s ()
put s = State $ \_ -> (s, ())
您可以看到,get
和put
在严格类型和惰性类型上都没有任何重复,没有类型类实例,没有任何内容。然而,尽管我介绍了两种可能的严格性
,但通常我没有状态ts a的Monad实例:
-- from http://blog.melding-monads.com/2009/12/30/fun-with-the-lazy-state-monad/
pro :: State t [Bool] ()
pro = do
pro
s <- get
put (True : s)
-- No instance for (Monad (State t [Bool])) arising from a do statement
然后我可以在Lazy
或Strict
处实例化t
,然后运行结果并得到我期望的结果。但我为什么要给出这样的背景呢?这是一个概念上的限制,还是一个实际的限制?有什么原因我不知道为什么Monad(State ts a)
实际上不成立,还是还没有办法说服GHC
(旁白:使用上下文Monad(状态ts)
不起作用:
无法推断do语句产生的(Monad(State t[Bool])
来自上下文(Monad(State ts))
这让我更加困惑。肯定前者可以从后者中推断出来吗?这是一个限制,但有一个很好的理由:如果它不这样工作,它的预期语义会是什么
runState :: State t s a -> s -> (s,a)
runState (State f) s = f s
example :: s -> a
example = snd $ runState ((State undefined) >> return 1) ()
嗯,可能是这样
example = snd $ runState ((State undefined) >>= \_ -> return 1) ()
= snd $ runState (State $ \s -> case undefined s of (s',_) -> (s',1)) ()
= snd $ case undefined () of (s',_) -> (s',1)
= snd $ case undefined of (s',_) -> (s',1)
= snd undefined
= undefined
example = snd $ runState ((State undefined) >>= \_ -> return 1) ()
= snd $ runState (State $ \s -> case undefined s of ~(s',_) -> (s',1)) ()
= snd $ case undefined () of ~(s',_) -> (s',1)
= snd $ (undefined,1)
= 1
也可能是
example = snd $ runState ((State undefined) >>= \_ -> return 1) ()
= snd $ runState (State $ \s -> case undefined s of (s',_) -> (s',1)) ()
= snd $ case undefined () of (s',_) -> (s',1)
= snd $ case undefined of (s',_) -> (s',1)
= snd undefined
= undefined
example = snd $ runState ((State undefined) >>= \_ -> return 1) ()
= snd $ runState (State $ \s -> case undefined s of ~(s',_) -> (s',1)) ()
= snd $ case undefined () of ~(s',_) -> (s',1)
= snd $ (undefined,1)
= 1
这些不一样。一种选择是定义一个额外类的函数,如
class IsStrictness t where
bindState :: State t s a -> (a -> State t s b) -> State t s b
然后定义
instance IsStrictness t => Monad (State t s) where
return = returnState
(>>=) = bindState
您可以使用单例,而不是将bindState
定义为isstrictity
的一部分
data SingStrictness (t :: Strictness) where
SingStrict :: SingStrictness Strict
SingLazy :: SingStrictness Lazy
class IsStrictness t where
singStrictness :: SingStrictness t
bindState :: IsStrictness t => State t s a -> (a -> State t s b) -> State t s b
bindState ma' amb' = go singStrictness ma' amb' where
go :: SingStrictness t -> State t s a -> (a -> State t s b) -> State t s b
go SingStrict ma amb = ...
go SingLazy ma amb = ...
使用GHC 7.6中的singleton基础设施,而不是自定义类和singleton类型,可以进一步增强这一功能。你最终会得到
instance SingI t => Monad (State t s)
这真的没那么可怕。习惯于约束集中有大量的信号。至少在一段时间内,这就是它的工作方式,这不是很难看吗
至于为什么状态t[Bool]
不能从状态ts
中推断:问题是状态ts
在您的顶层上下文中,这意味着s
在最外层被量化。您正在定义一个函数,该函数表示“对于任何t和s,Monad(状态TS)我将给您…”。但是,这并不是说“对于任何这样的单子(状态t[Bool]),我会给你……”。不幸的是,这些普遍量化的约束在Haskell中并不容易 这确实是数据种类的一个限制。我看到了一些相关的事情发生,GHC无法找出带有数据类型的GADT的模式是详尽的,并且它产生了不会进行类型检查的建议。我满足于示例此时的反应,带有一个模棱两可的类型错误——意识到不管模棱两可如何解决,出现了一个Monad
实例。它似乎是一个类型变量t::Strictness
可以保存值Strict
,Lazy
,或者两者都不能。不过,我明白你关于状态ts
的观点,谢谢。似乎应该有一种方式来表达普遍的约束。