2

While trying to define a Haskell function which corresponds to repeated evaluation, I ran into an "Equations give different arities" error. This is my code:

nEvals :: Int -> (a -> a) -> a -> a
nEvals 1   = ($) --equivalent to nEvals 1 f x = f x
nEvals k f = (nEvals (k - 1) f) . f --equivalent to nEvals k f x = nEvals (k - 1) f (f x)

I don't quite understand why Haskell is giving this error, since I'm using pattern matching, not explicitly assigning a value for a given set of inputs. Perhaps Haskell only allows pattern matching on a fixed number of parameters? If so, could someone explain why that decision was made?

PrimeNumbers
  • 117
  • 1
  • 8
  • 1
    You can skip pattern matching and think of this as a fold: make a list of `k` functions using `replicate`, then combine them into a single function using `foldr (.) id`. – chepner Aug 13 '21 at 21:09
  • I believe this question is a duplicate, hence I closed it. If you disagree, and think that your question is more nuanced, feel free to explain why so that we can vote to reopen. – chi Aug 13 '21 at 21:10
  • @chepner, ohhh great idea! Didn't think of that, thank you! – PrimeNumbers Aug 13 '21 at 21:26
  • @chi, I'm sorry, that's the same as what I meant to ask, I must've missed that question. – PrimeNumbers Aug 13 '21 at 21:28
  • @PrimeNumbers No problem at all :) . Mine wasn't a complaint -- I just wanted to be sure I did not close this too hastily. – chi Aug 13 '21 at 23:12

2 Answers2

4

You should specify the same number of parameters in all the clauses of the definition of your function. This thus means that you implement this as:

nEvals :: Int -> (a -> a) -> a -> a
nEvals 1 f = f  -- added f as parameter
nEvals k f = (nEvals (k - 1) f) . f

we can also reduce the number of parameters with:

nEvals :: Int -> (a -> a) -> a -> a
nEvals 1 = id
nEvals k = (.) =<< nEvals (k - 1)
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • 1
    I avoided the first implementation because I wanted to try to minimize the number parameters as that's aesthetically nicer to me. I'm just learning Haskell, so I wasn't familiar with the "=<<" function in the second implementation, thank you! – PrimeNumbers Aug 13 '21 at 21:22
2

The Haskell Report section 4.4.3.1 requires it:

A function binding binds a variable to a function value. The general form of a function binding for variable x is:

x p11 … p1k  match1
…
x pn1 … pnk  matchn

where each pij is a pattern, [...]

Note that all clauses defining a function must be contiguous, and the number of patterns in each clause must be the same.

It doesn't explicitly say why, but it does state that:

Translation: The general binding form for functions is semantically equivalent to the equation (i.e. simple pattern binding):

x = \ x1 … xk -> case (x1, …, xk) of
 (p11, …, p1k) match1
 …
 (pn1, …, pnk) matchn

This translation would not be possible with different numbers of patterns.

It might have been possible to instead define the behavior in terms of e.g.

case (x11, …, x1k) of
  (p11, …, p1k) match1
  _ -> case (x21, …, x2k) of
         (p21, …, p2k) match2
         _ ->  ...

but it seems like a mess that it's harder for the compiler to work efficiently with, for very little benefit.

that other guy
  • 116,971
  • 11
  • 170
  • 194