首页 > 解决方案 > RebindableSyntax 不能按预期工作

问题描述

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

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

整个代码:

{-# 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)

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

解决方案

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

我将(>>)函数更改infixr为参数的顺序。下面的代码:

working :: Aggregator Integer
working = 1 >> 2 >> 3 >> mempty 

notWorking :: Aggregator Integer
notWorking = do -- Now works
    1
    2
    3
    mempty

infixr 0 >>
(>>) :: Integer -> Aggregator Integer -> Aggregator Integer
(>>) a = (<>) (pack a)

标签: haskell

解决方案


您需要像这样定义运算符1

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

然后你可以这样做:

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

原因是 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

对此进行脱糖:

action1 >>= (\r1 -> action2 r1 >>= (\r2 -> f r2))

从根本上说,do 块中的后续操作需要“嵌套在”绑定早期操作结果的 lambdas 中,以便这些结果在范围内。这就是导致do块中行的右关联性质的原因。

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


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


推荐阅读