2

Coming from a Scala background I'm pretty used to using tail recursion to write stuff that can't be easily represented using another method. Let's say I want to calculate the length of a list, but I don't want to use a fold. I want to do it in a natural tail recursive way. I thought of this way:

myLength :: [a]  -> Int
myLength x    = innerLength x 0

innerLength :: [a] -> Int -> Int  -- Tail recursive inner call
innerLength [] _      = 0
innerLength (x:xs) n  = innerLength xs n+1

and this works fine. But it isn't very readable that innerLength is really the tail recursive inner call to count the list members and it seems like innerLength is scoped in such a way someone could just use innerLength instead of the better myLength.

Is there a better way to do this?

Steve
  • 679
  • 2
  • 8
  • 14
  • 3
    Yeah, use a `let` or `where` clause in the definition of `myLength`. – Yawar Mar 07 '15 at 18:04
  • 1
    `innerLength xs n+1` is parsed as `(innerLength xs n)+1`, this is *not* tail-recursive. – Franky Mar 07 '15 at 18:29
  • 1
    What @Franky says, moreover `innerLength [] _ = 0` throws away the accumulator instead of returning it. It should be `innerLength [] n = n`. I'd also be tempted to use strict application in the recursive case e.g. `innerLength (_:xs) n = innerLength xs $! n+1` – chi Mar 07 '15 at 18:33
  • 1
    @Franky Thanks for pointing that out. That seems to be a constant headache for me with haskell. – Steve Mar 07 '15 at 20:15

1 Answers1

4

Yes, you can either use let:

myLength :: [a]  -> Int
myLength x = 
    let innerLength :: [a] -> Int -> Int  -- Tail recursive inner call
        innerLength [] n      = n
        innerLength (x:xs) n  = innerLength xs (n+1)
    in innerLength x 0

Live demo

or where:

myLength :: [a]  -> Int
myLength x = innerLength x 0
    where innerLength :: [a] -> Int -> Int  -- Tail recursive inner call
          innerLength [] n      = n
          innerLength (x:xs) n  = innerLength xs (n+1)

Live demo

See here for the difference between the two.

Community
  • 1
  • 1
Shoe
  • 74,840
  • 36
  • 166
  • 272
  • Well, `innerLength [] n = n`, really. – kini Mar 07 '15 at 18:24
  • 2
    Haskell style often name the auxiliary function just "go" or "aux" since its scope is limited to the function and the alternative is just a repetition of the function name slightly modified, which doesn't bring any readability benefits. Only the top level functions usually get explicit signatures, it is considered that local variables are close enough to the potential problems that they don't need one (except if you wish to clarify a particularly obscure type). – Jedai Mar 07 '15 at 19:15