Haskell:类似于“do”符号的应用程序`$`运算符?

时间:2013-12-15 23:17:57

标签: haskell

我正在为idleCallback提供一个函数。这种符号有效:

idleCallback $= Just (do
    modifyIORef world play
    postRedisplay Nothing)

为什么这个(看似相似的)符号不起作用?

idleCallback $= Just $ do
    modifyIORef world play
    postRedisplay Nothing

为了保存你的胡扯,类型是:

($=) :: HasSetter s => s a -> a -> IO ()
type IdleCallback = IO ()
data SettableStateVar a 
idleCallback :: SettableStateVar (Maybe IdleCallback)
postRedisplay :: Maybe Window -> IO ()
modifyIORef :: IORef a -> (a -> a) -> IO ()

GHC说:

Couldn't match expected type `Maybe IdleCallback'
            with actual type `a0 -> Maybe a0'
In the second argument of `($=)', namely `Just'
In the expression: idleCallback $= Just
In a stmt of a 'do' block:
  idleCallback $= Just
  $ do { modifyIORef world play;
         postRedisplay Nothing }

这可以在不将do块包裹在括号中的情况下编写吗?

3 个答案:

答案 0 :(得分:9)

这是一个优先级错误....($=)绑定比($)更紧密。您可以在错误消息中看到:

Couldn't match expected type `Maybe IdleCallback'
        with actual type `a0 -> Maybe a0'
In the second argument of `($=)', namely `Just'

它认为($=)的第二个参数只是Just(它是一个引用函数的有效Haskell类型)。如果你将括号括在整个Just,包括do - 块,它应该可以工作。

答案 1 :(得分:2)

$不是保存括号的特殊符号,它是一个普通函数(带有中缀运算符)。因此,不能意味着“将所有内容括在右边”。它实际意味着“将我的第一个论点应用于我的第二个论点”。 因此,它通常用于保存括号,但不是在这里因为:

idleCallback $= Just $ do
    modifyIORef world play
    postRedisplay Nothing

将使用不$和更多括号重写:

(idleCallback $= Just) (do
    modifyIORef world play
    postRedisplay Nothing)

而不是你想要的。原因是,一旦您将运算符优先级/关联性表示法转换为显式格式,它实际上就会被解析为:

($) (idleCallback $= Just) (do
    modifyIORef world play
    postRedisplay Nothing)

核心问题是$=也不是特殊语法,而是普通函数(使用中缀表示法),而$的优先级低于$=;设计$具有极低的优先级;这使得它通常适用于正常代码中的括号减少,但是当你将包含$的正确表达式作为更大表达式的一部分移动到新上下文中时,通常会导致失败的原因。

进行这种转换的唯一完全通用的方法(当你推断Just $ do ...有效时,你正在隐式执行这一操作,因此idleCallback $= Just $ do ...也应该有效)是将完整表达式包装在在将它插入外部表达式之前将括号括起来(这将产生idleCallback $= (Just $ do ...),这确实可以正常工作)。实际上,所有表达式都是如此,特别是涉及运算符时。例如您会自动知道仅因为x + y有效并不意味着当您将其放入z * ___中的空白时它仍然可以按照您想要的方式工作;你必须盲目地括起来获得z * (x + y),或者根据具体情况推断具体代码,看你是否需要括号(或其他转换)。

答案 2 :(得分:1)

你的里程可能会有所不同,但是有一件事应该有效,除非$=具有最高优先级

idleCallback $= Just `id` do
...

这是有效的,因为id作为运算符没有定义优先级,因此默认情况下获得最高值。