2

Suppose I have data types

data Price = Price Double
data Book = Book {title :: String, bookPrice :: Price}

with a function extracting the numerical price

priceAsDouble :: Price -> Double
priceAsDouble (Price doubleValue) = doubleValue

Now, I want to write an accumulator for a fold over a book list, such as

go :: Double -> Book -> Double
go acc book = acc + priceAsDouble (bookPrice book)

which is fine and compiles.

However, if I change the last line to

go acc book = acc + priceAsDouble $ bookPrice book

I get the following contradicting compiler error:

<interactive>:10:51:
Couldn't match expected type ‘Price -> Double’
            with actual type ‘Double’
The first argument of ($) takes one argument,
but its type ‘Double’ has none
In the expression: acc + priceAsDouble $ bookPrice book
In an equation for ‘go’:
    go acc book = acc + priceAsDouble $ bookPrice book

<interactive>:10:57:
Couldn't match expected type ‘Double’
            with actual type ‘Price -> Double’
Probable cause: ‘priceAsDouble’ is applied to too few arguments
In the second argument of ‘(+)’, namely ‘priceAsDouble’
In the expression: acc + priceAsDouble

Question: I thought that ($) is nothing else but syntactical sugar for parentheses (). Evidently, I was wrong. Where is the mistake in my thought?

madison54
  • 743
  • 2
  • 8
  • 19
  • 1
    `$` is the apply operator. It applies a function on the left hand side of `$` to the argument on the right hand side. Often this allows `$` to replace parentheses around the last argument. However if the function and argument don't match `$`'s type signature, you can't apply the arguments to the function, even if there were parentheses around argument. – recursion.ninja Sep 13 '15 at 14:34
  • **`($)` is a function!** – AJF Sep 13 '15 at 14:40

1 Answers1

6

$ is syntaxic sugar for parentheses, but it doesn't apply at the level you expect.

go acc book = acc + priceAsDouble $ bookPrice book

is actually interpreted as

go acc book = (acc + priceAsDouble) (bookPrice book)

But acc + priceAsDouble doesn't make sense as a function to GHC.

Ironically, you'd need more parentheses to make it work:

go acc book = acc + (priceAsDouble $ bookPrice book)
JB.
  • 40,344
  • 12
  • 79
  • 106
  • 2
    Note also that `($)` is a very plain kind of syntactic sugar: It is just a completely ordinary Haskell function defined as `($) f x = f x` and made infix with the lowest right-associative presedence (`infixr 0`). – gspr Sep 13 '15 at 14:38
  • Which is kind of what makes it tricky to beginners, IMHO. The sugar is in `infixr`, not the function definition. – JB. Sep 13 '15 at 14:44
  • 6
    @gspr It used to be just a plain function, but GHC nowadays treats `$` used infix as actual syntactic sugar. This makes it work a little better with advanced type extensions. – Ørjan Johansen Sep 13 '15 at 14:44
  • @ØrjanJohansen: TIL! Takk :) – gspr Sep 13 '15 at 14:45
  • @ØrjanJohansen Oh my, as if it was confusing enough already. Would you happen to have some link documenting this? You've got my curiosity piqued too, here :) – JB. Sep 13 '15 at 14:48
  • See [this question](http://stackoverflow.com/questions/9468963/runst-and-function-composition), especially hammar's answer. – Ørjan Johansen Sep 13 '15 at 15:05
  • 1
    Thanks! I'll add a direct link to [the ghc-users post](https://www.mail-archive.com/glasgow-haskell-users@haskell.org/msg18923.html) for the curious. – JB. Sep 13 '15 at 15:13