Hacker News new | ask | show | jobs
by ykler 3247 days ago
I don't think many people (and not the parent) "equate" FP with Haskell, but Haskell is in a way the most functional mainstream language because it is the only lazy one. You can have a purely functional language without laziness in principle, but, as Simon Peyton-Jones points has said, laziness, despite having serious costs, keeps a language designer honest by making it impossible to add side effects. The non-lazy languages all have side effects. A language like OCaml can still be relatively functional because of the way it is typically used. While JavaScript is not traditionally used in a functional way but can be.

http://www.cs.nott.ac.uk/~gmh/appsem-slides/peytonjones.ppt

3 comments

You’re mixing up laziness and purity. Purity makes side effects impossible, so to speak, not laziness.

Haskell is the most functional language because a Haskell function is a mathematical function, which can only transform its arguments into a value. Everything is a constant in Haskell, and functions transform one or more constants into a single, new constant.

No he/she isn't. The argument (as advanced by SPJ) is that it is so awkward for a programmer to use side-effects in a lazy language that it is (practically, not theoretically) impossible for a language designer to add them.
I don't know Haskell but how is laziness related to FP?
The only way I can think to relate them is (a) FP tends to highlight the importance of value-semantics over all others, (b) non-termination is an effect, (c) FP also, subsequent to a, tends to emphasize control of side effects, (d) in a terminating lambda calculus all evaluation strategies are confluent/equal under the value-semantics, thus (e) laziness is particularly _available_ in a FP language.
Attempt at translation from CS-speak:

Laziness matters less in a FP language because if we consider non-termination an effect (impure), all pure functions should behave exactly the same regardless of whether they're "lazy" or not (plus in FP sameness is defined as same values, due to "value-semantics" - unlike OO where every object has a unique identity, different from all others) because in the absence of side-effects order of evaluation is not important.

Of course reality is different, and laziness has very visible and important effects when Haskell programs run on today's computers :)

Ha, thank you.
> Haskell is in a way the most functional mainstream language

1. Is it the most functional language because it's lazy? No

2. Is it the most mainstream language because it's lazy? No

3. Is it the most functional mainstream language because it's lazy? No + no = no

Laziness does not a functional language make.

> You can have a purely functional language

What would be the purpose of a pure FP? Oh. There would be no purpose.

> laziness ... keeps a language designer honest by making it impossible to add side effects

wat

Laziness is delayed execution. That's it. There's _nothing_ stopping you from delaying a side effect.

> Laziness is delayed execution. That's it. There's _nothing_ stopping you from delaying a side effect.

Laziness is about more than just side effects.

I think "evaluation" or "reduction" would be better words than "execution" here. Laziness (call by need) is an evaluation strategy for (beta-)reducing expressions, which has two nice properties:

- If an expression can be reduced without diverging by some evaluation strategy, then it can be reduced without diverging using call by need.

- Efficiency, in the sense that no duplicated work is performed.

The other common evaluation strategies are call by name and call by value. Call by name has the first property, but not the second; so there are cases when it's exponentially slower than call by need. Call by value has the second property, but not the first, so there are cases when it diverges unnecessarily.

This 'unnecessary divergence' is a major reason why most programming languages end up overly complicated to understand (at least, mathematically). For example, consider something like a pair `(cons x y)`, and its projection functions `car` and `cdr`. We might want to describe their behaviour like this:

    ∀x. ∀y. (car (cons x y)) = x
    ∀x. ∀y. (cdr (cons x y)) = y
This is perfectly correct if we're using call by name or call by need, but it's wrong if we're using call by value. Why? Because under call by value `(car (cons x y))` and `(cdr (cons x y))` will diverge if either `x` or `y` diverges. Since the right-hand-sides only contain one variable each, they don't care whether or not the other diverges.

This is why Haskell programs can focus on constructing and destructing data, whilst most other languages must concern themselves with control flow at every point (branching, looping, divergence, delaying, forcing, etc.).

Thank you! I clean forgot about call-by-need vs. call-by-value
>Laziness is delayed execution. That's it. There's _nothing_ stopping you from delaying a side effect.

This is true, but unconstraimed side effects are too difficult to reason about in a lazy language to make them practical. So in practice, very few lazy languages have unconstrained side effects.