2017-07-17 35 views
4

我想实现应用型实例这样的类型:应用型实例 - 数据的顺序流

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) 

哪一个(或做甚至它们中的任何)是正确的,为什么呢?

他们都只是通过“状态”转换顺序进行类型检查和区分。我无法找到任何理由相对于另一个...

+1

这个问题似乎是[nerd snipe](https://xkcd.com/356/)的成熟机会。这里有一个令人费解的(但是有效的)定义,对于一个类似于状态的monad的'>> ='你可以考虑:state f >> = g = state $ \ s - > let {(u,x) = ft; (t,y)= runState(g x)s}(u,y)'。观察'x','y'如何从上到下,但是''''''','''从底部到顶部。仍然让我的大脑受到伤害:) –

+1

如果你还没有看到它,[你可能会喜欢这个](https://lukepalmer.wordpress.com/2008/08/10/mindfuck-the-reverse-state-monad/) – luqui

+0

@BenjaminHodgson这是奇怪的美丽 – 4castle

回答

4

我相信两者都是正确的,因为他们没有违反任何适用法律,据我所知。但是,第一个是实际使用的内容。我认为这是因为惯例:预计<*>的左手论点的影响首先适用于它的右手论点之前。与IO比较,例如,在那里

(,) <$> readLn <*> getLine :: IO (Int, String) 

提示一个Int第一,然后读取的字符串。国家以类似的方式行事是很好的。

+3

我不认为这是完全正确的,假设一个'Monad'实例给出为'国家'。 ['应用法律](https://hackage.haskell.org/package/base-4.9.1.0/docs/Control-Applicative.html#t:Applicative)如果'f'也是'Monad' ,它应该满足'(<*>)= ap',假设通常的'Monad'实例,第一个定义会违反。 –

4

两者都是合理的。请注意,您可以从另外一个让其中一方:

x <*2> y = flip ($) <$> y <*1> x 

这是一个图书馆惯例,虽然,“效果”进行左到右。因此,第一个版本看起来比较熟悉。

4

首先,我建议不要使用(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) 

这也使得它更清晰,有没有真正在应用性实例中的任何内在的评估顺序(不像一个单子实例)。

+0

感谢您的提示!那么我会尽量不要过度使用这个符号。 – Ikciwor

+2

@Ikciwor一般使用这种符号没有任何错误。建议不要在这里使用*,因为它是一种向后依赖的方式:Monad是构建在Applicative之上的一个更强大的功能,所以使用monadic记法来定义'<*>'就像循环依赖。当然,在实践中,如果你知道两者都存在,你可以按任意顺序来定义它们,但是当你试图理解它们是如何工作的时候,最好从最小到最强大的顺序来构建它们。 – amalloy

+1

的确,这就是我的意思 - monad比应用程序严格得多。这在这里并不重要,因为'do'实际上使用了与你定义的monad完全不同的monad(函数/读取器monad),但是这又是一个monad实例,它只能提供很少的用明确的lambda表达式写得好或者更好,所以我确实避免了特定的monad。 – leftaroundabout