| > > This claim is a little strong out of context. > The context is... I was merely pointing out that you made the statement "X doesn't reduce to Y", but didn't specify which language you're talking about. I wrote the subsequent stuff under the assumption that you're talking specifically about (GHC) Haskell. > Nobody leaves it up to chance. Of course. I'm not saying that languages or implementations shouldn't pick (and document) an evaluation strategy. I'm saying that confluence is a useful feature, that exception handlers are a useful feature, that simplicity/predictability is a useful feature, but surprisingly (to me) we can only seem to pick two. > No, in a lazy language... Yes, in a lazy language. I'm talking more generally, about whether different (arbitrary) strategies would lead to different behaviour. > Importantly, this means it's senseless to talk about exceptions "being triggered". You either reduce to one or you don't. Whether you reduce to an exception or not depends on the evaluation order. Again, I'm not talking specifically about Haskell, but about confluence. By "triggered", I meant attempting to evaluate an `E`. In the case of `fst (0, E)` a call-by-name strategy would produce 0 without "triggering" (attempting to evaluate) the exception; call-by-need would do the same; call-by-value would trigger it; etc. Note that I'm assuming E behaves like _|_: if we "trigger" an E in any sub-expression, then the whole expression becomes E, and this propagates upwards until either the whole program is E, or we reach a `try x y` expression. In tht case an exceptional x would cause this expression to reduce to y. That's why we break confluence: `try (fst (0, E)) 1` would reduce to 0 under call-by-name and 1 under call-by-value. > the advantages of pure, lazy evaluation with regards to automatic parallelisation of code are of interest only to academics. You're the one who's talking about lazy evalution and automatic parallelisation :) I'm talking about confluence, which is useful whether a language is strict, lazy, serial, parallel, concurrent, etc. I'm saying it's especially useful for concurrency (and concurrency is a prerequisite for parallelism). "Useful"(/"attractive") doesn't mean "automatic parallelisation": it's a whole bag of stuff, including easier to understand code, having the same semantics for both serial and concurrent uses (e.g. no need for the "thread safe"/"not thread safe" notes which litter Java's class reference), etc. Yes, automatic parallelisation is in that bag, but if you think it's only of academic interest then why bother mentioning it at all? > > Google's MapReduce is clearly inspired by functional programming ideas like confluence > I don't agree remotely. They claim to take inspiration from the map and reduce functions from Lisp, which is strict and whose map and reduce functions are more correlated than intrinsically related to functional programming as a whole; even C++ has a particularly imperative one in its standard library. Lisp is a direct descendent of lambda calculus (e.g. McCarthy's "Recursive functions of symbolic expressions and their computation by machine, Part I" uses lambda notation, famously calling the s-expression alternative 'rather formidable for humans to read' ;) ). That makes it pretty functional in my view. So what if Lisp is strict? It also has dynamic scope. That has nothing to do with whether or not it's functional. A more prudent argument against Lisp being functional would be the inclusion of impure, side-effecting operations, which makes programming in e.g. Common Lisp not much different than other impure, non-functional, imperative languages (except for the macros). Still, map and reduce are not imperative in this way, and in fact they're often used to 'remove imperativeness' in many languages (e.g. defining map, filter and reduce with loops, then avoiding loops elsewhere). In fact, reading through "Recursive functions of symbolic expressions and their computation by machine, Part I" I see that a `maplist` function is used as an example :) PS: It looks like you're refuting an argument like 'Haskell is the best language, it invented map/reduce and can automatically parallelise all code, if only people stopped catching exceptions'. That's not at all what I'm saying :) |
My point is basically that you end up making this trade-off far earlier than that, as evidenced by Haskell, so it's not really right to blame exception handlers; `try (fst (0, E)) 1` isn't any more important than `fst (0, E)` and programmers won't let language writers leave the latter unspecified.
> Note that I'm assuming E behaves like _|_: if we "trigger" an E in any sub-expression, then the whole expression becomes E, and this propagates upwards until either the whole program is E, or we reach a `try x y` expression. In tht case an exceptional x would cause this expression to reduce to y.
I feel this gets to the crux of the issue: if you assume this, then a lot of what you said does hold, but you don't have to assume it and many languages (eg. Haskell) don't. Why are you assuming this anyway?
> You're the one who's talking about lazy evalution and automatic parallelisation :)
You brought it up, not me!
> having the same semantics for both serial and concurrent uses (e.g. no need for the "thread safe"/"not thread safe" notes which litter Java's class reference)
Thread safety of this kind just requires a lack of shared mutability (see Rust); purity and more extreme forms like being agnostic to order of evaluation don't really add anything in practice.
> [On Lisp]
I'm not saying Lisp isn't functional (it is), but arguing against your claim that "MapReduce is clearly inspired by functional programming ideas like confluence".