问题 Haskell中State的Functor / Applicative实例


在阅读(并略读)Wadler关于monad的论文之后,我决定更仔细地研究这篇论文,为他描述的每个monad定义函子和应用实例。使用类型同义词

type M a = State -> (a, State)
type State = Int

Wadler用来定义状态monad,我有以下(使用相关的名称,所以我可以稍后使用newtype声明定义它们)。

fmap' :: (a -> b) -> M a -> M b
fmap' f m = \st -> let (a, s) = m st in (f a, s)

pure' :: a -> M a
pure' a = \st -> (a, st)

(<@>) :: M (a -> b) -> M a -> M b
sf <@> sv = \st -> let (f, st1) = sf st
                       (a, st2) = sv st1
                    in (f a, st2)

return' :: a -> M a
return' a = pure' a

bind :: M a -> (a -> M b) -> M b
m `bind` f = \st -> let (a, st1) = m st
                        (b, st2) = f a st1
                     in (b, st2)

当我在newtype声明中切换到使用类型构造函数时,例如,

newtype S a = S (State -> (a, State))

一切都崩溃了。例如,一切都只是略微修改

instance Functor S where
 fmap f (S m) = S (\st -> let (a, s) = m st in (f a, s)) 

instance Applicative S where
 pure a = S (\st -> (a, st))

但是,由于lambda表达式隐藏在该类型构造函数中,因此GHC中没有任何内容运行。现在我看到的唯一解决方案是定义一个函数:

isntThisAnnoying s (S m) = m s

为了将s绑定到'st'并实际返回一个值,例如,

fmap f m = S (\st -> let (a, s) = isntThisAnnoying st m in (f a, s))

还有另一种不使用这些辅助功能的方法吗?


3249
2017-08-20 18:25


起源



答案:


如果你看 这里,你会看到他们这样定义:

newtype State s a = State { runState :: (s -> (a,s)) }

以便给内在的lambda一个名字。


11
2017-08-20 18:45



这也意味着 runState = flip isntThisAnnoying。 - kennytm
好的 - 所以虽然仍然需要辅助功能,但我可以使用记录定义类型,免费获取该功能。那么你所说的是,没有办法避免使用像'runState'或'run'这样的函数。谢谢。 - danportin
如果你把它想象成一个函数会让你受伤,那就把它想象成一个struct field accessor。 :-) - Owen S.
哦,这不会伤害我。我只是想知道是否有更好的方法来定义它。正如你所指出的那样,'runState'非常好:)但是Haskell是我的第一语言,所以我宁愿将它看作是从s到(a,s)的函数,因为它就是它的本质。 - danportin


答案:


如果你看 这里,你会看到他们这样定义:

newtype State s a = State { runState :: (s -> (a,s)) }

以便给内在的lambda一个名字。


11
2017-08-20 18:45



这也意味着 runState = flip isntThisAnnoying。 - kennytm
好的 - 所以虽然仍然需要辅助功能,但我可以使用记录定义类型,免费获取该功能。那么你所说的是,没有办法避免使用像'runState'或'run'这样的函数。谢谢。 - danportin
如果你把它想象成一个函数会让你受伤,那就把它想象成一个struct field accessor。 :-) - Owen S.
哦,这不会伤害我。我只是想知道是否有更好的方法来定义它。正如你所指出的那样,'runState'非常好:)但是Haskell是我的第一语言,所以我宁愿将它看作是从s到(a,s)的函数,因为它就是它的本质。 - danportin


通常的方法是定义 newtype newtype S a = S {runState : State -> (a, State)}。而不是你的 isntThisAnnoying s (S m) 你可以写 runState t s 哪里 t 是相同的 S m
你必须使用 newtype 因为类型同义词不能是类型类实例。


4
2017-08-20 18:46