Haskell: Something like the application `$` operator for "do" notation?

I'm supplying a function to idleCallback This notation works:

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

Why doesn't this (seemingly similar) notation work?

idleCallback $= Just $ do
    modifyIORef world play
    postRedisplay Nothing

To save your hoogling, the types are:

($=) :: 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 says:

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 }

Can this be written without wrapping the do block in parenthesis?

Answers


It is a precedence error.... ($=) is binding more tightly than ($). You can see this in the error message:

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

It thinks that the second argument of ($=) is simply Just (which is a valid Haskell type referencing a function). If you put parenthesis around the whole Just, including the do-block, it should work.


$ isn't special notation for saving parentheses, it's an ordinary function (with an infix operator). As such, it can't mean "wrap parentheses around everything to the right". What it actually means is "apply my first argument to my second argument". As such, it's often useful for saving parentheses, but not here because this:

idleCallback $= Just $ do
    modifyIORef world play
    postRedisplay Nothing

Would be rewritten using no $ and more parentheses as this:

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

Rather than what you wanted. The reason is that it's in fact parsed as this, once you've transformed the operator precedence/associativity notation to explicit form:

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

The core problem is that $= also isn't special syntax, but an ordinary function (with infix notation), and $ simply has lower precedence than $=; $ by design has extremely low precedence; this is what makes it often work for parentheses-reduction in normal code, but also often what makes it fail when you move a correct expression involving $ into a new context as part of a larger expression.

The only fully general way to make such a transformation (which is what you're doing implicitly when you reason that Just $ do ... works, so idleCallback $= Just $ do ... should also work) is to wrap the full expression in parentheses before you insert it into the outer expression (which would yield idleCallback $= (Just $ do ...), which indeed works fine). This is actually the case for all expressions, particularly when operators are involved. e.g. You would know automatically that just because x + y works doesn't mean it will still work the way you want when you drop it into the blank in z * ___ ; you have to either blindly parenthesise to get z * (x + y), or else reason about the specific code on a case by case basis to see whether and where you need parentheses (or other transformations).


Your mileage may vary as to whether it is better, but one thing that should work unless $= has the very highest precedence is

idleCallback $= Just `id` do
...

This works because id as operator has no precedence defined, so gets the highest by default.


Need Your Help

Load Fusion Table query value into JS variable?

javascript google-fusion-tables

I'm attempting to place, what will eventually be a dynamically sized, partial opacity filled circle over the basic default google maps red location marker dot. Initially all locations in the table ...

jQuery plugin Tablesorter sorts dates incorrectly

javascript jquery html jquery-plugins tablesorter

I'm trying to use the jQuery plugin Tablesorter to sort date fields. Now the strange thing is that the sorting seems to partially work, it does rearrange the order the results are displayed, which ...