RebindableSyntax 无法按预期工作

t4c*_*cer 8 haskell

当我定义>>函数(带RebindableSyntax扩展名)并直接使用它(mempty >> 1 >> 2 >> 3)时,一切都按预期工作,但是当我使用 do 块时,出现以下错误:

Couldn't match expected type ‘Integer’
            with actual type ‘Aggregator Integer’
In a stmt of a 'do' block: 1
In the expression:
  do mempty
     1
     2
     3
In an equation for ‘notWorking’:
    notWorking
      = do mempty
           1
           2
           3
Run Code Online (Sandbox Code Playgroud)

全码:

Couldn't match expected type ‘Integer’
            with actual type ‘Aggregator Integer’
In a stmt of a 'do' block: 1
In the expression:
  do mempty
     1
     2
     3
In an equation for ‘notWorking’:
    notWorking
      = do mempty
           1
           2
           3
Run Code Online (Sandbox Code Playgroud)

据我了解do将添加>>在每条新行上,所以它应该可以工作。
我在哪里做错了?

解决方案

Haskell 对 do 符号进行脱糖的方式是右结合的

我改变了(>>)函数infixr和参数的顺序。代码如下:

{-# LANGUAGE  RebindableSyntax #-}

module RebindableSyntaxStuff where

import Prelude hiding ((>>), (>>=), return)

newtype Aggregator a = Aggregator [a]
    deriving(Show)

instance Semigroup (Aggregator a) where
    (Aggregator xs) <> (Aggregator ys) = Aggregator (xs++ys)

instance Monoid (Aggregator a) where
    mempty = Aggregator []

pack :: a -> Aggregator a
pack x = Aggregator [x]

working :: Aggregator Integer
working = mempty >> 1 >> 2 >> 3 --Returns Aggregator [1,2,3]

notWorking :: Aggregator Integer
notWorking = do
    mempty
    1
    2
    3

(>>) :: Aggregator Integer -> Integer -> Aggregator Integer
(>>) a b = (<>) a (pack b)

Run Code Online (Sandbox Code Playgroud)

Ben*_*Ben 5

您需要像这样定义您的运营商1

(>>) :: Integer -> Aggregator Integer -> Aggregator Integer
(>>) a b = (<>) (pack a) b
Run Code Online (Sandbox Code Playgroud)

然后你可以这样做:

finallyWorks :: Aggregator Integer
finallyWorks = do
    1
    2
    3
    mempty
Run Code Online (Sandbox Code Playgroud)

原因是 Haskell 的do语法被定义为>>以右关联方式使用运算符。do { mempty; 1; 2; 3; }最终被读作mempty >> (1 >> (2 >> 3))>>操作本身是左结合的,所以你的手册例如mempty >> 1 >> 2 >> 3读作((mempty >> 1) >> 2) >> 3; 不是一回事。

我相信这是因为do绑定变量的黑人脱糖规则(使用>>=运算符而不是>>必须与右侧相关联。这个:

do r1 <- action1
   r2 <- action2 r1
   f r2
Run Code Online (Sandbox Code Playgroud)

对此进行脱糖:

action1 >>= (\r1 -> action2 r1 >>= (\r2 -> f r2))
Run Code Online (Sandbox Code Playgroud)

从根本上说,do 块中的后面动作需要“嵌套在”绑定前面动作结果的 lambda 表达式中,以便这些结果在范围内。这就是导致do块中行的右关联性质的原因。

实际的>>操作符 fromMonad原则上是关联的,因此与a >> (b >> c)具有相同的最终结果(a >> b) >> c。因此,do像您这样不绑定任何变量的块理论上可以脱糖到>>. 但由于>>是关联的,没有理由do与绑定变量的方式类似的方式对没有变量的行进行脱糖。


1a >> (Aggregator b) = Aggregator (a : b)。看起来它甚至应该是(>>) = coerce (:),但是如果没有在 上的类型注释,它就不起作用:,这使它看起来不再那么漂亮了。