Live data from Hacker News

A philosophical difference between Haskell and Lisp (2015)

chrisdone.com

101–110 of 181 posts

Re: A philosophical difference between Haskell and Lisp (2015)

#101
post #16

Common Lisp doesn’t just have LOOP, it also has Series [1]. I think the actual philosophical difference may be that Haskell does this with compiler support for stream function composition, whereas Common Lisp does it with a set of language-level macros any user could write (if they were as smart as Richard Waters). [1] https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node347.html

Strictly speaking, Common Lisp does not have series because they were not included in ANSI Common Lisp. Therefore even though you could use the macros provided with a separate streams package, you’d need to make sure that all your code used that package (rather than the CL definitions of let and lambda and so on) and properly declared stream functions. This ends up making code using streams not super composable which…

GHC does have special support for fusion and rewriting. See e.g. [1]. The laziness comes with the language, but turning it efficient requires a series of these rewrite rules expressed as GHC-specific metadata.

[1] https://markkarpov.com/tutorial/ghc-optimization-and-fusion....

Re: A philosophical difference between Haskell and Lisp (2015)

#102
post #68

Earlier quoted context omitted.

I disagree. With composition we have general building blocks which can be combined in an infinity of ways, and remain available to combine with newly added functions. The monolith way provides a fixed, non upgradable set of possibilities, so if your need does not fall into what already exists you need to add yet another monolith (see remove-if vs remove-if-not).

Change the Haskell example to read 3 and 5 as n and m from an external file. All of a sudden your beautiful code is now a huge mess and you need to refactor the one liner into at least three separate functions to try and keep the Maybe types from taking everything over. Meanwhile in lisp I would have written a special drop3-take5 function in the first place. Then refactoring it would just take replacing 3 with n and…

Let's say getting those n and m values has a nasty type like `getNM :: IO (Maybe (Int, Int))`. All you need to do is map twice when using the original function.

  foo (n, m) = take n . filter p . drop m

  bar = fmap (fmap foo) getNM
It's a little awkward, but no major refactor. A small price to pay for the nice types we get. I'd argue that if other languages make dealing with IO and optional values easier, its because they don't bother restricting IO (not necessarily criticizing that), or they don't bother checking completeness over optionals (i.e. a stray `null` can show up anywhere and derail the program, which I do think is a bad thing). Overall, Haskell provides many powerful tools for composing complex types, such as the functor/applicative/monad classes.

Re: A philosophical difference between Haskell and Lisp (2015)

#103
I tend to think that most natural languages use the same kitchen sink approach like Lisp, and as a consequence it has a sprawling set of vocabulary. For some peculiar reason, people seem to like it that way. So, naturally speaking, I guess Lisp is probably going to be more popular than Haskell.

Re: A philosophical difference between Haskell and Lisp (2015)

#104
I am less experienced than OP, but I got the impression the philosophical difference is that with Lisp, you are trying to abstract things with a simple language and AST code generation (macros) whereas in GHC Haskell you are trying to abstract things with a very in depth language, heavily based on mathematical ideas (see all those language extensions!).

Re: A philosophical difference between Haskell and Lisp (2015)

#105

I'm a little bit confused about the notion that composition isn't used much in lisp as opposed to Haskell. Isn't composition literally the benefit of S-expressions, prefix notation, and everything being a function?

Lisps lack implicit currying, so function composition becomes much more verbose.

And functions aren't that important in CL, not everything is a function and CL is usually written in an OOP or procedural style. CL is more about data and AST composition then anything else.

Moreover, CL is a lisp-2, so in a very real sense, functions are second class citizens in the language.

Re: A philosophical difference between Haskell and Lisp (2015)

#106
> One difference in philosophy of Lisp (e.g. Common Lisp, Emacs Lisp) and Haskell is that the latter makes liberal use of many tiny functions that do one single task. This is known as composability, or the UNIX philosophy.

I would rather say that each tiny function handles one factor and that a program is the decomposition into factors. Lisp also composes functions but are chunkier and can be ad-hoc one-offs. Static types vs runtime errors is another point of difference.

Re: A philosophical difference between Haskell and Lisp (2015)

#107

There is a strange... kind of poetry with Haskell. It is like math on wheels, math applied to procedures, math with... time. Its appeal to me is like the appeal of math to me, not like real analysis math but abstract algebra math. The beauty, the purity of mathematics of younger days that once became lost after encountering the sad complexities of the world. Understanding every little aspect and being able to prove e…

Very much agree. When solving a problem with Haskell one first has to step back and think in terms of what algebra the underlying domain objects satisfy, then encode that in a combination of Haskell's type system + pure functions. It takes a lot of thought, but if done right, it feels like cracking an elegant proof.

Re: A philosophical difference between Haskell and Lisp (2015)

#108
post #94
post #40

Earlier quoted context omitted.

So if I put (x1, x2...) through pipes f and g, instead of calculating all the f(x)s first followed by all the g(f(x))s, it calculates g(f(x1)), then g(f(x2)), etc. How is that order of operations different to stream fusion? Why does stream fusion require more purity than Unix pipes?

Let's say there's a global mutable variable M (doesn't have to global, can be x1.M = M, x2.M = M, for example, but easier to think with global) which is used by and modified when f(x) or g(y) runs. Then, the order of operations would matter and so you get different results with and without stream fusion.

Right, but couldn't the same thing happen with Unix pipes if the components write to a global file?

You can do the same optimization Unix pipes does without language enforced purity, just with a caveat that functions sharing state might behave weirdly when fused.

Re: A philosophical difference between Haskell and Lisp (2015)

#109

Earlier quoted context omitted.

This comment would be more informative if you briefly explained why you abhor it and hate almost everything about it.

Yep, sorry. This is a matter of feeling, completely irrational, but just my experience. When I program in lisp I get the same feeling when I'm solving an ODE by hand, or a Diophantine equation, or designing a numerical method to approximate the solution of a PDE, or finding the Euler-Lagrange equations of a physical problem. The thing is real and it gets shit done really fast; it is just exhilarating. Moreover, lisp…

Do you prefer analysis over algebra? How do you eat your corn?

(I prefer algebra and Haskell. But I like Lisp well enough.)

Re: A philosophical difference between Haskell and Lisp (2015)

#110
post #108
post #94

Earlier quoted context omitted.

Let's say there's a global mutable variable M (doesn't have to global, can be x1.M = M, x2.M = M, for example, but easier to think with global) which is used by and modified when f(x) or g(y) runs. Then, the order of operations would matter and so you get different results with and without stream fusion.

Right, but couldn't the same thing happen with Unix pipes if the components write to a global file? You can do the same optimization Unix pipes does without language enforced purity, just with a caveat that functions sharing state might behave weirdly when fused.

You could... I guess language designers generally avoid those types of optimizations since it can bring about unexpected results. As in, if syntactically the code is using one order of operations and semantically the code is using another order of operations, even if it's documented, lots of programmers would stumble into difficult to find bugs and be surprised by this behavior. Or worse, just get wrong results and never even find out it's wrong.
Post reply on HN