2

I just tried to write a function that removes two subsequent identical entries of a list. (As soon as there are two identical entries in the list, they should be removed.) I came up with following recursive function, which works exactly as expected. It relies on pattern matching:

 f :: Eq a => [a] -> [a]
 f(a:b:xs)|a==b = f xs
          |otherwise = a : f (b:xs)
 f x=x                                -- if the list has less than two entries

Now I though we could rewrite the second case as f=id, but for the code

f :: Eq a => [a] -> [a]
f(a:b:xs)|a==b = f xs
         |otherwise = a : f (b:xs)
f = id

I get following error, which I do not understand:

\path\to\my\program.hs:1:1:
Equations for `f' have different numbers of arguments
  \path\to\my\program.hs:
(1,1)-(2,34)
  \path\to\my\program.hs:
3:1-6
Failed, modules loaded: none.

As I see it both have the exact same number of arguments (one), but GHC does not seem to agree, can anyone explain what I did wrong?

Try it online!

flawr
  • 10,814
  • 3
  • 41
  • 71
  • When you say `f = id`, you're literally just making `f` an alias for `id`. The error comes from the fact though that you can't omit arguments from one form of a function definition, if the other forms have all their arguments explicitly. Annoying, but not a big deal. Just explicitly pass `x` to `id`. Of course, that defeats the purpose though, so just use your first way. – Carcigenicate Jan 13 '17 at 14:08
  • Thank you, that is interesting. I've never come across such an example even tough I have been playing around with Haskell for a while now. Do you know any tutorial/book/reference where this is mentioned? – flawr Jan 13 '17 at 14:14
  • No, I think this is just one of those quirks that everyone runs into eventually. This is rarely an issue though, since most of the time you write function definitions that omit arguments, they're in point-free form, and they only have the one definition since they're just a short-hand for another partially applied function. – Carcigenicate Jan 13 '17 at 14:16
  • Haha, well thank you very much for your explanation! – flawr Jan 13 '17 at 14:18
  • 1
    For some reason the Haskell definition requires all the equations to have exactly the same number of arguments. Probably, the committee decided that a missing argument was much more likely to be a programmer error than an intended eta-contraction, and ruled that out from the Haskell syntax. But yes, it would make total sense. – chi Jan 13 '17 at 14:48

2 Answers2

6

Note that the piecewise definition of a function isn't first-class syntax; it is just syntactic sugar for a case expression:

 f :: Eq a => [a] -> [a]
 f = \x -> case x of
           (a:b:xs) | a == b -> f xs
                    | otherwise -> a : f (b:xs)
           x -> x

This makes it a little easier to see why you can't change the number of arguments from piece to piece: the compiler wouldn't be able to correctly fill in the last line of the case expression.

chi
  • 111,837
  • 3
  • 133
  • 218
chepner
  • 497,756
  • 71
  • 530
  • 681
  • 3
    Well, the compiler could always eta-expand before generating the `case`. It should be semantics-preserving if previous equations had all the arguments (even in the presence of `seq` and function-typed bottoms). Still, this is an interesting point of view. – chi Jan 13 '17 at 14:52
2

You get an error for mismatching the parameters between the different equations of the function. So

valid (Just x) = ...
valid _ = ...

valid2 = ...
valid2 = ...

And this will fail:

invalid x = ...
invalid = ...

If you really want to use id, you still can. Just omit the parameter in the function equation that uses it, and re-introduce it on the RHS in a lambda (I don't know if I would do it personally though, it depends on the situation).


That being said, in GHC, there really is a difference between f = id to f a = a.

The inliner in GHC can only inline functions that are called with the same amount of parameters that are in the function definition.

MasterMastic
  • 20,711
  • 12
  • 68
  • 90