Bercovici Adrian
Bercovici Adrian

Reputation: 9360

How is this expression evaluated?

I am reading Real World Haskell and i can not understand when is the result of the break method evaluated. If it starts with the pre:case suf of clause then it has to go in the let clause fetch the tuple that break method generates and then treat the case expression? Or how does it do it?

splitLines [] = []
splitLines cs = 
  let (pre, suf) = break isLineTerminator cs
  in pre : case suf of
            ('\r':'\n':rest) -> splitLines rest 
            ('\r':rest)      -> splitLines rest
            ('\n':rest)      -> splitLines rest
            _            -> []

isLineTerminator c = c == '\r' || c == '\n'

Is it :

Or what is the order for constructing the case expression?

Upvotes: 1

Views: 95

Answers (2)

Mark Seemann
Mark Seemann

Reputation: 233125

In principle, all Haskell functions are defined using a single expression (at least, if you ignore do syntax). Contrary to statement-based languages like the C family, a function is just an expression.

Sometimes, though, it can enhance readability if you can break up a function definition in two or more sub-steps. Haskell's let...in syntax enables you to do that.

let lets you define one or more names that you can then reference in the in expression. You can define as many names as you want in the let part, but the in part must still be a single expression.

Another option is to use where. You can rewrite the splitLines function using where syntax instead:

splitLines [] = []
splitLines cs =
  pre : case suf of
          ('\r':'\n':rest) -> splitLines rest 
          ('\r':rest)      -> splitLines rest
          ('\n':rest)      -> splitLines rest
          _            -> []
  where (pre, suf) = break isLineTerminator cs

isLineTerminator c = c == '\r' || c == '\n'

In both cases, the expression of splitLines conses pre unto the expression starting with case suf of.

In order to be able to do that, it'll first have to evaluate pre and suf, which are both defined by the expression break isLineTerminator cs. So, in order to evaluate pre and suf, the overall function evaluates break isLineTerminator cs.

It now knows what pre and suf is, so it can evaluate the rest of the case expression and cons the result of that with pre.

Notice that splitLines is recursive, so this'll keep on happening until splitLines hits the base case [].

Recall that Haskell is lazily evaluated, so all of this only happens just-in-time, when a result is required.

Upvotes: 3

augustss
augustss

Reputation: 23004

It’s impossible to say how it’s evaluated unless you specify how the result of splitLines is going to be used. Lazy evaluation does as little as possible. E.g., if you test the result with null then break is never called. If you take the head and use that, break is called, but the case is never evaluated. And so on.

Upvotes: 7

Related Questions