数据流状态顺序的应用实例

时间:2017-07-17 21:46:53

标签: haskell state applicative

我正在尝试为这种类型实现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)

哪一个(或者甚至是其中任何一个)是正确的,为什么?

它们既可以检查也可以仅通过“状态”转换顺序来区分。我找不到任何理由更喜欢一个而不是另一个......

4 个答案:

答案 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 所说的那样不正确