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?

4
  • 3
    Yeah, use a let or where clause in the definition of myLength. Commented Mar 7, 2015 at 18:04
  • 1
    innerLength xs n+1 is parsed as (innerLength xs n)+1, this is not tail-recursive. Commented Mar 7, 2015 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 Commented Mar 7, 2015 at 18:33
  • 1
    @Franky Thanks for pointing that out. That seems to be a constant headache for me with haskell. Commented Mar 7, 2015 at 20:15

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Well, innerLength [] n = n, really.
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).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.