I learned Haskell in just 15 years

9 days ago (duckrabbit.tech)

Cute. All kidding aside, though, functional programming is worth the effort to learn, and it doesn't actually take 15 years. The payoff is at the end of the article:

"It’s quite natural to program in Haskell by building a declarative model of your domain data, writing pure functions over that data, and interacting with the real world at the program’s boundaries. That’s my favorite way to work, Haskell or not."

Haskell can be intimidating, though, so I would recommend F# for most beginners. It supports OOP and doesn't require every single function to be pure, so the learning curve is less intense, but you end up absorbing the same lesson as above.

  • Yes - the value of functional programming isn't that working in OCAML, or F#, or Haskell is 10x as productive as other languages. But that it can teach you worthwhile lessens about designing software that apply equally to imperative languages.

    Modelling the business domain, reasoning and managing side effects, avoiding common imperative bugs, these are all valuable skills to develop.

    F# is a great language to learn, and very approachable. Worst part about it is interacting with antiquated .NET API's. (I can't believe the state that .NET support for common serialization formats is still in...)

    • > Yes - the value of functional programming isn't that working in OCAML, or F#, or Haskell is 10x as productive as other languages.

      This is not true in my personal experience.

      As has been famously said (paraphrased): Functional programming makes tough problems easy and easy problems tough.

      In other words the value of functional programming depends on your domain.

      30 replies →

    • Hot take of the day: you learn that with imperative programming just as well.

      I familiarized myself with fp to the point of writing scheme and haskell around 15 years ago. Read the classics, understood advanced typing, lambda calculus and so on. The best “fp” I’m using nowadays is closures, currying in the form of func.bind(this[, first]) and map/filter. Which all are absolutely learnable by the means of closures, which are useful but I can live without. Sometimes not having these makes you write effing code instead of fiddling with its forms for hours.

      Still waiting for the returns from arcane fp-like code I produced earlier. Cannot recognize nor understand none of my projects in this style that I bothered to save in vcs. Imperative code reads like prose, I have some of it still in production since 2010.

      These FP talks are disguised elitism imo (not necessarily bad faith). Beta reduction and monadic transformers sound so cool, but that’s it job-wise.

      11 replies →

    • I wouldn't even say antiquated, modern .Net APIs can suck to work with too, the entire ecosystem is written for C# ASP.Net core and everything else feels second class.

      I love F#, the working with C# elements of the language drove me away.

      1 reply →

  • I was a college dropout and self taught bash and python programmer and quite some time ago, I read about Haskell, decided to teach myself to use it, and then realized I had absolutely no idea what programming actually was, and basically spent the next 15 years teaching myself computer science, category theory, abstract algebra and so on, so that I could finally understand Haskell code.

    I still don't understand Haskell, but it did help me learn Rust when I decided to learn that. And I think I could explain what a monad is.

    edit: It's a data structure that implements flat map. Hope this saves someone else a few years of their life.

    • > I still don't understand Haskell

      It's not you. Haskell has very bad syntax. It's not hard to understand it, it you rewrite the same things in something saner. Haskell was developed by people who enjoy one-liners and don't really need to write practical programs.

      Another aspect of Haskell is that it was written by people who were so misguided as to think that mathematical formulas are somehow superior to typical imperative languages, with meaningful variable names, predictable interpretations of sequences of instructions etc. They, instead, made it all bespoke. Every operation has its own syntax, variables are typically named as they would in math formulas (eg. X and X'). This makes no sense, and is, in fact, very harmful when writing real-world programs, but because, by and large, Haskell never raises to the task of writing real-world programs, it doesn't deter the adepts from using it.

      18 replies →

    • I think that's a good starting definition for programmers, but still could cause confusion when you run into something like IO in Haskell. IO isn't really a data structure, and it's hard to fit the "flat map" concept to it.

      1 reply →

  • I feel the same pay-off - but arrived at that point via Clojure. Immutable-first, aim for purity, ability to drop out of it when necessary.

    As stringent as you need it to be (static vs. dynamic types vs. specs), as flexible as you want it to be.

  • I really wanted to like F#, and I kinda do, but it has a number of quirks, compiler issues and cracks in the design that are getting worse:

    First off, the compiler is single-pass. All your definitions have to be in order. This even extends to type hints - it can't use clues to the right to deduce the type of an expression on the left. This is supposedly for perf reasons, but the compiler can become extremely slow because the inference engine has to work so hard - slower than GHC for sure.

    Speaking of slowness, Haskell is surprisingly fast. Idiomatic Haskell can be within 50% the perf of C, since its laziness and purity unlock powerful optimizations. F# is eager and the compiler doesn't do anything fancy. Perf often makes you reach for mutable state and imperative structure, which is disappointing.

    The OOP paradigm feels bolted on. Should you use classes or modules? Pure functions or members? It depends, what mood are you in? Unfortunately only member functions support overloads, and overloads are useful for some SFINAE-type patterns with `inline` functions, so they get a bit overused.

    `ref` struct support, which is vital for zero-copy and efficient immutable data, have very primitive support. even C# is ahead on this.

    Very limited support for implicit conversions, no support for type classes and no function overloading leaves F# with nothing like a numeric tower you'd have in Lisp, and makes building something like Numpy clunky.

    I use C# at work, and I love Haskell, so I really wanted to love F#. But it just doesn't get the love it needs from MS, and some design decisions aren't aging well - particularly as C# itself evolves in directions that are tricky for F#'s aging compiler to support.

  • I feel like functional programming is pretty trivial. It's pure programming that is very difficult.

    They're often conflated because Haskell is pure and functional and probably the most talked about heavily functional language.

    I certainly didn't know that impure functional languages like OCaml existed for ages.

  • That’s interesting because F#’s OOP, as someone who knows neither C# nor Java, makes it more intimidating to me than OCaml.

    Also interesting that when FP is mentioned, Hindley-Milner is implicitly understood to be part of FP too even though it doesn’t have to be. Clojure emphasizes immutability and FP but with dynamic typing and everything that comes with that.

  • > doesn't require every single function to be pure

    having never done F# or haskell, doesn't that start getting into the territory of languages that encourage functional programming like ruby or javascript (modern javascript)?

  • I would suggest Scala as FP for beginners. It doesnt forces you to do pure functions. And its really beginners friendly to start with.

  • In modern Fortran, functions should be pure (although the language does not require this), and procedures that mutate arguments are made subroutines (which do not have return values).

    • Note that Fortran's interpretation of the term "pure" bizarrely allows a "pure" subprogram to depend on mutable state elsewhere (in a host, a module, or a COMMON block). So Fortran's "pure" functions aren't referentially transparent.

      (F'2023 added a stronger form of "pure" and calls it "simple", but it didn't strengthen the places where a "pure" procedure should be required to be "simple", such as DO CONCURRENT, so being "simple" will be its own reward, if any compiler actually implements it. And a "simple" function's result value can still depend on a mutable pointer target.)

  • But why do we need Haskell for this?

    • Realistically we don't but it's very rare to meet a programmer who understands these distinctions thats not also a great functional programmer.

      This is my experience after spending five years as a Haskell programmer and managing a Haskell team for several years and now moving back to the c++ world to play with AI.

      I know lots of good c++ programmers working on cutting edge stuff, real experts in their field, but they sometimes still don't have a clear way to understand how to model data

      That is my opinion. It's probably highly contentious.

      5 replies →

  • I would recommend neither of those.

    Haskell has very bad syntax (with extensive backing from Microsoft, iirc the guy who writes the compiler is a Microsoft's Research employee).

    F# is a straight-up Microsoft's language.

    It doesn't matter what other benefits it has. Just don't touch anything created by that company, and you will have one fewer regrets in your life.

    But, if you still want a language from that category: SML or Erlang would be my pick.

    • SPJ has left MSR and is now at Epic games, working on a new PL. However, even while he was at MSR, MS didn't really have a say in how Haskell was developed.

      3 replies →

    • > Just don't touch anything created by that company, and you will have one fewer regrets in your life.

      :-)

In my opinion, if you are after the mystical experience of understanding functional programming, you're better off learning Prolog. I think it has more to offer in terms of insight, because wrapping your head around the language only takes a couple days, but wrapping your head around its consequences is a gift which keeps on giving for quite some time.

Immutable functional programming is basically what 80% of your Prolog code will look like. The benefit is that you'll be able to understand how everything works from end-to-end.

  • Prolog is a logic programming language though, I wouldn't expect it to have a lot of overlap with functional programming?

    • Prolog variables are immutable by default. Data structures are the same as the immutable functional programming counterparts (no arrays, and tree based everything). Recursion is the only way to loop. Map, filter, fold(l/r), reduce, accumulate, etc, are staple predicates.

      As I said, 80% of your code, or more, will look just like a functional programme.

    • An execution pipeline in a functional language is just half a relation in Prolog. Prolog allows you to also run it 'backwards', you can provide the output and have it figure out what the inputs would need to be.

      6 replies →

    • It's also biased into informal definitions, mixing side effects with logic, and encapsulating complex behavior together. It's way more biased into being a scripting language, while Haskell has all those biases pointed at being an application language.

      So, I'd say that both languages lead people into very different programing styles.

Great read! Can anyone here recommend a good resource for learning Haskell that's in the style of "Text-Mode Games as First Haskell Projects"? Haskell has been on my radar since forever, and I've got some FP concepts internalized by making a side project in F#, but I have no idea what a monad really is and a fun prohect to code along might be perfect.

  • I've been learning Unison [1] and I highly recommend. It's a Haskell-like language, but with some really interesting ideas around how code should be managed and distributed. They also use use algebraic effects (represented with "abilities" in Unison) instead of Monads, which gives some interesting advantages [2].

    [1] https://www.unison-lang.org/

    [2] https://www.unison-lang.org/docs/fundamentals/abilities/for-...

    • Have you written anything in Unison yet? To me Unison feels extremely ahead of its time. They clearly thought things through and aren't afraid to challenge the status quo. Maybe a bit too much ahead of its time even...

      I fear `ucm` a little. You mean I can't version my things with git? How do I... ehh, do anything? And how is the deployment story if one chooses not to use the Unison cloud?

      3 replies →

  • https://learn-haskell.blog/

    > In this book, we will implement a simple static blog generator in Haskell, converting documents written in our own custom markup language to HTML.

    > We will:

        Implement a tiny HTML printer library
        Define and parse our own custom markup language
        Read files and glue things together
        Add command line arguments parsing
        Write tests and documentation
    

    > In each chapter of the book, we will focus on a particular task we wish to achieve, and throughout the chapter, learn just enough Haskell to complete the task.

  • "Programming in Haskell" by Graham Hutton has a few small text mode games in the second half of the book: Nim, Hangman, the game of life, and Tic-Tac-Toe; and it walks you through the minimax algorithm. The author also implements a solution to the countdown problem, which is hard to explain, so as an example, you're given a sequence of numbers [1, 3, 7, 10, 25, 50] and the target 765; a correct solution is (1+50)*(25-10).

    The first half of the book is more geared towards a newbie to functional programming in general.

  • Haven’t you heard? Monads are burritos!

    In all seriousness, it’s not “text mode”, but one of the things that I felt really showed how cool Haskell and a friend could pure model could be a was Netwire and Functional Reactive Programming. It allowed me to design graphical applications the way I always wanted to instead of how they’re typically structured in imperative languages. There are lots of tutorials out there for making little games with it.

    • Ugh, autocorrect and it's too late to edit.

      I was suggesting the Netwire library, an FRP library for Haskell.

What's the benefit of learning a PURE functional programming language, opposed to just using a language which has adapted the best bits and pieces from the functional programming paradigm?

Given that you want write code that sees "real world" use, and is used to handle data and events from the real world. To me, sometimes the line between optimized code and intellectual curiosity blurs.

  • > What's the benefit of learning a PURE functional programming language

    1. It makes it easy to learn how to structure a program in a pure way, which is hard to do in languages that offers you a easy way out.

    2. Since "everything" is pure, writing tests is easier.

    3. You know for certain that if you discard the result of a function call, all the side-effects that it would normally trigger would be stopped as well.

    4. A program where all side-effects are guaranteed to be pushed to the boundaries, is a program that's easy to reason about.

    > a language which has adapted the best bits and pieces [...]

    Languages that has adapted to best bits and pieces from X, Y, Z tend to be worse than a language specifically for X, Y and Z.

    For instance, Java supports functional programming but functional programming languages are much better at it because they were designed for that specific paradigm. In the same vein, sure you can write pure programs in F#, but not as easily as in Haskell that was designed for doing just that.

    > and is used to handle data and events from the real world

    Pure code really only means (in practice) that side-effects are controlled, which is generally very helpful. It forces you to structure programs in a way which makes it easy to pinpoint where data is coming in, and where data is going out. It also makes for easier testing.

    Being able to know, definetly, the answer to "will calling foo perform a network request" without having to read the source for foo is quite nice, especially when dealing with third-party code.

    All this said, I probably wouldn't begin with Haskell. A language like Elm is much better suited for learning writing pure programs.

    • Agree with all the reasons, but number 1 is really the most important:

      > 1. It makes it easy to learn how to structure a program in a pure way, which is hard to do in languages that offers you a easy way out.

      When there's an escape hatch, you will reach for it at some point. It helps with getting things done, but you never end up really confronting the hard things when you have that, and the hard things are an important part of the learning/benefit.

    • The problem with Haskell is that it's slow and memory-heavy (and OCaml is the same, but worse). F# and Scala (and Clojure?) are pretty much the only reasonably usable FP languages.

      4 replies →

  • What's the benefit?

    You start to see functions as self-contained things, as lego blocks. All the logic of the function is there in the function. It only works on values it receives as inputs (it can't read global variables). It only outputs its results (it doesn't assign them to some other global variable that you have to track down).

    This makes your code modular. You can add a function in a chain of functions, if you want to perform an extra transformation. Or, you can replace a function with a different one, if you want to change something about the logic.

    • Is there a benefit if you're already familiar with writing functions like that? Is it wrong for me to expect that most programmers are already familiar with functions that only use their inputs, but treat that style as significantly more optional?

      I wrote pure functions for a minute there but that's not the same, a function that only uses its inputs can modify an object while a pure function would have to return a new object. But, similarly, I bet that a lot more people know about pure functions than have any working knowledge of Haskell.

      18 replies →

  • This is all myth. People don't write Haskell, because they read why other non-Haskellers also don't write Haskell, based on what other non-Haskellers wrote.

    > a language which has adapted the best bits and pieces from the functional programming paradigm?

    Why write in a statically-typed language when dynamically-typed languages have adapted the best bits and pieces from statically-typed languages?

    • > Why write in a statically-typed language when dynamically-typed languages have adapted the best bits and pieces from statically-typed languages?

      Unfortunately, dynamically-typed languages haven't adapted the best bit from statically-typed languages: that all types are enforced at compile-time!

      1 reply →

  • Don't think of it as being all pure code, think of it as tracking in the type system which parts of your code may launch the missiles and which parts can't. Given the following program,

        main :: IO ()
        main = do
          coordinates <- getCoords
          launch trajectory
          where
            trajectory = calcTrajectory coordinates
        
        getCoords :: IO Coordinates
        getCoords = -- TODO
        
        launch :: Trajectory -> IO  ()
        launch = -- TODO
        
        calcTrajectory :: Coordinates -> Trajectory
        calcTrajectory = -- TODO
        
    

    I can look at the types and be reasonably certain that calcTrajectory does no reads/writes to disk or the network or anything of that sort (the part after the last arrow isn't `IO something`), the only side effect is perhaps to heat up the CPU a bit.

    This also nudges you in the direction of an Functional Core, Imperative Shell architecture https://www.destroyallsoftware.com/screencasts/catalog/funct...

    • >as tracking in the type system which parts of your code may launch the missiles

      given that Haskell is lazy by default there's a million ways to shoot yourself in the foot through memory leaks and performance issues (which is not unlike the problems the IO type attempts to make explicit in that domain), so I never really understand this kind of thing. Purity doesn't say much about safety or semantics of your code. By that logic you might as well introduce a recursion type and now you're tagging everything that is recursive because you can easily kill your program with an unexpected input in a recursive function. To me this is just semantics you have to think through anyway, putting this into the type system just ends up creating more convoluted programs.

  • Haskell interfaces with the real world. ST allows for mutability in pure context

    https://github.com/serprex/Fractaler/blob/master/Fractaler.h... fractal renderer I wrote in highschool, has mouse controls for zoom / selecting a variety of fractals

    https://github.com/serprex/bfhs/blob/master/bf.hs brainfuck interpreter which mostly executes in pure context, returning stdout with whether program is done or should be reinvoked with character input. Brainfuck tape implemented as zipper

    your program may exist in real world, but most of it doesn't care about much of the real world

  • > Given that you want write code that sees "real world" use, and is used to handle data and events from the real world.

    Real world? As opposed to what?

    Is there any benefit to answering such polemical questions as if they are not rhetorical?

    • As opposed to the abstract academic world?

      The only time I had contact with Haskell was in university and I did not see it appealing back then, nor now, nor have I ever seen a program that I use, written in it.

      So learning a bit of pure Haskell might have been beneficial for me to become a better programmer, but I still fail to see it being more than that - a academic language. Useful for didactic purposes. Less to actually ship software.

      10 replies →

  • There's a video game on steam you can buy with real dollars built in Haskell.

    I work full-time writing Haskell. Fintech stuff. No shiny research going on here.

    I've written some libraries and programs on my stream in Haskell. One is a client library for Postgres' streaming logical replication protocol. I've written a couple of games. Working on learning how to do wave function collapse.

    Believe it or not, functional programmers -- even ones writing Haskell -- often think about and deliver software for "real world," use.

  • I can't speak for others, but I never really understood the benefits of functional programming when my language pretty much allowed unbounded mutation anywhere. I would say there's a chance for impure languages to impede you in learning what functional programming is about (or at least my experience with F# and OCaml did not really help as much as it otherwise could have I think).

    Your mileage might vary, but I've heard advice from others to learn Haskell and "go off the deep-end" because of people citing similar reasons.

  • In a way, one benefit is the whole ecosystem / culture / idioms built on top. Haskellers went further in that direction than most languages (except maybe scalaz and some hardcore typescript devs).

  • Pure functional programming doesn't preclude side-effects like IO; it makes side-effects explicit rather than implicit!

    At my previous job, we used pure functional programming to ensure that custom programs only had access to certain side-effects (most importantly, not IO). This meant that it was trivial to run these custom programs in multiple environments, including various testing environments, and production.

  • In my experience, I only really learned how to write small functions after Haskell. The discipline it forces on you is a good training.

When Haskell was a hot topic around two decades ago, ML was also quite often discussed. Today ML almost only means machine learning

  • That's what I remember from my Computer Science classes 2001-2002. Standard ML was hard to learn back then for a newbie especially a Computer Science newbie.

"What fascinated me about Haskell when I was still a teenager? Who knows. I had been coding with increasing enthusiasm since I was 10 or 11 but I was no wunderkind. I certainly hadn’t attained anything like the skill or, more importantly, taste I had after just a few years in the working world. What I like about it today is that it’s quite natural to program in Haskell by building a declarative model of your domain data, writing pure functions over that data, and interacting with the real world at the program’s boundaries. That’s my favorite way to work, Haskell or not."

I adore these sentences.:)When I read it,it feels like I met myself.

"It’s quite natural to program in Haskell by building a declarative model of your domain data, writing pure functions over that data, and interacting with the real world at the program’s boundaries. That’s my favorite way to work, Haskell or not."

Are there any good resources you can share to learn this specific way of programming?

Yes, it's better to spend 15 years to learn Haskell than keep creating messy imperative programs without knowing how to improve.

Sure, lord it over the rest of us peons. We can’t all be overachievers, you know.