Is Haskell bad for FP?

DrBearhands on February 10, 2019

After reading a few frustrated posts from people trying to learn FP through Haskell, and after having myself started using Haskell more intensive... [Read Full]
markdown guide
 

I don't know Haskell but I would be interested to know which language you regard as "good for FP", not your ideal language but among the existing ones, which one would you rather suggest to people?

Thanks!

 

I would recommend a Lisp, such as Clojure, Racket, Scheme or Common Lisp.

Lisps were the first languages to support FP.

The advantage of them over others is that it does not complicate things even further with the later extension from simple Lambda Calculus to Typed Lambda Calculus. The latter isn't really about FP, but type theory.

Just as people explore how to type and prove imperative programs, so do FP programs. But this can confuse new comers to FP. Category theory and typed lambda calculus aren't really part of FP, more like orthogonal additions.

Most people trying to learn type theory, category theory and FP at the same time find it too difficult and just abandon.

That's why I don't recommend it. Start with a Lisp, for a simple and direct introduction to FP and lambda calculus.

I'd personally suggest Racket or Clojure over the other Lisps, as they have more active communities and have a bigger focus on FP.

 

I cannot fully agree with what you are saying.

I must admit that I haven't explored LISP dialects yet, but I was under the impression many of them have side-effects, which would keep them from being considered functional.

Untyped lambda calculus has also been shown to be unsound. Admittedly, so has recursive typed lambda calculus without proof of termination.

All things considered I'd say that type systems can have a smooth learning curve but can provide really useful properties later on (which we have yet to fully explore).

This

Most people trying to learn type theory, category theory and FP at the same time find it too difficult and just abandon.

I will fully agree with. E.g. you can use functors and monads perfectly fine in practice without having a clue about CT.

I was under the impression many of them have side-effects, which would keep them from being considered functional

This is one of the confusion I feel Haskell brings about. Lisp invented functional programming.

The first functional programming language and the second oldest programming language still in use (after FORTRAN), LISP began life in 1958 as a project led by John McCarthy at MIT.

From google.com/url?sa=t&source=web&rct...

Purity is an orthogonal concept to FP. Haskell brings about a lot of additional orthogonal concepts which people then confuse and mix in their head.

FP is a paradigm. Fundamentally, it requires first class functions. Any language with first class functions can be used to program functionally.

Now side-effects break the purity of the lambda calculus. But FP is not the lambda calculus. The latter is a computation model, the former a paradigm of programming. And so FP requires design patterns to manage side effects. In turn, in a programming paradigm side effect is unavoidable, because computations are not the only thing you want to program the computer to do. Haskell chooses to use monads for that. And it chooses to add additional compiler constraints that enforce their use for side effect. This is not the only pattern one can use to manage side effects in FP. In most Lisps, no single pattern is enforced on you, you can freely explore different ways to deal with it, and choose which one you prefer.

Untyped lambda calculus has also been shown to be unsound. Admittedly, so has recursive typed lambda calculus without proof of termination.

This also seem to stem from a confusion brought about by Haskell. The untyped lambda calculus has no type system. Soundness is irrelevant to it. Soundness is a quality of type systems.

So, I'm not dismissing the potential utility of monads for managing side effects, or compiler awareness of effects, or of type systems, or the applications of category theory within programming, etc. But all these things are better learned (in my opinion), one at a time, or you risk conflating them and lose the ability to distinguish one from the other.

So if you want to learn functional programming. I still think a Lisp is best. As it is as close as you get to the first incarnations of the lambda calculus and of functional programming.

Plus, Lisp can later allow you to add one by one each additional concepts. You can play with monads and see how they can help you manage side effects. Then you can explore the full range of categorical abstractions and see how they can help you structure your code. With Clojure and Racket, you can also slowly introduce a type system on top, and play with its own set of benefits and trade offs. Finally, you can even attempt to build a type system of your own.

I must admit that I haven't explored LISP dialects yet, but I was under the impression many of them have side-effects, which would keep them from being considered functional.

<Clutching My Pearls>OH NO SIDE EFFECTS! HOW DARE YOU THREATEN MY FUNCTIONAL PURITY!</Clutching>

If you don't have side effects, all you've got is a gently warming black box. ;)

Seriously - I'd take a look at Clojure - or at least some of Hickey's videos where he talks about the philosophy behind it. And possibly the ones where he trashes type systems and reclaims FP for dynamic languages. You may not agree with him but it's riveting.

FP is a paradigm. Fundamentally, it requires first class functions. Any language with first class functions can be used to program functionally.

Wikipedia, which is a decent proxy for popular opinion and therefore linguistic semantics, would call that functional style, and places functional programming under the mantle of declarative programming, excluding thereby the existance of side-effects in FP. This is my reasoning for saying impure languages are not functional.

But let's avoid further confusion and call them pure and impure for now.

Impure functional programming is not backed by many of the mathematical properties that make pure functional programming easy to reason about.

When I post about FP and what you can do with it, it is referrring exclusively to pure FP.

I would therefore not recommend my readers to learn languages based around impure FP, as that paradigm can be achieved with more popular languages that one might already know, like JS.

I feel that when you refer to 'pure' FP you are referring to languages with a static type system which is checked on compilation. Probably languages with higher-kinded types. This is useful for writing programs in a functional style as it places hard restrictions on what you can write.

A programmer in a dynamic language can write the same program, in the same style, with the same (mathematical) properties - only they will not be checked by the type system. You will have to limit yourself to using the parts of the language which don't mutate - you will need a bit of discipline. This is easier in a language like Scheme which makes it very clear when you're performing a function which mutates a variable.

It's harder these days to find a language that won't support a functional style of programming - even Java now has first class functions - which is why I prefer to refer to it as a paradigm and not a language feature.

Purity is tied to compilation and static typing, almost by necessity. While I have no idea how a pure language that is dynamically typed would look like, it would still have e.g. explicit dependencies to make refactoring easy, whereas a language with side-effects does not.

Scheme which makes it very clear when you're performing a function which mutates a variable

Depending on how this is implemented, you might call it a linear type or monad in disguise. If side effects are explicit they're not really side effects.

Purity is tied to compilation and static typing, almost by necessity.

function addOne (x) {
  return 1 + x
}

How is this impure?

You might be able to make a pure functions, if you don't consider type errors to be side effects (which I do if they don't require explicit catching, which is unlikely in a dynamic language), but that doesn't make the entirety of the language pure.
How are you going to represent effects? As you pointed out you will need at some point. Untyped monads? A framework like TEA but error resistant?

So, you might be able to do it, but it's going to be a bit awkward at least, which is why I said "almost by necessity".

Please, let's put aside the denotational debate, because I'm frankly not interested in it, and it is pointless.

Let's discuss the concepts and intended meanings instead. And so let me ask you a question:

When you said "Haskell is bad for FP", what are the concepts you were referring too? And in what way was Haskell bad for them?

Please, let's put aside the denotational debate, because I'm frankly not interested in it, and it is pointless.

That may be, but you have convinced me! I should be stating pure FP specifically, to avoid confusion :-)

When you said "Haskell is bad for FP", what are the concepts you were referring too? And in what way was Haskell bad for them?

I was referring specifically to (primarily statically typed and compiled) purely functional programming. I don't think Haskell has too much influence over impure languages.

If I had to summarize my issues, I would say that Haskell, for a flagship language, strays too far from the core concepts of purely functional programming / typed lambda calculus. It adds complexity (String/Text, typeclasses, lazyness...), reduces certain arguably desirable properties (errors break programs-as-proofs) and it is inflexible in how it represents the imperative world in a functional context, pushing IMO too much coding in IO monads, which is essentially imperative programming.

Because Haskell is, again, the flagship of pure fp, it really shapes the notion of what pure (typed) fp is. I think this may be bad for adoption and innovation.

At this point, though, I should probably write a new post about what I think purely functional programming could / should be.

Haha, ya, I think if you added pure in front of FP for your post title, all confusion would have been avoided, at least for me.

Especially because I think purity is a hurdle for newcomers, and a hard stepping stone. So I find if people start with impure, and gradually of their own learn the benefits of purity and the mechanism to write more and more pure code, it can be a better stepping stone for them moving to a purely FP language like Haskell. It's a more gradual learning curve, I feel.

So I actually thought that this was what you meant also.

I'd actually be really interested in that follow up post you mention. Because my only experience with purely functional languages is Haskell. And I was under the impression that most of the concepts you say bring too much complexity were actually necessary to upheld purity.

For example, without typeclasses, I thought the type system would be a lot weaker, and you'd lose quit a bit of its expressiveness. That without laziness and non-strict evaluation, you were not able to isolate side effects and extract them out of your functions so they evaluate purely. I also thought you couldn't match the performance of impure algorithms without it. And similarly, without Monads and its syntax sugar over them, IO effects couldn't be tracked and controlled by the type system.

Basically, I've always been under the impression that all of Haskell's complexity was due to it trying to maintain purity at all cost.

So I'm intrigued to learn of alternative ways, that could be arguably simpler, yet would still allow for practical, performant and strictly pure code.

Regards!

Haha, I guess that is a point in favor of the idea that Haskell is too dominant. :-)

 

Thank you Didier! Didn't know Racket existed!

 

I think Racket/ Scheme does a better job at teaching people about FP because of their simplicity.

 

I generally suggest getting started with Elm. It's often considered a gateway language.

After that, it really depends on your interests. Perhaps a crash course in category theory and/or logic before diving into more complex languages like Idris. Unfortunately, I have not found a good excuse to devote the necessary time to the later myself yet, though its concepts seem really exciting.

 

I should probably point out that Haskell is still a good language to learn. Especially if you're at all practically minded and want something mature with at least some libraries for most common problems (though not s rich as Node.js or python). It's just maybe not the best one to start with.

Thank you! Elm as a gateway is a concept that I've already came across, interesting! It's also quite practical :D

The problem with Elm is the incredible religious community towards the maintainer (you are pretty much at the mercy of them) and that they had to rename everything from the already established names in the FP community, which will just confuse you when you switch away from Elm.

If you want to do Frontend Dev with FP and what to get a gateway drug, I'd try PureScript.

The problem with Elm is the incredible religious community towards the maintainer

I've heard about this in the past but a lot of languages with BDFLs have detractors :D

If you want to do Frontend Dev with FP and what to get a gateway drug, I'd try PureScript.

Ahah I'll consider it, thanks!

While I agree with the notion that renaming existing concepts is a bit problematic (I've already criticized the name "custom type" to indicate sum / enum types), and that mods and vocal community members can be rather zealous, I do believe Evan himself is generally rather thoughtful and the BDFL model does have its own set of advantages, such as allowing for unpopular decisions that will eventually benefit the language.

Elm is OSS, however, so you can pretty much fork it and roll your own if you were so inclined. People have done so in the past.

 

I recommend OCaml. It is a pragmatic functional language, near as succinct as python, clear, fast, safe, simple.

 

And for people who don't like the syntax, there is also Reason.

 

Just to leave my two cents here 😅

  • Backwards compatibility: This is not really Haskell-the-language but the Prelude and while yes it's incredibly bad and should be changed, you can at least use a different prelude that e.g. uses Text everywhere. What I mean is that it's not a flaw deeply rooted in the language but the library eco system. Haskell-the-language actually has some breaking changes from time to time, for example in I think four releases * won't be the name of the kind of types, it's Type (so 5 :: Int :: Type, not 5 :: Int :: *)

  • I don't get the second point. You always write executables at the end, just because you use a bunch of AWS APIs this does not change

  • Laziness: Maybe. It makes a lot of algorithms easier to express and allows you to treat your algorithm as data structure you traverse (which is pretty cool imo). Also without laziness, some normally trivial identities do not hold.

  • the bottom that every type inhabits is sadly a direct result of the halting problem, getting rid of it would require haskell to become a theorem proover like Agda or Coq

  • too many features: yes, there are a lot of extensions and they might be overwelming for a newcomer, but for beginners they don't matter, the features are tucked behind extensions and you should know what you are doing before enabling them. A beginner won't need them, but seasoned programmers can use the type level features to make their software even more resiliant and safe.

As a big note: I am not trying to change your mind or want to proclaim my opinion as better or above yours, it's just that: my opinion. I came to Haskell after I got fixed by FRP doing frontend work and quickly fell in love. Nowadays I write all my backend code in Haskell.

 

Nowadays I write all my backend code in Haskell.

As do I! :-)
On the whole though, I'm not saying Haskell is bad compared to other existing languages. Instead, I believe FP can be a whole lot more than Haskell, and that this is currently not properly being explored, partially because of Haskell's dominance.

you can at least use a different prelude

I will have to look into that.

You always write executables at the end

This is not true. You will very likely need some kind of executable at some point to run your program. What you write is a solution to a problem. With FP, you don't need to know what the underlying architecture is, only how the functions are composed from smaller functions.

E.g. in big data applications, you might map a pure function f over a data source and split the computations over many nodes. This can be expressed simply as fmap f stream. Done. Let the compiler figure out how to turn that into executables, config files and deployment actions.

allows you to treat your algorithm as data structure you traverse (which is pretty cool imo)

True, and agreed. There are certainly upsides to non-strictness, which is why it became popular in the 80's in the first place. For the flagship of FP, I think it's a bad quality.
OTOH, maybe it's possible to turn non-strict code into strict code at compile time.

without laziness, some normally trivial identities do not hold

Interesting, do you have more info about this?

the bottom

I'm not really concerned with the bottom. While I'm in favor of fully functional programming, I don't think it's necessary. I'm not really sure what you're commenting on though.

for beginners they don't matter

Sort-of. If you're programming as a learning exercise it's fine. If you want to make something "real", you will likely need libraries, which are often made by more experienced devs. So for real-world development it has a bit of a wall in its learning curve.

When you come across a function with a polymorphic type with 7 instance requirements... I can understand optimization aspect of it but damn...

 

I will have to look into that.

Just put NoImplicitPrelude into your .cabal file or put {-# LANGUAGE NoImplicitPrelude #-} at the top of ever file. I use Protolude for example: stephendiehl.com/posts/protolude.html

Interesting, do you have more info about this?

For example this: head . fmap f = f . head. If f bottoms on the second element in the list, the left will bottom and the right not. In a lazy language both won't.

I don't think it's necessary

Bottom is just the name for crashes (which is different than errors), ie non-recoverable, like panic!() in Rust. Strict languages don't need to bother, because a crash will just instantly crash the program. In a lazy language this is not the case, so you need a name to talk about this behavior.

Ah, sorry, I meant "fully functional programming" isn't necessary, not bottom :-)

 

Just curious, had your backend ever dealt with 10k operations per second?

No. I have played around with HPC a bit, but nothing client-facing.

The server I'm currently working on is made to fully scale horizontally, as it should theoretically be capable of keeping up with the likes of youtube, but I haven't gotten to the point of testing capacity yet.

Previous backends I set up would usually not even get to 1k / day. Which was a bit frustrating.

Not mine, because I mostly write internal tools. But other have. warp, the Haskell web server has really good performance. Most of it is thanks to the excellent runtime of Haskell. I once sae a blog post where they wrote a simple webserver for static files and the performance was comparable to nginx

According to field reports by my fellow colleagues, every naive network-bound implementation does 8-9K RPS, which is good enough 9 times of 10.

 

I'm learning FP with JavaScript, trying to escape from the uncertainty of jQuery DOM control and somewhat imperative NodeJS and maybe moving to React Hooks.

Am I nuts? Should I just learn Clojure? I did start out with Common LISP but haven't touched any LISP for 20 years so my memory of it has faded!

On the Haskell side of things, there is so much in JavaScript of course that is 'bad' - but I'm learning to value an effective style which cleans things up. Is Haskell too opinionated to all a similar type of flexibility?

 

For me, Haskell is an exercise language. I used it professionally for a while, but felt that the trade-off between safety and reduced development velocity was not always worth it.

Now I practice Haskell as a hobby, and use its strengths in other languages. Typescript is an excellent language for this, as it is getting mainstream and JavaScript has always had some functional DNA to it — but regularly playing with Haskell can even improve your reasoning about PHP code.

I feel like Haskell is a good teacher exactly because it can be such a pain to work with.

 

You are going to miss some of the benefits of FP by doing functional style programming in JS, rather than relying on purity.

I myself have not noticed any reduced development speed in Haskell or other ML dialects. On the contrary, any non-trivial work is a lot easier because of the reduced complexity of reasoning about pure code with well-defined types.

Haskell specifically can be a little slower due to setup and library complexity as well as compile times.

 

I think FP and Haskell devs are getting trapped by the same mistakes as OO, and could eventually find themselves similarly discredited. IMO there is an unhealthy (historically west coast) focus on Programming Language while ignoring the automation potential of Monads. NYC fortunately is not following this path. The smarter the Monad, the less worry about code.

AI-driven software automation e.g. Monadic or RPA-like software automation is the future and Haskell will be struggling to stay relevant in a few years. No one wants to keep coding all that plumbing just to orchestrate some lambda code.

 

I'm just getting started with Haskell, so my opinion might not be the most accurate one.

There are two big barriers when getting into Haskell:

  • the complexity of the ecosystem: Stack vs. Cabal, bad text editor support, complex docs. All of that make it hard to get started. Other languages do much better in that sense (i.e. Rust).
  • the big part of the community that focuses on the CT heavy, research and academic oriented face of Haskell. Quite often a library points out to a paper as part of the documentation. This scares a lot of people of. But in reality, the industry face of Haskell is much approachable.

Both of them are solvable, but require effort.

I still think it's worth to invest, both personally and as a industry, in the language. Although I would not mind a "cleaner" version.

EDIT: to answer your question. I think it's both bad and good. Bad because of the arguments you mentioned and the ones I did. Basically the entry barrier is high. And good because it's a natural progression for those learning FP. At least in my experience it was.

Another language will not necessarily solve these problems. E.g. the academic tendency will still be there.

 

To me, the biggest entry barrier was indeed cabal, but also the cryptic errors.

After a while you get better at reading the errors, but I feel like the threshold could be so much lower for beginners if type system errors were reported in something that resembled English.

 

yeah, stack and cabal is a unfortunate historic development, but luckily both camps are slowly converging.

 

I think the problem with Haskell right now is the lack of good quality tutorials. Readers trying to learn the language have only a few good options like:

learnyouhaskell and schoollofhaskell and to be honest those look dated.

Compared that to Javascript or Typescript where you can find all sorts of up-to date tutorials and expert opinions.

Also if you search in stack overflow the top questions are about fundamental things like
stackoverflow.com/search?q=haskell

Q: Getting started with Haskell
Q: What is a monad?
Q: Large-scale design in Haskell? [closed]
Q: What is Haskell actually useful for? [closed]
Q: Good Haskell source to read and learn from [closed]
Q: “What part of Hindley-Milner do you not understand?”

If you just look at those titles you may wonder whether this language is actually used in practice or is just a toy language.

It's also really difficult to keep things simple and explain things in a concise way before the reader bails out. Personally the moment a tutorial touches terms like Monads or Category theory jargon I get lost.

 

Haskell is certainly one of the less popular languages out there. I think a lack of tutorials is just a symptom of that. The community does have a strong basis in math, because having that will make you appreciate the language more. I do believe Haskell has not done a very good job of translating the theoretical know-how into practical benefits. Not yet, anyway.

 
 

@mudasobwa is a known Elixir user out here
@kspeakman is a known F# user out there

You should follow them!

 

I've been learning F# recently. I like it, though I keep having to refer back to OCaml's docs to get a better understanding of what's going on.

 

What do u think about scala? I am currently enrolled in Martin's course and I am loving it.

 

I've only looked at it briefly. I saw side effects and decided to look further. If you have side-effects, you're not doing FP.

 

Yes Scala gives you that option but it doesn't mean you can't do FP in Scala. It has all the features of an FP language. It also give you an amazing type system, consistent Apis for all the data structures, concise syntax and many other great features. Compile time is fast and like Haskell it does have a lot of features but you don't have to know everything to get started.

Also learning FP is more of a paradigm shift and that's probably why most of us think that Haskell or Scala are not beginner friendly because we confuse (simplicity with familiarity).

You can do functional programming in most languages. The good thing about Haskell is that it does not compromise. Scala forces you to have a lot of self-discipline to not do bad stuff.

That's correct. We might as well call C functional if that's the criterium.

Functional programming entails two things:

  1. no side effects
  2. computation as evaluation of mathematical functions

2 does imply 1, but let's ignore that for now.

If you don't have the guarantee of 1, there are a lot of properties that just do not hold for you program. You might call it functional style programming, but calling it functional is incorrect.

 
 

I love Haskell. It's cool.
Probably have to find the material that suits you first, but it's much easier now.

 

Juss stick yer Rust in an AWS Lambda and let 'er rip!
That's all the FP you really need ;)
~ Me, an intellectual, xD

code of conduct - report abuse