Ah, I think your clarification makes a lot more sense than your original phrasing, because Haskell's position is indeed that side effects are bad, not that effects are bad. The bad thing is "side", not "effects".
Another possible takeaway is to start from something 'principled' (for whatever definition of principled you like) and then add functionality, rather than starting on shaky ground and trying to patch it up later.
From memory the example in the video was 'pure functions first - then figure out IO'. But it could just as easily apply to memory safety, or nulls.
Why build a new language with state-of-the-art static analysis for detecting nulls, when you could just refrain from putting nulls into your new language?
And of course, lambda calculus is even more useless.
But you can still make a nice universal machine that can run any computable function from stdin to stdout, by parsing a lambda term from the start of stdin, and applying it to the remainder [1].
FP is clearly useful for data transformations, and this is where you've seen it catch on in Python and even Java. Outside of that, for as theoretically elegant it is, the reality of using it for real-world, stateful systems is messier, and the fact that none of them have gotten any traction beyond hobby projects or one-off uses says something.
> and the fact that none of them have gotten any traction beyond hobby projects or one-off uses says something.
What are you talking about? One of the world's most resilient systems was written in Erlang (the AXD301)... and was dynamically typed at that, lol. Not to mention WhatsApp, Discord, and others.
Based on a thread a few days ago, I think a lot of people who have never used FP before don't seem to realize that outside of Haskell, it's generally very easy to do state in functional languages. Also, they aren't constantly copying entire data structures every time you modify them which some people just refuse to believe for some reason.
The company I work for operates primarily with a monolith Haskell service. There are many companies that use Haskell, not least including Meta. The idea that FP isn't used in real-world projects is, at the very least, outdated; and, at the very worst, flat-out wrong.
There’s plenty of real-world FP systems written in Scala.
In general, FP is not a problem today, because data mutation usually happens in some kind of external database, and the programs people have to write are just stateless transformations of incoming requests (http or other kinds, depending on the application).
His comment is being taken out of context. He is talking about Haskell before they figured out how to add effects. As he says [1], a program without effects is useless, as it doesn't do anything except make the computer hot. Then they figured out how to combine — in a single language — effectful computations, and effect-free ones, without making them pollute each other. The type-system keeps them apart. This makes Haskell useful. So in summary, he is not saying modern-day Haskell (which has effects) is useless.
No, he’s saying that it is a language closer on the spectrum to useless than useful- and that is because side effects are an afterthought/discouraged rather than a first class citizen.
The only potential for misunderstanding the headline is if you fail to see that it’s hyperbole. Otherwise I don’t see how it’s being taken “out of context”.
No, that's not what he's saying. The original post you are responding to is correct - he produces a quadrant that maps "usefulness" to "safety". Safety was dealt with by removing side effects. Nowhere does he state that side effects are an afterthought or discouraged, what he says is that they need to work out how to seperate code that produces side effects from code that has no side effects.
You stated that "side effects are an afterthought/discouraged" and I stated that "nowhere does he state that side effects are an afterthought or discouraged".
I was paraphrasing, inferring. I didn't mean to make a strong claim about Haskell specifically, but about how side effects correlate with software being useful - a point that I think Simon would agree with.
I still think you and I are in agreement? And indeed, Haskell is closer to being a language that discourages/protects against side effects than one that allows them without regard for the complexity they introduce.
> Haskell is closer to being a language that discourages/protects against side effects than one that allows them without regard for the complexity they introduce.
Haskell doesn't discourage side effects at all, it simply makes them declarative in the form of monads. All monads, whether they're the IO monad or not, are about handling side-effects and then being explicit about it wherever they're used.
So, if you use an IO monad in the middle of your pure function then that function becomes an IO function and it spreads out from there (like async/await in other languages). This encourages you to partition your effectful and non-effectful (pure) code. It doesn’t discourage effectful code.
When he talks - about having more control/safety - this is how Haskell deals with it: by being declarative, not by discouraging or shying away from side-effects.
This whole thread gives the impression that Haskell doesn't want to have any impact on the world around it, which just isn't the case. The fact the `main` function is an IO operation kinda proves that.
SPJs comments are clearly tongue in cheek and self-deprecating in the extreme.
For one, Rust's "trait" system, which is a foundational part of the language that enables a lot of Rust's expressivity, is a limited imitation of Haskell's "type classes". Rust would be a very different language without it.
For those who may not know it: SPJ was employed at Microsoft Research for a long time. He recently moved to Epic Games, where he co-developed the Verse language.
I still wonder why Haskell sans IO isn't a thing. Seems like it would be a really great way to define/implement rules for a rules engine that supported it. I guess you'd still have to worry about OOM or CPU thrashing, but in general it seems like it would be a very expressive language offering a secure sandbox and guaranteeing functional purity for such tasks.
The suggestion is to use pure Haskell for the rules DSL only. The surrounding system that applies any side effects would presumably not be written in that language.
Looks like it has become abandonware, or at least the component they open sourced. Oh well, such is life in the Haskell ecosystem. https://github.com/facebook/Haxl
Simple Haskell could be a thing, there have been some efforts in the community but none really crystalized. Frankly, I don't think the problem is so much difficult I/O, but rather too many language extensions, competing toolchains, and some missing libraries.
Haha, that's exactly what I'm trying to avoid. We're looking at adopting openpolicyagent. Which frankly, a modern JSON-oriented datalog may be ideal. But this whole time I keep thinking, this would be so much easier in Haskell. Granted, the rest of my team would probably find it equally confusing. Maybe even moreso? IDK, it'd be an interesting experiment. If such a thing as Haskell-based policy agent existed.
IO distinguishes execution (of actions) from evaluation (of expressions).
To execute an `action :: IO Ty' for a value of `a :: Ty', you use <-
do (a :: Ty) <- (action :: IO Ty)
..
The 'function' rand is not really a function, but an action. It doesn't make sense to ask which int rand() evaluates to, and we can't equationally reason about rand() as an int. We cannot factor it from `rand() + rand()', or replace it with `2 * rand()' because it is not an int! Haskell is explicit about this, `rand :: IO Int' an action that produces an Int when (effectfully) executed.
The addition of actions doesn't make sense `rand + rand': Num-eric operations do not automatically lift over IO Int.[1] Instead we explicitly write `liftA2 (+) rand rand'. Shorthand for
do r1 <- rand
r2 <- rand
pure (r1 + r2)
where r{1,2} are Ints. The separation between evaluation and execution means we can factor rand out while still executing it twice.
do let r :: IO Int
r = rand
r1 <- r
r2 <- r
pure (r1 + r2)
This factors out the 'recipe', not the value it produces. To factor out the result of the IO-action, we just use a single bind/draw <-.
do r1 <- rand
pure (r1 + r1)
[1] This can be changed with Applicative lifting:
{-# Language DerivingVia #-}
deriving via Ap IO a
instance Num a => Num (IO a)
42 comments
[ 2.9 ms ] story [ 94.9 ms ] threadSimon Peyton Jones – Haskell is useless [video] - https://news.ycombinator.com/item?id=23957953 - July 2020 (1 comment)
Haskell People - https://news.ycombinator.com/item?id=15371448 - Sept 2017 (97 comments)
read that as:
"beyond the well-established idea, from Haskell, that side effects are troublesome and need to be explicitly modelled and thought about."
From memory the example in the video was 'pure functions first - then figure out IO'. But it could just as easily apply to memory safety, or nulls.
Why build a new language with state-of-the-art static analysis for detecting nulls, when you could just refrain from putting nulls into your new language?
But you can still make a nice universal machine that can run any computable function from stdin to stdout, by parsing a lambda term from the start of stdin, and applying it to the remainder [1].
[1] https://www.ioccc.org/2012/tromp/hint.html
https://woodrush.github.io/blog/lambdalisp.html
What are you talking about? One of the world's most resilient systems was written in Erlang (the AXD301)... and was dynamically typed at that, lol. Not to mention WhatsApp, Discord, and others.
Based on a thread a few days ago, I think a lot of people who have never used FP before don't seem to realize that outside of Haskell, it's generally very easy to do state in functional languages. Also, they aren't constantly copying entire data structures every time you modify them which some people just refuse to believe for some reason.
[1] https://youtu.be/iSmkqocn0oQ?t=202
The only potential for misunderstanding the headline is if you fail to see that it’s hyperbole. Otherwise I don’t see how it’s being taken “out of context”.
I still think you and I are in agreement? And indeed, Haskell is closer to being a language that discourages/protects against side effects than one that allows them without regard for the complexity they introduce.
Haskell doesn't discourage side effects at all, it simply makes them declarative in the form of monads. All monads, whether they're the IO monad or not, are about handling side-effects and then being explicit about it wherever they're used.
So, if you use an IO monad in the middle of your pure function then that function becomes an IO function and it spreads out from there (like async/await in other languages). This encourages you to partition your effectful and non-effectful (pure) code. It doesn’t discourage effectful code.
When he talks - about having more control/safety - this is how Haskell deals with it: by being declarative, not by discouraging or shying away from side-effects.
This whole thread gives the impression that Haskell doesn't want to have any impact on the world around it, which just isn't the case. The fact the `main` function is an IO operation kinda proves that.
SPJs comments are clearly tongue in cheek and self-deprecating in the extreme.
They made the claim that the primary function (lol) of Haskell was to discover new PL ideas which then got adopted by the major languages.
Erik Meijer, he's in the video :)
For those who may not know it: SPJ was employed at Microsoft Research for a long time. He recently moved to Epic Games, where he co-developed the Verse language.
It's a good idea, and Facebook actually does something like this for their spam filtering rules: https://engineering.fb.com/2015/06/26/security/fighting-spam...
Isn't that just Haskell without the use of IO... ? Which is already a thing...
There are more ways to implement rule engines and rules than to learn Haskell. One of them could be to use Prolog.
I didn't editorialize the title. The original title I posted was "Simon Peyton Jones — Haskell is useless".
FYI: Simon is a major contributor to the design of the Haskell programming language and a lead developer of the Glasgow Haskell Compiler (GHC).
A previous discussion from 2007: http://lambda-the-ultimate.org/node/2356
To execute an `action :: IO Ty' for a value of `a :: Ty', you use <-
The 'function' rand is not really a function, but an action. It doesn't make sense to ask which int rand() evaluates to, and we can't equationally reason about rand() as an int. We cannot factor it from `rand() + rand()', or replace it with `2 * rand()' because it is not an int! Haskell is explicit about this, `rand :: IO Int' an action that produces an Int when (effectfully) executed.The addition of actions doesn't make sense `rand + rand': Num-eric operations do not automatically lift over IO Int.[1] Instead we explicitly write `liftA2 (+) rand rand'. Shorthand for
where r{1,2} are Ints. The separation between evaluation and execution means we can factor rand out while still executing it twice. This factors out the 'recipe', not the value it produces. To factor out the result of the IO-action, we just use a single bind/draw <-. [1] This can be changed with Applicative lifting:https://www.microsoft.com/en-us/research/wp-content/uploads/...