我正在尝试为这种类型实现Applicative实例:
newtype State s a = State {runState :: s -> (a, s)}
我对(< *>)函数有一些不同的想法。 我想到的一种实现它的方法是
(<*>) :: State s (a -> b) -> State s a -> State s b
State f <*> State s = State $ do
(fa, fs) <- f
let (sa, ss) = s fs
return (fa sa, ss)
或者
(<*>) :: State s (a -> b) -> State s a -> State s b
State f <*> State s = State $ do
(sa, ss) <- s
let (fa, fs) = f ss
return (fa sa, fs)
哪一个(或者甚至是其中任何一个)是正确的,为什么?
它们既可以检查也可以仅通过“状态”转换顺序来区分。我找不到任何理由更喜欢一个而不是另一个......
答案 0 :(得分:5)
首先,我建议不要使用(monadic!)do
语法来定义这样的应用实例,因为它相当模糊了正在发生的事情。这是您仅使用标准函数语法的定义:
State f <*> State s = State $ \q
-> let (fa, fs) = f q
(sa, ss) = s fs
in (fa sa, ss)
和
State f <*> State s = State $ \q
-> let (fa, fs) = f ss
(sa, ss) = s q
in (fa sa, fs)
这也更清楚地表明在一个应用实例中没有任何内在的评估顺序(与monad实例不同)。
答案 1 :(得分:4)
两者都是合理的。请注意,您可以从另一个获得其中任何一个:
x <*2> y = flip ($) <$> y <*1> x
然而,这是一个图书馆惯例,&#34;效果&#34;从左到右进行。因此,第一个版本看起来更熟悉。
答案 2 :(得分:4)
我认为两者都是正确的,因为就我所见,它们并没有违反任何适用法律。但是,第一个是实际使用的。我认为这是因为惯例:预期<*>
左手参数的效果首先应用于其右手参数之前。与IO
比较,例如,
(,) <$> readLn <*> getLine :: IO (Int, String)
首先提示输入Int,然后读取一个String。让State以类似的方式表现很好。
答案 3 :(得分:0)
它取决于 Monad bind >>=
as this answer 的状态流,并且在评论中也指出
适用交换法
<块引用>如果 f 也是一个 Monad,它应该满足
pure = return
(<*>) = ap
意思是如果 MonadState 状态从左到右流动,那么在应用中也应该如此,反之亦然。
但这并不意味着 applicative 应该取决于 monad 实现或 do-notation 正如@leftaroundabout 所说的那样不正确