Haskell

Has anyone noticed that at this moment, three of the top 50 topics on hacker news are about Learning Haskell?

3 Likes

Yeah, these things seem build their own momentum. Submit a Haskell article that hits the front page, Haskell people come to read it, submit other Haskell content, and then upvote and comment on other Haskell stuff. It happens with Elixir and Erlang periodically.

The 5 years with haskell article is pretty interesting.

2 Likes

I don’t read hacker news but I was looking something to learn functional / practice programming … and I Haskell match my taste (I recommend haskellbook.com)

1 Like

reminder that there was a day when ever y post on the HN front page was about erlang :stuck_out_tongue:

1 Like

What are Haskell’s selling points?
Do any big apps or services use it?
Is there anything to love about it as there is with Ruby and Elixir?

I’ve always heard people say Haskell is ‘hard’ and without the kind of benefits or perks that we seem to see in other languages.

1 Like

Haskell is a statically typed, purely functional lazy programming language. Because of this, it is quite special.

It being pure means that it is always possible, both by humans and by the compiler, to reason about code. This makes refactoring a lot easier, and also allows the compiler to perform awesome rewriting tricks that are impossible in any other language.

It also means that you are forced to separate your functions that produce effects (IO, random number generation, etc) from your pure computing logic.

Being lazy means that working with bounded and boundless data structures in Haskell works the same way, and no CPU cycles are spent on computing things that you don’t actually need later on. It does however also mean that predicting (memory and time) efficiency is hard.

Being properly statically typed and having type-inference, Haskell can find out what types you mean in 95% of the cases. In the other five percent, you can instruct Haskell what you mean by writing a type signature. Other than most languages, Haskell allows for polymorphic return values, which lets you write very generic code!

Haskell uses type classes (which are similar to Elixir’s protocols but stronger because all data types in Haskell might receive an instance of a typeclass (implement a protocol) rather than just structs in Elixir, and while protocols dispatch on the first argument, which instance of a type classes to pick is resolved using type inference at compile time) to allow for polymorphism. Haskell has a large collection of commonly used type classes that ship with the standard library, which allow common anchor points for your new data structures.

Yes!
Other than folklore suggests, Haskell is not only used as a ‘research language’.

Haskell is used a lot in Fintech (Financial Technology) applications, because of its ability to write abstractions, work with complex numeric types, and a compiler that makes it super fast.

Haskell is also a prime target that compilers for new (domain-specific) languages are written in, because of its approach to parsing.

(See https://wiki.haskell.org/Haskell_in_industry)

Yes!

Haskell has nearly no keywords. Almost everything you see was defined as a function somewhere. This includes the possibility to define new binary operators with customizable precedence, creating new types.

Haskell’s module system allows you to fully shield your internal functions and types from the outside world, to separate your public and private APIs. It goes with this a step further than Elixir, as you can have nested function definitions that only are defined inside of another function using let or where clauses.

In Haskell, you usually spend a lot more time thinking about how you want to approach a problem, than how your approach has to look in code. When you then write down your solution, this will often only take a few lines, because of the high amount of abstractions Haskell (and its standard library) provide.

Because Haskell is all of pure, typed, lazy and functional, there are a lot of concepts that take getting used to. Haskell definitely has a steep learning curve.
What does not help in this regard, is that most of the available documentation is ‘hard documentation’ that only explains what a function does, rather than ‘soft documentation’ that explains when a function could be used, and show some simple examples.

But there are some great introductory books (I love http://learnyouahaskell.com/) and a very helpful community to get you going.

It is definitely worth it to learn Haskell. Besides Haskell itself being a wonderful language, it will teach you a lot of concepts that can be used in a much broader context, and will make you a better programmer.

This statement is false. Because of Haskell’s type system and purity, it is possible to swap wrapper implementations in your code without changing anything else.

Is an algorithm faster if it is written in an imperative style that does use in-place mutations? Not a problem? By wrapping it in an ST context, we can use mutations in the implementation, while the outcome of the function is still perfectly pure.

Want to work with the Actor Model? Not a problem!
Rather use Software Transactional Memory? Not a problem!
Have some calculations that are somewhat slow? By wrapping it with one of Haskell’s paralellization libraries’ data structures, Haskell will automatically distribute it over your cores, the network, or run it on your GPU.
More info about the concurrency libraries that exist for Haskell right now.

I think the most important benefit that Haskell has over most other languages, is its ability to write leak-free* abstractions for applications in most business domains, whose proper use can be verified at compile time.

Haskell really follows the key points that Guy Steele, in his talk Growing a Language hinted at. It already exists for 27 years but has come a long way since then. Haskell is obviously not perfect; It contains some peculiarities that are kept for historical reasons and backwards-compatibility, and with a language that is so close to math, it is always possible to debate about the proper Haskell-interpretation of some concept, which has given the rise to many pragmas that extend the language’s syntax and capability in one way or another.

However, I consider Haskell to be a language that has a lot of wonderful, useful and convenient ideas to bring to the table. In my ideal world, many more people would use or languages like Haskell, or at least know about their clever ideas. But it is definitely true that learning Haskell takes a lot of time and effort, which means that it might not be possible for everyone to learn it.

12 Likes

For Mac OS X there is Haskell For Mac - Learn Functional Programming with Haskell which has its own little IDE (but is sandboxed).

Of course Stack is free and unconstrained.

The Erik Meijer - Functional Programming Fundamentals videos are still available - they are based on Graham Hutton’s Programming in Haskell 1e.

2 Likes

And Thunks are sloooow, given the same datasets Haskell is outperformed by just about anything else compiled.

Actually it’s inference tends to only work in the more simple of cases. There is a reason you always see Haskell code with types (unlike, say, OCaml, which I’m trying to keep out of this thread ^.^), and that is when you start using some of its advanced things the compiler starts failing pretty hard at the type inference as typing those hard constructs quickly becomes NP-Complete (this is the major source of the slowness in Haskell compiling, though not the only one by far).

Actually Haskell’s type classes are closer to the Witness style of polymorphism rather than Elixir’s Protocols (which are type testing styles, inherently slower but the EVM is optimized for matching so it is actually very very fast in Elixir). A closer example to replicate Haskell’s type classes would be having something maybe like this without any of the decoration or nicities of typeclasses (I.E. this is how it is often implemented internally, but using Elixir syntax here):

# This is like a typeclass callback
def to_string(witness, value), do: witness.(value)

# This is like using a typeclass
def make_template(witness1, witness2, value1, value2), do: """
  The 1st value is:  #{witness1.(value1)}
  The 2nd value is:  #{witness2.(value2)}
  """

# This is like defining classes for the typeclass, I.E. the implementations for the various types:
def integer_to_string(i), do: String.to_integer(i) # Oh hey, Elixir already has this!
def float_to_string(f), do: String.to_float(f) # Elixir has this too!
def string_to_string(s), do: s # Whooo no-op!

# And use it like:
make_template(&integer_to_string/1, 42, &float_to_string/1, 6.28)

However, what if you have a custom type? Let’s add to the above code:

# Make its 'class' here
def mystruct_to_string(witness, %MyStruct{values: values}) do
  values
  |> Enum.map(witness)
  |> Enum.join(", ")
end

# And use it:
make_template(&mystruct_to_string(&integer_to_string/1, &1), %MyStruct{values: [1, 2, 3, 4, 5]})

Except since the program knows what types everything is ahead of time then it implicitly chooses the witness for you at the call-site, however, notice a type definition for a class in haskell that implements a typeclass:

(==) :: (Eq a) => a -> a -> Bool  

Why yes, that (Eq a) bit there is indeed the witness! You can have many witnesses in a function, just like in make_template/4 above. The compiler auto-guesses for you based on context of the caller then transparently passes it in as expected. This is also why every function that ‘uses’ a typeclass has to define it in its signature. There are cases that Haskell can do dynamic typeclass lookup without needing to thread a type through a program, however this decodes into a very slow virtual that happens after a costly type lookup to acquire the class then get the appropriate function from its vptr list.

Only super-fast when all the types are statically known through-out the program, which they thankfully are when working with money, most programs will not see that speed. ^.^

Actually OCaml (bleh, trying not to bring that in, at least I ignored it in typeclasses above) is significantly more used for compiler research everywhere from Facebook to Microsoft to many others. OCaml’s module system makes writing compilers significantly easier and faster than Haskell, and since OCaml’s compiler is entirely pluggable as of a recent (3yrs) version without needing to modify its source (which is not hard admittedly, its compiler is pleasant to read) it is being used to test a lot more styles as well. And once the ConcurrentOCaml branch is merged in (it just hit another milestone whoo!) you can even emulate the EVM’s style of pre-emptive green-threadings across few real threads with relative ease. :slight_smile:

Eh technically defp in elixir completely shields those functions from the outside, and indeed they may not even exist after compiling. Also:

def hello do
  world = fn -> "world" end # Honestly this is like a `defp` function...
  "hello #{world.()}"
end

You can define functions inside Elixir too. :slight_smile:

I really really hope people think a lot of how to approach a problem in Elixir too, I can spend hours mapping designs and such before I ever write a line of code in source… >.>

Haskell has a lot of stuff in it as well to work around limitations in its type system or to add fluff and so forth and that is just all the more to learn. Heck, look at all the extensions that are built in to just GHC alone, and most projects will require most of those to be on, and the ecosystem does not shy away from not only using them all but creating even more. The ecosystem really prefers succinctness over readability, and ‘that’ is one of the biggest things that makes it hard to learn.

/me is twitching wanting to mention OCaml some more in examples… ^.^;

3 Likes

I am not going to start the OCaml vs Haskell debate, because I do not know enough about OCaml to compare them objectively.

I would like to put some of your claims in some context, though:

Thunks are slower than strict evaluation, but I would like to note that the compiler optimizes away thunk generation in many cases, and that Haskell has ample tools to decide how and when things should be evaluated.

There are two major reasons that type signatures on top level functions exist in Haskell:

  1. To document your functions, just like we use @spec in Elixir.
  2. To make sure your mental reasoning is in line with what you have actually written down.

The only time that type inference becomes undecidable, is when language pragmas that allow certain forms of dependent typing to be specified. As far as I know, this is a mathematical certainty and not something that is a limitation of Haskell.

Using a vtable is of course slower than direct execution, but I don’t think that ‘very slow virtual’ + ‘costly type lookup’ does the system justice. Vtables are avoided when possible, and I do not believe there are any other ways (than having a vtable) of doing these kind of things.

Yes! Me too!! :smiley:
I would totally say that Elixir is better in this regard than many other languages as well.

I totally agree, and this is one of Haskell’s main drawbacks.

Thank you for your reply! :slight_smile: Haskell is by no means perfect, just like all other languages. However, I do feel that it really is worth learning because it is so different, and there are many areas where I would prefer to solve problems using Haskell than any other language.

2 Likes

As usual there are people on both sides of the fence:
Quora: Which of Haskell and OCaml is more practical?

My issue was that F# was my first exposure to OCaml and as such it didn’t come across as “pure-ly functional” as Haskell (just conveying my perception at the time - Haskell was way more “shiny”). For the time being Haskell is a nice place to visit because it forces me “think about problems in a different way”.
But I think it’s important that Haskell isn’t the first “place you visit”, I think it’s helpful to have had some time with something Lisp-y/Scheme-y (and compact) like Racket first (or for all I know OCaml fits the bill as well).

In hindsight I was actually quite surprised to find that “FP with Erlang” is actually a far more accessible way to start wrapping one’s head around FP - the “lack of purity” really doesn’t taint the experience (though compile-time type checking would still be nice).

Actually there is one thing that may become an issue - I think within the Elixir user community (not talking about the core members) there needs to be more of a push towards the Erlang-y FP-with-pattern-matching and sequential-vs-concurrent coding style.

I could see how, coming from an imperative language, it might be convenient to simply adopt Elixir, stick to largely sequential code, just modifying the imperative habits enough to get around immutability.

The constraints in Erlang are such that you feel pushed towards FP and concurrency - I didn’t experience that push to the same degree in “pure” Elixir.

PS: This digression relates to the fact that I initially became interested in Haskell because I wanted to become more familiar with Functional Programming. Learning Haskell may not necessarily be the smoothest approach to discovering FP.

4 Likes

From my experience
It is easier to learn functional programming form language which pure functional.

From haskell book

If you are new to programming entirely, Haskell is a great first lan-
guage. Haskell is a general purpose, functional programming1 lan-
guage. It’s applicable virtually anywhere one would use a program
to solve a problem, save for some specific embedded applications. If
you could write software to solve a problem, you could probably use
Haskell.
If you are already a programmer, you may be looking to enrich
your skills by learning Haskell for a variety of reasons — from love
of pure functional programming itself to wanting to write functional
Scala code to finding a bridge to PureScript or Idris. Languages
such as Java are gradually adopting functional concepts, but most
were not designed to be functional languages. Because Haskell is a
pure functional language, it is a fertile environment for mastering
functional programming. That way of thinking and problem solving
is useful, no matter what other languages you might know or learn.
We’ve heard from readers who are finding this book useful to their
work in diverse languages such as Scala, F#, Frege, Swift, PureScript,
Idris, and Elm.

1 Like

True, which is a good thing (OCaml uses val as the keyword to predefine the type of something, or you can define it with the thing), however in a lot of cases it is utterly necessary. A common pattern in OCaml is to write your code, then ask the compiler to dump the types of everything and you get a nice mli file describing it in detail, then you can alter to to further hide things from other modules or make things opaque or whatever. :slight_smile:

It will happen eventually, it is just that how ‘often’ it happens in Haskell is substantial…

Doing direct witness lookup on location is always faster, and by knowing the call path through the compiler can optimize far better as well. You can use witness in Haskell as it is too, just no one really does until the limitations of typeclasses appear (specifically in 2 cases, wanting to perform a different function that a typeclass default, or wanting to speed up program compilation, one of the best ways to do that is to stop using typeclasses and inline witnesses instead).

You really really should learn OCaml. :slight_smile:

It’s language may feel a bit crufty but there is ReasonML if you want a ‘newer’ syntax (blegh, too javascript looking to me) but every-single-tiny-design-decision in it was for a purpose, and every single one is for speed, and every single one is for clarity.

Just ignore the occasional exceptions in the stdlib (there are newer alternative calls for many of those that return options instead), and don’t touch magic, literally, there is a call in OCaml called Obj.magic, it lets you break the type system, with all the power that entails. OCaml is a language to ‘Get Work Done’, like Erlang, and like Erlang it has methods of doing anything even if it would be otherwise impossible in other languages like Haskell, but nowadays you should never need such magical power considering the module system, GADT’s, and so forth let you do it all anyway. ^.^

F# kind of has 2 worlds, one is OCaml, with a lot of missing power (no first class modules and such, that loses a ton of the power there), the other is the .NET world, and the .NET world is entirely non-functional. F# is really just a better syntax on .NET. You can run much OCaml code straight on it, but you’ve gotta eventually touch .NET stuff, so it is significantly less pure than OCaml or Haskell.

Yeah I agree there, Erlang has a better ‘feel’ than Elixir in terms of concurrency and FP, but the Macro’s keep me here. ^.^

2 Likes

Don’t get me wrong, I like Haskell Programming from First Principles but there is a reason it weights in at 31 chapters/1189 pages (Sept 2016).

  1. Functional Programming
  2. Haskell’s Type System
  3. Lazy Evaluation

are a lot to take on all at once. These days you can flatten the learning curve a bit if your start with Elm because there you don’t have to deal with lazy evaluation or the type system. Then when Elm gets a little constraining you could transition to PureScript so that you can take advantage of the type system - while you still don’t have to deal with lazy evaluation. Lazy evaluation is then the only remaining hurdle towards Haskell.

But in the end Haskell still feel’s like something that is more or less an “end-in-itself” rather that something** “practical” (e.g. Erlang/Elixir).
**(Though Haskell still is an interesting “something”).

I think most people learn Haskell for the derivative benefits (and possibly bragging rights :icon_biggrin: ).

6 Likes

From what I know, only Erlang/Elixir and Haskell consider it acceptable and common to forcibly kill one thread from another. In Elixir it’s OK because each thread has it’s own memory and shares nothing. Haskell has shared-memory threading, but the type system makes it safe to just kill threads at arbitrary points in their execution. It’s interesting that only (again, as far as I know) these two languages have this property, and yet they are quite different from each other.

1 Like

I am not got so far but I think Haskell has STM as concurrency model (https://en.wikipedia.org/wiki/Software_transactional_memory). But I prefer to separate available currency models from language itself (language can support many currency models ).

I would go even deeper and say that if Haskell flavor of functional programming is nice and dandy, and really good for CS research, it is the wrong way to go if your goal is to eliminate errors and write good cood that can evolve and be debugged easily.

Ocaml and erlang are far more interesting here (Rust could be discussed but it is aiming at a niche that most of the academic CS community stopped dealing with more or less, so a bit hard to talk about other approach).

I would quite like to see an Eiffel successor here…

1 Like

STM is just one of many Haskell libraries.

Are you talking about the fact that Haskell is a pure functional language here?

I agree with you that it is possible to prototype faster in an impure language (and even faster in an unityped dynamic language), but I disagree that pure functional programming is ‘the wrong way to go if your goal is to eliminate errors and write good code’ and would rather say the opposite.

In a pure language, you are forced to push effects pertaining the outside world to the boundaries of your application, which is also what most software design methodologies advise.

Also, writing in a pure language gives you guarantees of safety and predictability that you do not have in an impure language. Of course Haskell contains some escape hatches that you can use to e.g. debug easier, but it is also possible to make sure that your application and all of its dependencies do not use them in the production version of your code.

OCaml, Erlang/Elixir and Rust are all impure, and therefore cannot give you the same guarantees that Haskell can. Of course, they have other advantages. I love Rust’s approach to static lifetimes, for instance. (And also, its typeclass hierarchy, unburdened by historical decisions, looks quite a bit cleaner than Haskell’s).

@mkunikow @Buttons840 here is some more information about multiple ways to do concurrency in Haskell.

2 Likes

i am talking of static typing and formal proof as a solution. It is solving the wrong problem imho, because the problems you talk about here are not the one that create most of the safety problems in the systems we build. It is a question of goals.

The goal of Haskell is to solve “code” problems, not the problems that are encountered when the software is in use. It solves it well, even if imho in an over engineered way, but it creates the opposite effect at a higher level. At best, the positive effects for the safety of the system in which it is used are second or third level effect. Which is good, but i think that there is a need for something else here. Erlang is far more aimed at solving the safety problem, even if it is not totally aiming at it either.

3 Likes

…which bugs me the most when writing OCaml code :slight_smile: the disintegration between ml and mli files on development is painful. I always wanted my mli files to be used for linting my code (i.e. when the impl doesn’t match the type; that’s what interfaces are for, right?), however I don’t see any editor support for that. The difference in syntax when adding type annotations is also a bit annoying. Kinda sad that type-driven dev doesn’t seem to be the idiomatic way of writing in OCaml.

One mindblowing experience I had with Haskell compiler is when it suggested ways to improve the readability of my program. I was writing several if blocks with return () expressions and the compiler introduces me to when and unless, letting me write something like:

when (null args) $
  do printHelp
     error "No command given."

let command = head args

unless (isValidCommand command) $
  do printHelp
     error $ "Invalid command: " ++ command

For me it was a major win for readability :smiley:

My pain point as a beginner in Haskell is dealing with string types. It is always unclear when to use which.

2 Likes