dogweather

dogweather

I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something…

Haskell reminds me of Java, and even Ruby: it’s got a couple awesome features — strong static inferential typing plus purity. (Ruby has dynamic behavior and “developer friendliness”. Java has “strong” static typing.)

But you can’t just have an excellent type/safety system at the application developer level. To make it work, it’s turtles all the way down. Lens only exists because the native type system makes that (normal) kind of work too hard. And so there’s a seemingly never ending rabbit hole of complexity to learn, all in service of the original goal. The complexity also arises from the need to master implementation details of the Haskell system.

In Ruby, the cost of the “awesome experience” is the need to become an expert test writer, and essentially write every app twice.

In Java, it’s the cost of more and more additions and complex syntax (generics, etc.) to support the original strongly typed goal.

Contrast this with languages which have decided to go another, less pure way: Elixir and Elm.

Learning Elixir, you’ll be blown away by how quickly you can master the whole system and the libraries. I was surprised to realize I was done learning, and I could just spend my time creating business value.

In Elm, they drew a line in the sand at typeclasses — they sealed up the recursively descending rabbit hole of complexity right there.

I do really really like programming in Haskell — at the experienced newbie level. I enjoy being able to refactor and the enforcement of case checking exhaustion (with the correct options enabled.) But would I switch to another simpler ecosystem which provides these same benefits? Yeah.

Showing Posts 1 to 10

Qqwy

Qqwy

TypeCheck Core Team

Thank you for your post!

Having programmed in all these languages myself, I find myself disagreeing with you on a couple of points:

First, let me start off with mentioning that every language has its warts. Being a polyglot programmer is, in that regard, both a blessing and a curse: I miss all the features the other languages have, while being slightly annoyed at times at the odd implementation-details or things that exist because ‘it seemed like a good idea at a time’ (and are kept for backwards-compatibility).

I love all the languages (truth be told, some more than others, but it is definitely only a partial order):

  1. About the complexity of the abstractions in Haskell. Yes, you are right, Haskell has mechanisms that can be very difficult to understand. However, I have the strong feeling that this is caused more by a lack of good documentation and tutorials than because ‘abstraction is bad’. Be aware that complexity is not the same as difficulty; often, Haskell turns out to be harder for developers whose mind already contains the leaky abstractions that are present in other languages: For instance, working with recursion is only more difficult than working with loops if you already know loops.

And I have to say: In the last four years, the amount of beginner and beginner-intermediate material about Haskell (and pure functional programming in general) has skyrocketed; probably in part caused by more and more people from other communities looking at the functional paradigm for inspiration.

Haskell’s type system does not exist ‘because it is nice’ (although, of course, tail recursion is its own reward :stuck_out_tongue_winking_eye:), but because it allows you to write code that is as decoupled as can be: For instance, you can write algorithms that work on certain kinds of containers (like: ‘all things that you can add a new element to’) without knowing:

a) what type of actual container it is, nor
b) what things are stored inside.

And because this not only works on containers, but because computational statements are also a ‘container type’, it is possible to leverage the compiler to do insane things, like running a piece of code no longer sequential, but as parallelized as is possible.

That said, Haskell definitely has some rough edges. Most of them are caused by its age (because now we know more than we knew back then), resulting in:

  1. A Prelude with some non-total functions, which basically go against how to program properly in the language.
  2. Some things have very counter-intuitive names, especially when coming from a different programming language (class is for instance used to create a Haskell typeclass. If it had either been called interface or protocol, then a lot more people would have understood it the first time).

Ok, besides my first and maybe most important point, that it is mostly a difficulty to get started for new programmers because of a lack of good tutorials (which seems to be changing), let’s attempt to answer some of your points directly:

Lenses exist because they allow you to decouple what you want to read/change from how it is stored, meaning that you do not have to hard-code your accessors (which in many other languages, you are forced to). In Elixir, we basically have lenses too: It’s called the Access behaviour.

To be honest, I have never found the need to be mindful about the implementation details of Haskell while writing my code, (other than a general sense that Haskell is non-strict in its evaluation, which is not really an implementation detail). The only times you’d dive into this, is when you want to get that last 20% of speed or memory-optimization, which you almost never need. (The root of all evil, etc.)

Ruby is a language that is easy, but definitely not simple: The objects (the proverbial ground) you are holding are shifting under your feet all the time. It is very hard to reason about a Ruby-based system for both users and e.g. computer optimizers. This indeed means a lot of tests need to be written.

It is also the reason that I really like Ruby for writing prototypes, or short command-line-interface tools. But for the same reason, I dislike working with long-running Ruby (on Rails or otherwise) applications: It takes a lot of discipline to keep your code clean in such an unrestricted programming context.

As for Java: Java definitely is somwhere lower on my poset of liked programming languages; mostly because Java’s approach to Growing a Language, and because of its enforced ‘static’ typing.
As one of my colleagues often says: Java is not so much a programming language, as it is a platform to build on top of, because it has such a large ecosystem.

Yes, new Java versions definitely make an attempt at making it a better language :slight_smile: .

Let it be known that Elm is a fully pure language. (Especially since Elm 0.19, since it is now impossible to write your own Native modules)
I still see Elm as ‘Haskell-lite’, and that is meant in the most affectionate way. Because it did not have to care about being backwards-compatible with Haskell’s existing things, it was able to rename a lot of things, and make some different design decisions.

This point is wrong for two reasons:

  1. It has never been said that Elm will not ever support typeclasses. It is definitely true that they are not there right now, but depending on how Elm as a language develops, they might still be added. Evan Czaplicki is more concerned with keeping elm simple (frequently removing languages from the language, which is still possible in its current pre- 0.1 phase). It might very well turn out that typeclasses might greatly simplify some things. But there is definitely a lot of other stuff to implement first. (and building the compiler logic that allows typeclasses to exist, well, that definitely is hard :sweat_smile:).
  2. Elixir has typeclasses. They are called Protocols.

With all that having said, though, currently, I am able to work professionally with Elixir (and Elm), whereas Haskell hasn’t found a place in my professional workspace yet.
But this is probably because we are mostly a web-development shop.
The reason then that we use Elixir, is because it is a very pragmatic language (as is Erlang; which, as Joe Armstrong has repeatedly said, only ended up as a functional language ‘by accident’ because it was the most practical solution for the problem that Ericsson had at the time).

In the end, all programming languages are a tool. Programming in Haskell (or in any language) is not the goal, but a means to build something truly marvellous.

25
Post #1
aseigo

aseigo

“Naaah,” I respond, digging into my own box of smelly opinions … :wink:

Haskell has great ideas but years and years of it being out there in the wild shows that it is more complex than is useful for nearly all developers working nearly all problems. This is not necessarily a failure, it is just a choice, and I do not think any amount of documentation will change the impact of those design decisions. Haskell is about the language, not the application of it. The designers of the language seem to understand this, fwiw, and are similarly ok with it.

I agree with the OP that Elixir (and, for that matter, for its day, Erlang) strikes a far more pragmatic road to walk down as a developer. That Elixir emerged with a few lighthouse frameworks (Phoenix, Nerves) from rather early on shows the application-not-the-language emphasis. This is also not entirely a good thing, but it does make it usually more practical to use.

Which is probably the biggest thing Haskell will end up giving to the world. Microsoft language designers pay a lot of attention to Haskell, it seems, for instance.

Probably just because Haskell is a poorer fit most of the time, and the rest of the time languages already in use for the other cases are acceptably not-bad-at-that-problem and so get selected even then.

I see a lot of non-web companies around here that are using “non-traditional” languages, but very few are using Haskell .. even though the languages they choose are often either FP or draw heavily from FP ideas. shrug

LOL, yeah, I know that feeling .. :slight_smile:

easco

easco

FWIW, this also sounds an awful lot like Ecto Changesets too. I realize that’s a library-level concept, not a core language feature, but the parallel came to mind.

jeremyjh

jeremyjh

I’ve used both languages extensively for five years or so now. My personal take is that development in Haskell is more pleasant when all else is equal, but frequently all else is not equal, and I think most development teams would be better served by Elixir just from the standpoint of hiring and training developers. For personal projects I usually reach for Haskell first but not always.

The thing about lens is, it is awesome and not really that hard to learn, but its documentation is terrifying. I saw this comment the other day that explains it so much better. It can feel overwhelming until you’ve developed the intuitions about how to use the various functions, but you only need a few. Once you’ve mastered it though you have a tool that has less syntactic overhead compared to put_in/get_in, plus rigorous type-safety.

dogweather

dogweather OP

Yep! I’ve been using Haskell well over a year in production but still can’t understand most r/haskell posts.

Haskell is the programming language equivalent of the fable about the king with the mouse problem who solved it by getting cats. But then he had a cat problem, which he solved by getting dogs. Etc., etc.

OvermindDL1

OvermindDL1

Just as a note, have you tried OCaml? It has Haskell’s power but is more Python’y in syntax (even 95% of type declarations are ‘optional’ but still inferred)? The whole ML family is also much older than Haskell, just OCaml is the latest incarnation of it directly and even then it’s 20 years old. ^.^

Otherwise yeah, what other people said here too.

Plus Kotlin is basically eating Java, it’s already done so in the android world…

Also, Elm does have typeclasses, it’s number type is one such incarnation of it, they are just not user-definable typeclasses, but it does have typeclasses (with quite a surprising number of bugs popping up around it’s incarnation of it).

Eh, protocols are similar conceptually, but they are not typeclasses, they require a value to dispatch on so they are dispatchers, not a typeclass thing, which can work purely on types without values.

Lenses are a library in almost every language that has them. Also Ecto Changesets are nothing like Lenses. A ‘lens’ in the Elixir world would be something like:

defmodule Lens do
  defstruct [get: nil, set: nil]

  def compose(%Lens{get: in_get, set: in_set}, %Lens{get: out_get, set: out_set}) do
    %Lens{
      get: &in_get.(out_get.(&1)),
      set: &out_set.(&1, in_set.(out_get.(&1), &2)),
    }
  end
end

Now let’s have an object setup like:

person = %{name: "some_name", measurements: %{inseam: 42.8}}

We want some lenses to access both the inseam and the measurement (I’ll ignore name for now):

lens_person_measurements = %Lens{
  get: & &1.measurements,
  set: & %{&1 | measurements: &2},
}

lens_measurements_inseam = %Lens{
  get: & &1.inseam,
  set: & %{&1 | inseam: &2},
}

Now that we have the lenses setup to access each of those values we then can combine them to create a thing to access the entire depth in a single option:

lens_person_measurements_inseam = Lens.compose(lens_person_measurements, lens_measurements_inseam)

Which we can now use like:

iex(11)> lens_person_measurements_inseam.get.(person)
42.8
iex(12)> new_person = lens_person_measurements_inseam.set.(person, 6.28)
%{measurements: %{inseam: 6.28}, name: "some_name"}
iex(13)> lens_person_measurements_inseam.get.(new_person)
6.28

Now with a statically typed language you can build the Lens’s automatically, like what Elm does with ports, or what OCaml does with PPX’s, but with a dynamically typed language you can’t, however Elixir’s Access protocol can still let you specify them manually and more shortly, for example for the same person object:

iex(14)> access_person_measurements_inseam = [:measurements, :inseam]
[:measurements, :inseam]
iex(15)> get_in(person, access_person_measurements_inseam)
42.8
iex(16)> new_person = put_in(person, access_person_measurements_inseam, 6.28)
%{measurements: %{inseam: 6.28}, name: "some_name"}
iex(17)> get_in(new_person, access_person_measurements_inseam)               
6.28

Now admittedly a Lens can do a lot more than just ‘simple lookups’, it can do arbitrary set/get (which you can compose to ‘update’ as well), whether a map, list, tuple, a database call, or any combinations there-of, the ‘user’ of the lens doesn’t know or care what it is actually doing, and it remains entirely type-safe. :slight_smile:

EDIT: As an aside I had a beautiful to use Lens library in Elixir before the most recent OTP version killed off tuple calls at my great annoyance… And no, adding a compile option is not sufficient as most people just wouldn’t do that… >.<

Thus you are left with the ugly mess of blah.get/set.(..) calls everywhere… >.<

dogweather

dogweather OP

To me, it looks like basic lenses simply bring the standard Elixir map/struct accessors to Haskell. So it’s hard for me to get so super-excited about them.

OvermindDL1

OvermindDL1

Two things:

First, Haskell already has record/dict accessors, so that’s meh.

And Second, you missed a big thing that I said:

Unlike Elixir’s Access, which is built only for structs/maps/lists/tuples, a proper Lens library can handle any kind of get/set operation, even databases, environment, anything, all with the same interface and API without needing to know anything about what you are accessing.

But as you can see, you can still write Lens’s in Elixir, just a bit more verbose now without tuple calls. ^.^;

Qqwy

Qqwy

TypeCheck Core Team

The problem with number is related that Elm pretends to have arbitrary-size integers but secretly still uses JS’s built-in numeric IEEE-like floats underneath. I would not call number a typeclass because it is not parametrized (it is still of kind * and not * -> * or higher): A type cannot be both number as well as something else.

But yeah, I am unsure what to call number if not typeclasses. My point was, in any case, that it was not possible to define your own typeclasses (nor instantiate them for your custom types), which mean that you need to maintain a large amount of boilerplate code manually.And yes, typeclasses are not a first-class feature, which means that you should not over-use them even in languages that have them, but they are still really expressive.

Haskell’s protocols also need to haul around a vtable if you call a typeclass-function in a way that would mean that more than one instance would reach at the same time. Typeclasses are only able to work on ‘just types’ and be compiled away in a compiled language, and then only sometimes. The same is true for Rusts traits.

That said, no, the comparison is not 1:1. But that was also not my point:

My point, comparing Elixir Protocols to Haskell Typeclasses, is that having a feature that somewhat looks like Typeclasses does not make your language devolve into a ‘recursively descending rabbit hole of complexity’ right away, but that it is rather a very expressive and widely-used and accepted feature of the language.

When comparing Haskell with OCaml I do want to point out for people that do not know, that OCaml is not pure (and AFAIK has no way to enforce purity), which might matter to you.

gon782

gon782

In what sense?

As for your original post, how did you learn? Did your company provide internal training, books, etc.? I’m curious mostly because you say you’ve now used it for a year in production but you don’t seem at all comfortable with it and you seem to have almost no concrete points against it. I would expect specifics from someone who’s sat down and actually used it.

Where Next? Top

Trending in Discussions Top

AstonJ
As the title says, please share what you’ve been up to with Elixir. Whether that’s been learning it, looking into it, making stuff with i...
2977 92995 915
New
caslu
I want to open this thread for you all to discuss and help those who really like Ash but are still hesitant to use it in a real project. ...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
GES233
I’m posting this in response to Jose’s recent tweet (Cr. link) : People are sleeping on Elixir for a coding harness: Hot-code swappi...
New
marciol
It would be helpful to have a list of companies worldwide that hire engineers without prior experience in Elixir. Often, it can be quite ...
New
durvia
Anyone running long-lived stateful processes on BEAM? We’re building an AI agent runtime and would love to compare notes. We’re a small ...
New
AstonJ
This might be a bit disturbing for some but it’s happening - computers running on living human neurons. They’ve made them smart enough t...
New

Other Trending Topics Top

marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
webofbits
Aludel - LLM Evaluation Workbench Aludel is an embeddable Phoenix LiveView dashboard for evaluating and comparing LLM prompts across mult...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews