Hacker News new | past | comments | ask | show | jobs | submit login
Elm 0.19 Broke Us (dev.to)
276 points by maxhallinan on Aug 25, 2018 | hide | past | favorite | 226 comments



I think there’s a very weird mismatch between the way the core language people are pushing for the adoption of the language, and then at the same time they will turn around and say “we’ll, it’s not even 1.0, yet, so what’d you expect”.

The way Elm is developed is fit for a language that has little to no users, and can take going so long between updates, that remove escape hatches needed to fill out the immaturity of the language.

It really saddens me to see it go like this, because I once had high hopes for Elm. Nowadays I see it purely as a stepping-stone into more advanced languages like PureScript and Haskell. Elm is there to hold your hand, but it seems it has no intention of ever taking you further than the basics.


What you term the "immaturity of the language" is imo a well thought put focus on simplicity. It was never Elm's intention to reach the complexity of Haskell or indeed PureScript. I think that was a sound decision.


I think you are right, but the way decisions are communicated in Elm are frustrating. There are lots of decisions like this, which in the end are just decisions, but can be frustrating if you are expecting a more complex language.

Even without talking about type classes, the lack of applicative is weird. I know: nobody understands what applicative is for, but if you have map and you are currying parameters, you're going to run into the issue ;-) Instead, you've got map, map2, map3, map4, map5 (which also, frustratingly, don't implement applicative). There are lots of validation scenarios that are just way more complex to do in Elm than they need to be.

But to be honest, it's fine because the language is not supposed to give you everything that you might want. It's geared for people who want a subset that's easy to learn. If I'm going to write code for myself in this space (functional language that compiles down to JS), I'm going to pick Purescript because it has everything that you're ever going to want. But if I'm choosing a language for my team that doesn't know functional programming and (to be blunt) doesn't really care to learn: Elm is a much better fit. They can learn just enough to get their work done.

But I just wish it was stated in those terms clearly. The premise of the article has been played out time and time again in the Elm community: "I thought this was a fully featured functional language, but when I tried to do this completely reasonable thing I got my hand slapped".

At every turn the response to, "Why can't I do X" is "Because you don't need to do X. It's much simpler to do without X" -- which usually isn't true. It's much simpler to learn something without X, but X is there to make your life much easier as long as you can deal with the complexity of learning it. It's the old "ease of use" vs "ease of learning" issue.


On the other hand, people need to learn to take no for an answer and move on.

Instead, egos get involved and a new essay of the week appears on how Elm is doomed because it doesn't have <pet feature>. That nobody can take Elm seriously if it doesn't have type-classes, synchronous FFI, derivable JSON codecs, component state, or whatever the pyre was built on that day.

If you didn't know better, you'd think they were disgruntled paying customers.

Elm's simplicity, lack of abstraction, and resulting boilerplate have more to offer than helping beginners learn. Nothing demonstrates this better than coming back to an old Elm application and immediately making meaningful changes to the code despite a long hiatus from using Elm at all.

There are a lot of languages that continually add features in this space like PureScript, ReasonML, and even TypeScript.

Why not see where Elm ends up with its aggressive devotion to a minimal API instead of trying to turn it into a language we already have?

Some of the best aspects of Elm come from its restrictive nature. Like how every Elm application uses the same "TEA" abstraction which is a breath of fresh air for anyone who never felt particularly empowered by the paralyzing abundance of options in the JS ecosystem nor the task of webpack-plumbing them together.


Nobody can take Elm seriously because having lines of code in Elm is a liability at this point.

If Evan hadn't wanted this to happen, he shouldn't have positioned it as the "beginner-friendly" language and then pulled the petty dictator routine out as soon as people were locked into his ecosystem.

> Nothing demonstrates this better than coming back to an old Elm application and immediately making meaningful changes to the code despite a long hiatus from using Elm at all.

Or... not having it compile because the BDFL (or, realistically, DFL) has decided to remove half the features it needed to compile.


That's not the takeaway that you should have - simplicity doesn't matter if minor language versions make breaks.


[flagged]


While I agree about it being ok to break things at this stage, some of the words in your reply were a bit confusing - what do you mean a "private unsupported API" in an open source project? Kind of supports the notion that this is a private group project and others can use it, but are not welcome to participate.

Also, if calling this a minor release is idiotic, why was the version number then a minor increase (at least according to commonly accepted understanding, i.e. semver) and not a 0.20 or whatever else, especially if there are breaking API changes.


GP probably meant something like "an internal API that's exposed in case you need it but gives no guarantees about stability because it's still evolving"


> Kind of supports the notion that this is a private group project and others can use it, but are not welcome to participate.

Native's existence was an implementation detail. no one using it didn't know it was likely to go away if they read anything about it from core. it was undocumented, but where you could find any documentation it said very clearly not to use it. people used it anyway. I was one. but I knew going in that it would likely disappear.

> why was the version number then a minor increase (at least according to commonly accepted understanding, i.e. semver) and not a 0.20 or whatever else, especially if there are breaking API changes.

https://semver.org/#spec-item-4

you not understanding semver doesn't make elm bad


Elm doesn't seem to use semver (which requires that "A normal version number MUST take the form X.Y.Z")


Clearly the internal API existed because Evan et al. decided they needed to use it. So why did they decide it a feature that was so obviously necessary was too complicated for the hoi polloi?


I asked upthread about native versus ports and somebody was kind enough to mention ports were async only.

But I'm still curious about exactly what people were using native for and why at least some users don't feel they can rewrite easily - care to share what you used and why, and what your thoughts are about replacing it?

(the discussion around this has, so far as I've been able to see, devoid of concrete examples, and now the question's nerd sniped me :)


In semver versions before 1.0 are prerelease versions.


No, "immaturity of the language" refers to Evan's tendency to make breaking changes for absolutely no good reason.

Use custom operators? Fuck you. Now go rewrite all the libraries you contributed to the community.


> remove escape hatches needed to fill out the immaturity of the language

I think you mean "immaturity of the platform".

Also, native modules weren't the only "escape hatch". An Elm application can communicate with JS through ports.


Complicated interop with JS also broke Darts back.


Politics broke Dart's back.


Sure, the bad JS interop had to come from somewhere.


What was the capability of native modules which enabled the dev.to essayist to use them, where ports would have been unreasonable ("there is no other reasonable way to do what I need")?


Check out this comment for an explanation of why Ports cannot possibly cover all use cases: https://www.reddit.com/r/elm/comments/99bzf8/elm_or_reasonml...


hm i'm not sure why ports would not work in that use case (the keycloak one, right?). the port would be how you communicate between js and elm, so it looks like inside the js callbacks is where you would `send` that data through the port to elm


Agreed, it could work with Keycloak, or other 3rd party libraries, depending on the amount of collaboration between JS and Elm. My point was that some things that are simple when you can pass around promises suddenly get very complicated when all you have is ports and messages.


I came across an example which makes using ports sound a bit tedious (but suggests an alternative.) It's at the start of this talk:

https://www.youtube.com/watch?v=tyFe9Pw6TVE


excellent video, thanks. custom elements are a very interesting alternative


Ports are an asynchronous interop boundary. You basically subscribe to a port from Javascript and attach a callback to handle data as it passes from Elm -> JS. Javascript errors then are relegated outside of Elm or they fail at the boundary (like serialization errors when passing data from JS to an Elm port). They're kept outside of Elm.

Native modules, on the other hand, let you create Elm functions that synchronously call Javascript. That means that what look like regular Elm functions can now synchronously blow up at callsite inside Elm.

The first native modules I saw in the wild as a beginner were people making shortcut functions for things like `Math.random()` instead of the proper way of threading a random seed into the entry point and keeping it in the model which has various benefits. Same with quick access to "Date.now()".

From this perspective, you can see why native module access was removed, and especially why you needed permission to publish a native module Elm's package repo. Native modules are tempting because they are easy, but widespread use eliminates one of the major upsides of Elm: no runtime errors inside Elm, or rather the ability to boundary them.

However, some forms of JS interop are hard or impossible without synchronous FFI, 1:1 JS library wrapping being the most obvious example. Having to use asynchronous ports for that is like wrapping a Javascript library's sync functions with an EventEmitter. You emit an event and then listen for the resulting event. Maybe you even decide to pass in a messageId echo for request/response reconciliation (I've seen people build this abstraction around Elm's ports!).

On one hand, a lot of people have the idea that because they are using Elm, they shouldn't have to write any Javascript at all. After all, this is what many compile-to-JS languages give you.

And on the other hand, no more synchronous FFI means that some Elm apps that exist today cannot move ahead, and some people think the approach is too heavy-handed or they disagree with the direction.

In my experience, Elm is best seen as a way to build a functional core that you wire up to an external world of Javascript. For example, a game simulation that emits game states over a port, and you write Javascript to render those updates with a JS library like Pixi.js, resisting the temptation to wrap Pixi.js in Elm and calling it from Elm.


> you needed permission to publish a native module Elm's package repo.

See, this, to me, sounds quite reasonable.

A feature is potentially dangerous to the rest of the ecosystem if encountered unexpectedly: therefore, we put some extra friction on sharing code which uses those features, and made it an obvious and opt-in process if you wished to take that potential danger into your project. Great!

Given that... why on earth would it be necessary to remove the feature from the language entirely?


You can find some explanations by the language author here:

https://discourse.elm-lang.org/t/native-code-in-0-19/826


At this point it's barely a stepping stone. I'd recommend just diving in to Haskell or the like head-first so you learn the tools you'll actually use.

When you update this quickly (and for no reason), the learning resources go out of date too.


I've heard of a few people discontent with Elm for the reasons mentioned in the article make a very successful jump to ClojureScript and being much happier about it.

It offers an MVU framework: re-frame. There are others too. Its community is very friendly and helpful, it loves to discuss the language itself. No one seems to think of themselves as more knowing. It has never ever broken backward compatibility. It's a core tenet of the language to never break anyone ever. Finally, it has very good interop with JavaScript. And its getting better and better every release.

It's also ran by a BDFL, actually more like two. Rich Hickey as the BDFL of the standard language design, and David Nolen as current implementer and main maintainer of ClojureScript. But they're loved and admired. And if you disagree with their direction, you're free to add your own features as a macro, that's why they made the language a Lisp.

The language design prioritizes pragmatism. All discussion starts with: What exact problem are you facing for which there are no simple way to solve. And working backwards from real problems, solutions are added and new constructs are built.

The only thing is, it comes from a different school of thought opposite to strong static types. But it shares a lot in common, emphasis on pure functional code, immutability of variable and values. No OOP, everything is a function with support for higher order functions, closures and partial application. And the same focus on interactive live development.

If you don't mind losing the strong static types, I highly recommend giving it a try. You'll only miss the nice Elm error messages.


Good suggestion!

Just to clarify one point, though, Clojure/Script does support OOP, with protocols/records/types, it's just not the first thing we suggest people reach for in the language. As a community, we're much more likely to use boring maps/vectors/functions first, and switch to OOP when we really need type-based polymorphism, or extreme performance.

One consequence of not forcing everything to be OOP (like, e.g., Java) is an honest evaluation of where it makes sense, and where it adds nothing but complexity.


Well, I don't want to get into a semantics debate, but I can't help myself :p. And I'm pretty sure Rich Hickey himself would disagree with you about Clojure/Script having OOP.

Protocols, records and types are not instances of OOP in my book. Polymorphism and abstract data types are concepts orthogonal to OOP and FP. The fact Clojure/Script has support for polymorphism and ADT does not mean it supports OOP.

If you want to claim it supports OOP, I'd say it does in the form of interop. Genclass, proxy, or defining classes in Java or objects in JavaScript and using them from Clojure/Script.

But, what is OOP exactly? We can debate this. I like to use Wikipedia's definition that it is languages where there is a construct, called an object, which groups together data fields and methods which can access and modify the data fields within.

Clojure/Script has no such construct. But one can define them in Java or JavaScript, and use them from Clojure/Script through interop.


Records and protocols meet your definition of OOP exactly.


Well, like I said, we're in the realm of a semantic debate, and I'm definitly being pedantic.

But, as I see it, records and types define only data. The former as an immutable map with pre-defined keys, the latter as a fixed set of data fields. Thus neither are an object.

Protocols define only function signatures, without data. More like interfaces. Thus it too is not an object.

Why am I being pedantic? Because data and functions are never coupled in this case. But in traditional OOP, an object groups data and methods, and are thus coupled together tightly.


https://en.wikipedia.org/wiki/The_Art_of_the_Metaobject_Prot...

"In his 1997 talk at OOPSLA, Alan Kay called it "the best book anybody's written in ten years", and contended that it contained "some of the most profound insights, and the most practical insights about OOP", but was dismayed that it was written in a highly Lisp-centric and CLOS-specific fashion, calling it "a hard book for most people to read; if you don't know the Lisp culture, it's very hard to read"."


But a record defines implementations of the protocol methods. Would you say Traits in rust aren't an OO construct?

The data and methods are coupled in the record definition. The methods depend on having the records fields in scope.


> Would you say Traits in rust aren't an OO construct?

I'd say traits in Rust, typeclasses in Haskell and Scala imlicit views are the equivalent to Clojure/Script protocols. And similarly, those constructs by themself aren't OOP. I think a simple proof is that if they were, then all OOP languages would have them. But because there are OOP languages (the most popular ones at that) which don't have them, then it follows this construct is not fundamental to OOP.

> But a record defines implementations of the protocol methods.

It's better to think of it as a protocol is extended to a type. It is not the record which defines the implementation. The methods don't belong to the record, they are independently defined, they can come from a dependency, they can even be shared with other types and other protocols. This is contrary to objects which define their own methods.

> The data and methods are coupled in the record definition.

Functions are associated with a protocol and a type independently of the record or protocol definition. This association is many to many. The same function can be associated over many protocols and many types. There is no coupling. You can even dissociate them. To add new associations or dissociate existing ones, you don't need to touch the record definition at all.

> The methods depend on having the records fields in scope.

They don't. Record fields aren't scoped to their associated methods. They are always public. There is no self or this scope. They are just normal functions like any other. You don't even need a protocol. Just write a function which takes a record and does something with it. Now if you want to make this function type polymorphic, you can use a protocol.

I'm not sure I'm explaining myself very well. Think of an object. The object defines some data fields. And it defines some methods together. You can't use those methods with other objects. The ownership is tied to the object that defined them. The only way to share the methods is through inheritance. Similarly, you can only manipulate the data fields of an object through the object, because the object owns the access to them. Only if it chooses to make them public or expose a getter/setter are other objects allowed access. This is OOP. If you model your programs with objects like that, you are doing object oriented programming. Data is encapsulated behind objects, and can only be manipulated through sending a message (a method call) to the object. The object defines the set of possible methods for that data. You can't do that in Clojure/Script, because no object like construct exists that would allow you to do it.

Now in Clojure/Script, you have datatypes. That is, data with an associated type. You can create custom ones, such as records. A record is a set of key/value pairs with an associated type. A Person record defines a type Person, with keys Age and Weight and their values. That's all a record does. It can't restrict access to its data, it can't expose methods, and it can't receive messages (method calls). You can now write functions that take input of this new type. Those functions can be defined anywhere. They have no special access to the data inside Person, there's no `this` or `self`. The functions don't live inside the datatype. Now, if you want a common set of functions to work over many different types, you can specify a protocol. That's all a protocol does. This is just functional programming.

Sorry for the length. The distinctions are subtle, couldn't find a way to explain them with less words.


The essence of OOP is abstraction via message sends to and from an otherwise opaque interface. (Really really close to actors, but message sends are synchronous by default and objects don't have their own thread of control.) That's what gives you polymorphism; inheritance and encapsulation are down to discipline. Often implemented as a table of function pointers for performance reasons, the message sends becoming ordinals into the pointer table, and losing a certain degree of encapsulation and first-classness in their own right, like delegation or queuing or other interesting things.

OOP isn't specifically attached to tying behaviour and data together. But if you only get to interact with state via message sends, and you try and follow that all the way through so your only non-primitive values are objects (and perhaps even your primitive values are objects too), then at least some of those opaque references are going to be mutable. And then you get state into the mix, and trouble starts brewing.

IMO Rust traits, Go interfaces, Java interfaces are the essence of OO.


I mean, yes and no. That's why this is a semantic debate. There are no right or wrong. Paradigms of programming are not formal concepts.

Some people say the essence of OOP is polymorphism. Others say it is inheritance. Others say it is the tying together of data and behavior.

I personally find issue with saying OOP is just ADTs + polymorphism. Which is what rust traits, go interfaces and java interfaces are.

    Type A
    Type B
    function F(value) {
        switch(typeof value) {
            case A: "I am A."
            case B: "I am B."
        }
    }
There, I have achieved object oriented programming. I have values with an identity, A or B, and I have polymorphic functions over them.

I don't know, it feels too broad to be useful.

That's why I prefer to say that OOP is more related to the tying together of data and behavior behind an object. This is how the Gang of Four book defines OO.

I find it frames things in a way that is more differentiating.

So now I can have OO, inheritance and polymorphism in any combination I want. This means Clojure/Script can now be defined as supporting inheritance and polymorphism, which it does, yet not supporting OO. And I can have another language supporting OO and polymorphism, but not inheritance, such as Rust. Or without OO and inheritance, but with polymorphism like Go, etc.


> This is how the Gang of Four book defines OO.

That book defines OO from the point of view of Smalltalk and C++.


> I'd say traits in Rust, typeclasses in Haskell and Scala imlicit views are the equivalent to Clojure/Script protocols. And similarly, those constructs by themself aren't OOP. I think a simple proof is that if they were, then all OOP languages would have them. But because there are OOP languages (the most popular ones at that) which don't have them, then it follows this construct is not fundamental to OOP.

This seems completely circular to me. Your definition of OOP is really "whatever all OOP languages do"?

> I'm not sure I'm explaining myself very well. Think of an object. The object defines some data fields. And it defines some methods together. You can't use those methods with other objects. The ownership is tied to the object that defined them.

In Clojure you can't use a protocol method on a type that doesn't implement that protocol. I don't see it as being that different from D's Uniform Function Call Syntax (a free function can be called as a method of its first argument and vice versa.)

What is OO to you? Is it Class Oriented Programming? Is it a programming paradigm or is it the list of features and practices common to a family of languages? Almost no common OO languages treat method calls as messages. It's just a function call with a (usually) hidden 'this' parameter. Applying a protocol function to a record that implements it isn't really any different, from a perspective of message passing. A message is passed to the record and based on its pedigree it decides how to respond.

Plenty of OO languages don't have private data and allow direct access to object fields. Arguably, by your definition, it isn't OO at all to even provide for public fields. As long as the methods follow the data around or vice versa, in my view, encapsulation has been achieved. Enforcement is another concern, but it's not Jail Oriented Programming so I don't see that enforced encapsulation is a necessary condition for OO.

A record implementing a protocol can receive method calls. All of its method implementations happen with the records own fields in scope (assuming you define them in the defrecord). It doesn't need to restrict access, if you'd like it to have private data.. respect its privacy and don't look.

As I understand it, the state of the art in Class-Oriented Programming has been to define interfaces for everything, and then define classes that implement those interfaces, and then use the interface to type functions and methods. A Trait in Rust or a Protocol in Clojure just knocks out the middle man, the unnecessary and unwanted class. What's left is still OO, you can get rid of the class and all of it's baggage and still have plenty of OO left.

> A record is a set of key/value pairs with an associated type. A Person record defines a type Person, with keys Age and Weight and their values. That's all a record does. It can't restrict access to its data, it can't expose methods, and it can't receive messages (method calls). You can now write functions that take input of this new type. Those functions can be defined anywhere. They have no special access to the data inside Person, there's no `this` or `self`. The functions don't live inside the datatype. Now, if you want a common set of functions to work over many different types, you can specify a protocol. That's all a protocol does. This is just functional programming.

This, to me, is a description of object-oriented programming without the class ceremony. It's also functional programming. The use of records and protocols is functional programming, but the constructs themselves have OO nature. I don't think functional programming and object oriented programming are in opposition to each other. I think functional programming and mutable state heavy/class-happy programming don't mix but the latter is not my definition of OO.


> This seems completely circular to me. Your definition of OOP is really "whatever all OOP languages do"?

It seems logical to me that for two things to be grouped together in the same category, some commonality between the things are required. So there has to be something in common with all OOP languages which makes them OOP. Otherwise, it is not possible for them all to be OOP languages.

You're saying a language supports OOP if it has a protocol like construct. Sure, you can decide to define OOP as such. But then languages commonly refered to as supporting OOP, like Java, get excluded. So it seems to me it's a bad way to define OOP if it excludes Java, which is probably the most cited language when talking about OOP.

> What is OO to you?

It's a paradigm. Where your program models the problem using a concept known as an object. Characterised by the grouping of data fields and procedures responsible for accessing and modifying said fields. Objects know about both their fields and their methods. Where other code can only access the object's fields and the object's methods through a reference to the object itself. A pure OO language would have everything modeled this way exclusively. Thus nothing would exist outside an object, and you'd only have objects interacting with other objects. An impure OO language, thus a multi-paradigm language with support for OO, would allow part of your code and data to be modeled this way through objects, and other parts using other paradigms.

Without resorting to interop, Clojure/Script, to my knowledge, does not have an object construct which fits this definition.

> from a perspective of message passing. A message is passed to the record and based on its pedigree it decides how to respond

The call isn't made to the record, but to the protocol function. A function is called, and this function decides what to do based on the type of the first argument.

> Plenty of OO languages don't have private data and allow direct access to object fields. Arguably, by your definition, it isn't OO at all to even provide for public fields

Those are impure OO languages, they are multi-paradigm. If you were to make all your data inside objects public, and never put any methods on the objects. And you would put all your methods outside of objects, or inside objects with no data and have them take objects with data as their first argument, then your program is no longer using the OO paradigm, it is no longer object oriented.

> As long as the methods follow the data around or vice versa, in my view, encapsulation has been achieved

Clojure/Script records don't have their methods following them around. You can define the functions for the protocol in another namespace, and then you have to require them separately. They don't tag along with the record.

> A record implementing a protocol can receive method calls. All of its method implementations happen with the records own fields in scope (assuming you define them in the defrecord)

Again, the call is made to the protocol functions. Simply importing the record does not make these functions exist within your namespace, you need to require them. And these functions are not scoped within the record. They don't see the fields, they need to access them through the record, like any other function would. Nothing is special about these functions. They're just functions that know how to do something to a concrete datatype.

> As I understand it, the state of the art in Class-Oriented Programming has been to define interfaces for everything, and then define classes that implement those interfaces, and then use the interface to type functions and methods. A Trait in Rust or a Protocol in Clojure just knocks out the middle man, the unnecessary and unwanted class. What's left is still OO, you can get rid of the class and all of it's baggage and still have plenty of OO left

No, what you're seeing is the evolution of languages away from OO. It is a direct result of the criticism applied to the OO paradigm, and traditionally OO languages recognising its shortcomings, thus evolving other paradigms or practices which bypass the OO model. All these languages are multi-paradigm in essence now. A trait effectively is an alternate concept from an object. It defines functions interface and implementation separately from the datatype. You can have a language with traits yet without objects, such as Clojure/Script. Such a language is not OOP. You can't take away the concept of objects and still be object oriented. This is just support for type polymorphism, and it's ortgogonal to objects.

I think you might be the one with a circular definition of OO. Because you see things in languages that have support for OOP, does not mean all their features are part of the OOP paradigm.

Take Java, remove its support for interfaces, now ask yourself if it still supports object oriented programming? Yes? Why?

At least, this is my taxonomy. But I feel it is also the most common taxonomy in use. And I also feel it is a more useful taxonomy. Things are more granular, more specific. And within this taxonomy, the criticism against OOP is well founded, and more convincing. And it makes sense then that Rust and Haskell and Clojure/Script do not advertise support for OOP. It makes sense why Java, which lacks Traits, is still OOP, etc.

Just my 2 cents.


I am not saying that traits/protocols/interfaces/typeclasses are necessary for OO, I am saying that they are sufficient.

The object-like construct in Clojure is an instance of a record or a type that implements a protocol. (A deftyped type can even contain mutable fields!) It doesn't matter where the methods live, they apply only to objects that implement the protocol. That's behavior and data bundled together. As far as I am concerned that is an object. A trait is just a different expression of an object from an instance of a class. The interface being defined separately changes nothing; interfaces/traits/typeclasses offer different advantages than subtype polymorphism and inheritance hierarchies.

Would you say CLOS isn't OO?

A trait isn't an object and neither is a class. An instance of a data type that implements a trait is as much an object as an instance of a class.

Rust, Haskell, and Clojure don't advertise support for OOP because in the common taxonomy an object is defined as "an instance of a class".

> Clojure/Script records don't have their methods following them around. You can define the functions for the protocol in another namespace, and then you have to require them separately. They don't tag along with the record.

> Again, the call is made to the protocol functions. Simply importing the record does not make these functions exist within your namespace, you need to require them. And these functions are not scoped within the record. They don't see the fields, they need to access them through the record, like any other function would. Nothing is special about these functions. They're just functions that know how to do something to a concrete datatype.

1. Nothing is special about methods in "OOP" languages either, aside from the dot operator and implicit this. There are object systems that have neither.

2. You have made a factual error here. An implemention of a protocol on a record or a deftyped type(is there a proper non-generic name for this?) DOES have implicit access to the fields of the record or type.

    (defprotocol Container
      (inventory [this])
      (add [this thing]))
    
    (defrecord Box [contents]
      Container
      (inventory [this] contents)
      (add [this thing]
        (update this :contents conj thing)))
    
    boot.user=> (-> (->Box [1 2 3])
                    (add 4)
                    (inventory))
    ;; => [1 2 3 4]
The protocol method clearly has access to the contents field of the record, scoped by the record definition. The 'this' argument is a convenience to allow reconstituting the immutable object for return. You could just as easily have been forced to call the constructor again in order to reconstitute the object. If the record contained an atom it would happily carry around mutable state and there'd be no need to reconstitute anything.

Building on my previous example:

    (deftype MutableBox [^{:volatile-mutable true} contents]
      Container
      (inventory [this] contents)
      (add [this thing]
        (set! contents (conj contents thing))))
    boot.user=> (let [b (->MutableBox [1 2 3])]
                  (add b 4)
                  (inventory b))
    ;; => [1 2 3 4]
A type with mutable state, its fields in scope. This isn't even used.

You're correct that the call IS made to the protocol method. And yes, not having the protocol in scope prevents from calling the object's methods. One might even say that methods you don't know about and thus can't refer to are encapsulated. It's an extremely flexible object system--one that eschews mutable state and works well with a functional style and prefix syntax--but it's still an object system.

Object isn't a keyword in any of the languages we've mentioned. But we know how to find them and what they are. I don't understand why you can't see this one.


> The protocol method clearly has access to the contents field of the record, scoped by the record definition.

Thank you for pointing this out. I have learned something today. I didn't know that protocols implemented inline within the deftype and defrecord macros were actually given direct access to the fields. And even more surprising to me, that mutable fields on deftype are actually made package restricted. Well then, I have to concede it to you, even within my definition of OOP, deftype is now an equivalent construct to my concept of objects. Those methods are no longer trait like, they're full blown object methods, with special privileges, and tight coupling to the mutable data fields. They can not be defined separately to the type, and a type can not be extended outside its definition to support such methods.

> I am not saying that traits/protocols/interfaces/typeclasses are necessary for OO, I am saying that they are sufficient. > Object isn't a keyword in any of the languages we've mentioned. But we know how to find them and what they are. I don't understand why you can't see this one.

So how would you define OO? I get it your are in the camp that equal polymorphism to OO? And by the way, I do see it. Clojure/Script has object-based constructs. If you think of the more general sense for object, which is identity + attributes, records fit the bill. This is defined here https://en.wikipedia.org/wiki/Object_(computer_science)#Obje.... Now, I also agree with you, it even has a construct that groups data and behavior together in the form of deftype with mutable fields. So at this point, it has support for OOP.

What I'm really arguing is just a taxonomy. I see three distinct characteristics. The grouping of data and behavior together behind an object (OO). The ability to dispatch to different implementations of a procedure based on the datatype (type polymorphism). And the ability to inherit behavior from the behavior of an associated parent (inheritance). And this is my preferred taxonomy.

In that sense, prior to me learning about deftype with mutable fields, I saw Clojure/Script as supporting type polymorphism using either protocols or multi-methods. And inheritance through multi-methods. But I did not see anything to group data and behavior together behind an object. Thus I did not consider it had support for OO.

I know some people say polymorphism alone is the essence of OO. Thus Haskell, Rust, Clojure/Scipt, Go, PureScript, and others are all OO languages. I don't know though, I feel the average dev doesn't think of it that way.

Other people think of OO as the bundling of data and behavior, and that seemed much more reasonable to me. This is how the Gang of Four book defines OO for example. And this is how I like to think of it also.

But, after this conversation, I wonder if I don't prefer the definition from the wiki link I shared. Where OOP is the combination of all three. A single construct which groups data and behavior, and allows polymorphism as well as inheritance. And this is what is known as an object, and all three must be present to be considered OOP. With this definition, I think Clojure/Script wouldn't fit as OOP. Since, and tell me otherwise, deftype doesn't support inheritance. And multi-methods don't group data and behavior.


> So how would you define OO? I get it your are in the camp that equal polymorphism to OO? And by the way, I do see it. Clojure/Script has object-based constructs. If you think of the more general sense for object, which is identity + attributes, records fit the bill. This is defined here https://en.wikipedia.org/wiki/Object_(computer_science)#Obje.... Now, I also agree with you, it even has a construct that groups data and behavior together in the form of deftype with mutable fields. So at this point, it has support for OOP.

> I know some people say polymorphism alone is the essence of OO. Thus Haskell, Rust, Clojure/Scipt, Go, PureScript, and others are all OO languages. I don't know though, I feel the average dev doesn't think of it that way.

I don't think that polymorphism alone is sufficient to be considered OO. I think it's important to recognize you can do OOP without much language support, people even do OOP in C. There's probably someone doing OOP in assembly somewhere.

> But, after this conversation, I wonder if I don't prefer the definition from the wiki link I shared. Where OOP is the combination of all three. A single construct which groups data and behavior, and allows polymorphism as well as inheritance. And this is what is known as an object, and all three must be present to be considered OOP. With this definition, I think Clojure/Script wouldn't fit as OOP. Since, and tell me otherwise, deftype doesn't support inheritance. And multi-methods don't group data and behavior.

Lets work with your definition..

You concede that all of those constructs group data and behavior, since they let you define a type's methods in a manner that has access to that type's internal implementation. You concede that they allow polymorphism because you can substitute any type that implements the trait/protocol/interface/typeclass. I don't think subclass inheritance is necessary to OO because it's there as a means of expressing type polymorphism and in the class-bases languages it was for the longest time the only way of expressing type polymorphism. So.. what's the difference between participating in an interface/trait/protocol and inheriting from an abstract class? I don't particularly think there is any (some of these languages even have default implementations, including Haskell), you get subtype polymorphism from either.. so in my view the inheritance requirement is met even without a full-blown class hierarchy or deep prototype chain.

> Those methods are no longer trait like, they're full blown object methods, with special privileges, and tight coupling to the mutable data fields. They can not be defined separately to the type, and a type can not be extended outside its definition to support such methods.

All of the other constructs work the same way, even typeclasses in Haskell. Trait implementations in Rust can have privileged access to private fields. I assume it's the same with Go interfaces. And in Haskell you can't destructure or even construct a type if you don't have access to its constructor--so if you don't export the constructor from your module.. all your fields are private. But you can export a function that constructs the type. You can export the typeclasses. You can export accessors for public fields, and functions to return a modified version of the instance and so forth.

> I know some people say polymorphism alone is the essence of OO. Thus Haskell, Rust, Clojure/Script, Go, PureScript, and others are all OO languages. I don't know though, I feel the average dev doesn't think of it that way.

Well, they're not all OO languages. They all do have language level support for all of the concepts that OOP is made of and you can use them to do Object-Oriented Programming even though some of them are clearly Functional Programming languages. OOP means classes and nothing else in the mind of the average dev. In my opinion it's really just a pattern that you can apply/implement anywhere with more or less support from the language and in sufficiently flexible languages you can even build your own object system and it might even be nice to use. Supporting OOP doesn't make a Functional language not-Functional, or even a multi-paradigm language. No one would call Haskell a multi-paradigm language, yet it has language level support for OOP. Classes and FP don't really mix, but OO in FP works and doesn't have to compromise the FP paradigm. OO isn't something you need to dedicate an entire language to in order to use or benefit from.


I don't think I'm able to understand what you consider OO to be then. It really seems like you make it out to be type polymorphism.

For example, is this OO (in pseudo-code):

    person = [:person "John" 23];
    animal = [:cat "Moonshine" 5];

    function talk(object) {
        switch(object[0])
        case :person
             return "I'm " + object[1];
        case :cat
             return "Miow " + object[1];
    }

    talk(person);
    talk(animal);
In your eyes?


No, that's just a switch statement. OO is the inverse of that. The objects need to bring their own code with them in some form or another.

So a vtable, a struct/map of functions, a closure around a struct/map of functions, a closure of state, and that sort of thing would be your minimum OO with no real language support.


> The objects need to bring their own code with them in some form or another.

Can you clarify what counts as "with them"?

For example, does this count #1:

    person = [:person "John" 23];
    animal = [:cat "Moonshine" 5];
    talk-protocol = {:person (object -> "I'm " + object[1];)
                     :cat (object -> "Miow " + object[1];)

    function talk(object) {
        call(talk-protocol.get(object[0]));
    }

    talk(person);
    talk(animal);
Or is it more like this #2:

    person = [:person {:talk (object -> "I'm " + object[2];)}
              "John" 23];
    animal = [:cat {:talk (object -> "Miow " + object[2];)}
              "Moonshine" 5];

    call(person[1].get(:talk));
    call(animal[1].get(:talk));
Or would you consider both an instance of "bringing their own code with them"?


I'm not sure I really understand the point of this exercise but I'll play along.

I'm not going to write in that EDN + CoffeeScript pseudocode. I don't really like those kind of types without pattern matching. I do wish JS had keywords though.

Lets use modern JS and pretend that JS objects are simply heterogenous maps of String -> any...

    let cat = {
      talk: () => "Meow"
    };

    let person = {
      thinkingAbout: "Politics",
      talk: (self) => `Have you heard about ${self.thinkingAbout}?`,
      thinkAbout: (self, topic) => self.thinkingAbout = topic
    };

    function invoke(object, method, ...args) {
      return object[method](object, ...args);
    };

    invoke(cat, 'talk');
    invoke(person, 'talk');
    invoke(person, 'thinkAbout', 'TV');
    invoke(person, 'talk');
That'd be one example of a very bootleg object system. If you want examples on how to build an object system so you can do OOP in a language that doesn't intentionally support OO... look into the Common Lisp Object System, or for some object-oriented C code (there's a lot of it out there), or see Chapter 3 of SICP: https://mitpress.mit.edu/sites/default/files/sicp/full-text/...

Both of your new examples are more OO than your last one. CLOS is basically something like what we've been doing + way more effort than either of us is going to put into this.

Your switch statement example was such a straw man it's hard to even say what was wrong with it. Polymorphism in OOP replaces switching on type every time you define a method. This isn't a rule, but I think OOP should be on the opposite side of solutions to the expression problem from switch statements and pattern matching.

Again: In the mind of the average dev any construct that doesn't exactly mirror the semantics of classes isn't OOP. There are people who didn't consider JS to be OO because it didn't have classes. (It still doesn't, but it pretends to so they say "now its OO".) I really can't stress this enough. The languages we were talking about before aren't extreme straw man examples of OOP, like these snippets are.

EDIT: I should add with regard to these examples that I don't think it's necessary that data and behavior be grouped in definition as long as they can be treated so at the site of invocation even if the methods are invoked in the same manner as free functions.


Before I go any further. Just want to thank you for the thorough exchange we had. And maybe we'll have to agree to disagree on this one. Which is to be expected in a discussion about the name of things. That said. I did learn about some particularities of deftype in the process, so it was a win for me. Okay, now I continue.

> I'm not sure I really understand the point of this exercise but I'll play along

I'm just trying to see what are the concrete characteristics that are relevant to you to classify something as OO. Would be great if you could list them out. But I'm trying to reverse engineer them right now.

With my prior examples, I was demonstrating the difference I see between OO and a functional approach to type polymorphism.

In #1, data and behavior is grouped together inside the object. Thus you access the object to look up methods to call. The object is thus front and center.

In #2, data and behavior are not grouped. The function is front and center. You first call the function, and it looks up an implementation for the type of its argument(s).

To me, this ordering matters. It's the difference between FP and OO in my eyes.

Similarly, I don't consider #2 to have objects. There's just tagged records and functions. While #1 has what I consider objects, a structure capable of grouping data and behavior.

This order is even reflected in the syntax differences between OOP and FP. Where in OOP you use "noun.verb", because the method is on the object, and you go through the object to get to it. While in FP you use "verb nouns", because the function is first class, and it's the one doing the dispatch, the nouns are just dumb data.

If you look at this, it's also obvious why FP languages are more likely to support multiple dispatch. The function can easily choose an implementation based on all its arguments.

> Both of your new examples are more OO than your last one.

What makes it so? It's especially confusing to me because you also said:

> I should add with regard to these examples that I don't think it's necessary that data and behavior be grouped in definition as long as they can be treated so at the site of invocation even if the methods are invoked in the same manner as free functions.

And this is true of my function that has a switch/case in it.

Is OO the act of looking up functions to call in a datastructure based on type?

This is the only definition that seems to not contradict with what I feel you're saying.


Crucially they don't carry around mutable state by default. If you want a record with protocols to act like a stateful object you can do it, but you have to explicitly jump through hoops eg by embedding an atom in one of its fields and writing all your own getters/setters. It's not an especially pleasant or useful way to write Clojure(Script) but, as others have mentioned, it can be useful for things like achieving very high performance in certain situations.


Sure, they're immutable, but it's still very much OO. I've never heard of OO requiring mutable state.


I think Elm can learn a lot from Rust before 1.0 here. Rust made some "insane" decisions that should have pissed off the community but did not mostly because of the way the communication was handled. There were obviously some disagreements along the way which caused some valuable community members to jump ship, but overall it was really well handled and the language became better as a result.


Seeing these type of processes playing out over the years just impresses on me how exceptionally well done the Rust process was. The atmosphere and attitude of the community was always excellent.

For newbs/outsiders looking in, I think a lot of it has to do with a culture of validating people's experiences. That's difficult. If a user complains about it, the natural reaction can easily be to go defensive ("You're doing it wrong" or "we documented that here"). Rust has successfully fostered a culture of approaching it as an opportunity to learn something ("Why did you not see the documentation").


Elm could learn even more from Perl 6. One of our mottos is "torture the implemeters on behalf of the users." Another is "hug trolls" and try to turn them into productive members of the community. Or the even more basic approach of not removing features without deprecating them for a release cycle. And for crying out loud listen to your users, as they see edges of the software that a language designer can miss.


And how well has any of that worked out for Perl 6?


Working out better and better, thank you.


Yeah! I also liked how escape hatches weren't looked down upon as much. They're kind of important if you want to be developing production software! It's cool to remove stuff if things are poorly designed (which Elm's native code API was), but it'd be great if something better was put in its place! Reason seems to be handling this in a decent way - Purescript as well. So there are other options at least.


I’m not familiar with the history of rust. Could you give some examples?


Things that people were super excited about that got nuked: typestate, green threads, garbage collection support, tail recursion call syntax. But also small things (ternary operator was removed, a ton of keywords were changed and more).


Thanks. Makes sense. I can seen wanting some of those myself, but I get you have to draw the line somewhere if you’re ever going to be ‘done’ with the initial/beta releases.


The reddit discussion for this post is a telling confirmation of the articles complaints about the community forums/interaction: https://www.reddit.com/r/elm/comments/9a0hc6/elm_019_broke_u...


It is interesting that the message locking the post seems to portray engaging with people with opposing views about design decisions as taking time away from working on Elm, instead of being itself a form of working on Elm!

Maybe Elm needs some sort of "Please do not use this language in production" sign, and no "Community" link at the top of the home page. There's nothing wrong with a group of folks making a language for themselves / their employers, perhaps putting it on GitHub because why not, but not having a home page at all and making it clear that this isn't a stable language for others to use and they will put much more emphasis on the feedback from their own experiences than from others'. There's definitely nothing wrong with them doing that until they get to a 1.0 they're happy with, and then releasing it to the community.


> taking time away from working on Elm, instead of being itself a form of working on Elm!

Indeed. I think somewhere along the way the line between "programming language" and "programming language implementation" has been blurred, where the language is the implementation. It used to be that in designing a programming language you didn't have to write a single line of code. e.g: https://en.wikipedia.org/wiki/ISWIM

I don't think there's anything wrong with the implementation being the language definition, but there has to be time left for reflection.

> Maybe Elm needs some sort of "Please do not use this language in production" sign

Except Evan has explicitly been pushing Elm for use in production. Like, hard. He even tells you how: http://elm-lang.org/blog/how-to-use-elm-at-work


Elm is much more stable than every option in the market. Lots of companies with 100k+ loc web apps, and 2 companies with 250k+, with no runtime exceptions in production, due to it's type system. Removing native modules was needed to keep the language guarantee to not crash in runtime for every program that compiles.


"Stable" as in "doesn't crash in production" and "stable" as in "doesn't change backwards-incompatibly" are two almost entirely unrelated uses of the word. Most folks looking for a language prioritize the latter. There's no point in not having runtime ezceptions if your code stops compiling.

I understand that removal of native modules was required for maintaining (some interpretation of) the promise of being crash-proof, and that it needs to be done before 1.0. My point is exactly that the language isn't at 1.0 yet.

But more than that, my point is about the approach the developers take to the language, not about any specific decision. I'd still have the same comment about an Elm which lacked native modules from day 1, or an Elm where they figured out how to keep native modulea. Not running into breaking changes by luck is different from intentionally prioritizing stability and other people's use cases. It doesn't matter how many people are deploying Elm in production today—it's still a pre-1.0 language and maybe they shouldn't be deploying Elm in production.


> It doesn't matter how many people are deploying Elm in production today—it's still a pre-1.0 language and maybe they shouldn't be deploying Elm in production

Maybe the Elm website shouldn't advertise the fact that people are using it in production.


I agree with you, but breaking code is not really a problem since elm compiler is fast and has nice error messages. Porting big code bases takes time, but it's very worthy imo.


> breaking code is not really a problem since elm compiler is fast and has nice error messages.

Not true on either front. Elm's compiler does not have good error messages, and breaking code is absolutely a problem.


Lol Elm devs are often bragging about their huge line counts. Meanwhile I rewrote an Elm project in Clojurescript and it removed 75% the line count. Large line count is such a strange metric.


I know that loc is not a good metric, but there is not a better one. A project with 1kk loc will always be more complex software than a 100k loc, so we can compare. Were you using cljfmt like Elm users use elm-format?


There's not a better metric? I disagree. I think the best metric is "how much does it ultimately cost to write and maintain the software?"


So Elm could not be better. No runtime exceptions and easy refactor, what else would I need? There is not even comparison to a identical React project.


There is more to consider than runtime exceptions. If it takes one-third or less time to write in Clojurescript, for example, this saves much cost. Even if you add in more runtime debugging, you still save time in writing code, and time saved in the compilation-fix cycle. Then of course, you save all the significant added time required in Elm to do non-trivial things with JavaScript libraries, which is inevitable.

One language isn't necessary better, but I do think one can be argued to certainly be less expensive.

Give two teams the same project. One team will use Elm and needs to write 20K lines and figure out some complicated JS interop. The other team uses ClojureScript and will need to write 5K lines and not worry about handling JS libraries. Which team costs more money?


no runtime exceptions except for infinite loops lol


>Elm is much more stable than every option in the market.

This is false.


> Maybe Elm needs some sort of "Please do not use this language in production" sign

That's the point of 0.X.Y supposedly.


I looked at http://elm-lang.org/ and the "Install" link (to make sure there was no comment about not using it in production). There is no mention of the version number anywhere. There are, however, testimonials from (what sound like) real companies using it in production.


And that counts a lot more than any version number - advertising it for production.


I think the problem there is that far too many projects live in that world for years and years and years despite being obviously used in production.

At this point if a project has been around for a long time and has version numbers like that I just assume it’s not real semantic versioning.


> The (apparent) view that the people who spend the most time working on Elm are not people, but unfeeling robots who exist to create and distribute free software in their free time, and then to take unlimited amounts of verbal abuse on their own forum. If they respond to this verbal abuse by exercising moderator powers like banning, that makes them tyrants.

> The core team has spent countless hours discussing these topics again and again. People get exhausted explaining and re-explaining themselves until they are blue in the face. Honestly, posts like this are what makes contributors want to quit

This is a disappointing mindset for them to have. If people have questions about design decisions, and express dismay about the real-world effects those design decisions have on them as production users of the software, then it's an opportunity to convert those passionate users into engaged contributors to the core, rather than considering them as abusive adversaries. If you are explaining things until you are blue in the face, that's a bug in your documentation - and sure, bugs are frustrating, but it's the sign of an effective developer to not let them cause emotional distress. (And while the original post was direct in its depiction of the BDFL, it was far from toxic in its wording, and not meant to be an ad hominem attack.)

Instead, the thread is locked, the poster is confused, there's even a slimmer chance of the blogger reintegrating into the community to stave off these types of issues in future releases, and the language is worse off for all of that.


Not to mention, it's not actually verbal abuse that they ban. The Elm forum rules are totally batshit: https://github.com/elm/forum-rules



I’m very surprised. The moderators’ move is a way extreme to me. Unless they are personal attacks, deleting comments can never be good for the community. I cannot see any good reasons deleting those comments.


Don't be! This is how Elm works: with Evan as a petty dictator micromanaging every aspect of the discussion imaginable and in fact banning people from posts in other forums.


Haven't really been paying any attention to elm, all I can say now is that I never will.


I have, I thought it’s philosophy was pretty neat and I love the idea of being able to use something like Haskell in JavaScript land. I’ve seen complaints about how slow it moves and the fact that there is effectively one developer has certainly worried me.

I never made a serious try to get it into production.

It’s now very clear to me there’s no point in EVER trying unless there’s a massive shift in the project.

The community/leadership sounds very broken.

This seems more like what I would expect from someone’s hobby project that got out of hand and accidentally got a bunch of people who follow along no questions asked.


> This seems more like what I would expect from someone’s hobby project that got out of hand and accidentally got a bunch of people who follow along no questions asked.

It does, but then you realize he actually advocates using it in production and tries aggressively to grow the community. See e.g. http://elm-lang.org/blog/how-to-use-elm-at-work


Reflex-FRP is finally getting documented and usable. I looked at ELM and decided that I could wait for reflex.


Eh, I wouldn't exactly be a martyr for these deleted comments.

dulac91 has been "leaving Elm" starting at least six months ago, supposedly only still around to let others know of his decision.

superdisk and type_level_memes are piling on about how much Elm sucks in multiple comment threads, random examples being "Elm's BDFL is just a DFL" and "Elm is not the language for [me]. Or... anyone, apparently, since writing Elm code can be considered a liability at this point", respectively.

This sort of dismissive toxicity just begets more of it and turns off newcomers.

Sometimes you have to make your point and then leave the theater so others can enjoy the show.


> Elm's BDFL is just a DFL

Which is true.

> This sort of dismissive toxicity just begets more of it and turns off newcomers.

Ah yes, legitimate criticism of Elm turns off newcomers so it's entirely justified to ban legitimate criticism. Wonderful.


Looks like they locked the thread and removed it from the homepage.

I understand they are set in their ways and aren't interested in changing the language in ways some people want, but removing any and all posts that criticize them feels more like censorship than anything.


A few months ago on r/elm I made a post about how Evan was killing the momentum of Elm by not communicating about Elm's timeline more clearly.

I was told, I was lucky not be be banned from the r/elm.

A month or so later someone else posted on r/elm referencing my prior post. So I replied back and said I was the OP and that I agreed with his view and how nothing has changed. That reply go me banned from /r/elm.

The elm forum has a very strict rules, almost dictatorial [1], Posters who demonstrate a Pattern of participation in Conflict may be banned from any Elm Forum....A Pattern may also arise within a single conversation.

[1] https://github.com/elm/forum-rules


Evan probably talks about you in the first minute of https://www.youtube.com/watch?v=uGlzRt-FYto


The fact he thinks it's worth bringing these things up is very telling.

I would have ignored them for what they are and moved straight to talking about what success is.

The fact it got laughs is also very telling of the community.


A very interesting talk.


Also the frankly insane definition of Conflict as "something which isn't actually at all related to Conflict."

Under Supreme Leader Evan's rules, apparently talking about puppies would be "Conflict"


Oh my lord. Those Forum rules are hilarious.

Like its written by a cute 5 year that thinks they're being clever and nobody will know what they mean is that they will block, delete, and ban anyone ever voicing critical thoughts.

Like how do they ever think that language will improve?

Either say nice things or I ban you.

There is no appeals process.


My working opinion of Elm is that it's a very promising language hamstrung by poor governance and crippled by a shockingly toxic community.

...and that Reddit threat certainly doesn't give me any reason to change my mind on any of those points. :)

Edit: I should note, I sympathise with the situation the Elm project is in. The extremely limited resources they have for development mean they have to make some compromises, and I'm not sure they've even made the wrong ones, given their goal. If they waited until a 1.0 version before trying to get adoption the project would lose relevance, so they have to get people to use an unstable beta in production. If they didn't make breaking changes to enforce a vision on the project, you'd end up with a ugly soup like PHP. If they devoted significant time to dealing with the community, they might not get anything done. Golang has charted a similar course but has had way more resources (and still has made a number of people angry); Rust has kept people happier, but in exchange for a much more community-driven vision, and not without cost in terms of false starts and dropped features. Developing a new language as anything more than an exercise is very hard!


I posted that article yesterday. I decided to do elmconf before strange loop this year, and so I'm learning Elm before the end of September.

I have to admit that I was thoroughly confused at the locking and removal of the thread. It also means I didn't even have the chance to respond that I don't necessarily agree or disagree... I'm a new part of the community.

I'm still going to continue learning and implementing a few small projects that I have planned. However, I'm a little worried that I'm going to be burnt by some in this community calling real needed debate a diatribe and silencing people that don't agree with them.


Actually many deleted comments are indeed uncalled for or criticize the creator in a personal way, so I don't have a problem with removing them.

Furthermore the discussion about type-classes is getting old. YES Haskell has them and NO Elm doesn't have them at the moment or maybe never. It's similar to the Go generic-type flame-wars that emerged in many Go discussions.


I'm torn about this. I'm rooting for Elm and I think it currently might be the best frontend solution. I really like Evan's focus on developer UX and simplicity. This means removing things, making the language smaller and simpler. And to do that you need to maintain a certain amount of control.

However this seems to have backfired. Too many in the community want this or that and/or don't feel their opinion is heard. Then they switch to something else. Unfortunatley I think something like ReasonML will win in the end. JS interoperability and just enough Algol syntax to staisfy the masses.

Evan is between a rock and a hard place here and it's due to the current sentiment in frontend dev.


I author the Cycle.js framework and for a good while I considered it to be a sister project to Elm, because both had a focus on simple functional reactive programming for frontend. I've advocated Elm and been a bit in the community as well.

Evan is great person, I still remember fondly the time we had good long conversations at a conference. I started the first attempt at integrating Elm with React Native, and that was only possible through hacking the native modules in various ways. I figured I couldn't progress with this plan due to how restricted Elm is regarding native modules, but also other things like naming of community modules. Elm is strict not just as a language but also as a community culture.

What shocks me about the latest state of Elm and its community is how easily conversations get locked. Locking should be the last resort. What leaders block or ban indicates what they do not want to risk. There should always be the possibility of blocking to say that "we cannot afford having X in our community." and defend some basic principles.

In many communities, X is just personal attacks or harassment, that kind of thing. The consistent pattern I see in the Elm community management is that X is dissent. "We cannot risk having divergent discussions (polite or impolite) in our community" is message that restricts thought freedom and reinforces cult-like behavior. It's not a good thing to have in a community, specially in an open one on the internet where membership is fluid (unlike dissent in a company for example). Curbing dissent ultimately leads to lack of innovation (which comes from freedom to create without fear of rejection), which reinforces the status of the leader as the primary source of innovation, because whatever the leader does is assumed to be never rejected. I think these community dynamics are bad in the long run, it creates more feeling of rejection, decreases innovation, and centralizes even more responsibility and power to the BDFL.


Andre's talk "The Past, Present, and Future of Cycle.js" [1] describes how Cycle was grown by the community through discussion and experimentation.

[1] https://vimeo.com/216787869


> Too many in the community want this or that and/or don't feel their opinion is heard. Then they switch to something else. Unfortunatley I think something like ReasonML will win in the end.

That's not the only reason ReasonML will win. The BuckleScript JS compiler generates fantastic JavaScript, with no huge extra runtime at all. Also, ReasonML benefits from all the stability of OCaml's type checker. And, as you suggest, both ReasonML and BuckleScript are in my experience a much friendlier and open community to experienced devs and commercial users.


The runtime is not that big with 0.19 in my experience. I rewrote a (P)react component that was 5kb and Elm came out to be 6.8kb.

If you're comparing it to ReasonReact, you still have the React/ReactDOM Javascript dependency which is still ~30kb gzipped


Kind of curious if anyone's played around with using incr_dom with Reason:

- https://github.com/janestreet/incr_dom/

- https://www.youtube.com/watch?v=h_e5pPKI0K4

Seems like it might be tied to js_of_ocaml though...


React is unfortunately quite big indeed. However you can actually replace React in ReasonReact with an API compatible alternative via Webpack.

I tried this a few weeks back and have to say that it only worked with Inferno.js though, not Preact (lack of React 16 feature support) nor Nerv.js.


Thanks for the plug! I work on Reason. We do have great interop, but we also make other trade offs; hopefully folks don’t rage-join for the wrong reasons =).


It's an alpha pre v1 language, it's going to break things to get better.

However they also need to encourage users to build things so they can observe patterns and improve the language - often by removing the functionality developers relied upon (in the maintainers opinion wrongly.)

These pre v1 developers are going to get angry and burnt, the main problem is that Elm is ambitious and reliant on just one guy. 18 months with few warnings on breaking changes gives developers a long time to become reliant on deprecated functionality.

I've done some Elm development, the lack of documentation and support unless you hang out on chat channels put me off. I might try again post v1 but that feels a very long way away.


> It's an alpha pre v1 language

Not according to the homepage. According to the homepage you should install now and it's already used in production by major companies!


> According to the homepage you should install now and it's already used in production by major companies!

> > they need to encourage users to build things

Is obvious from feedback on this article that this is alpha quality software. Feel free to be a guinea pig but I'd be very worried if I had any Elm code in production (unless you are no red ink and can hire the language author)


> Evan is between a rock and a hard place here and it's due to the current sentiment in frontend dev.

Evan is suffering the direct consequences of his actions because he tried to recruit people to the community and then fucked over their production projects.


I agree that community management and handling of issues could be better in Elm, but I’m willing to overlook that due to the terrific quality and innovation of the end result. Elm has benefited greatly from vision and the willingness to take risks, which is much easier to do with less communication overhead and fewer contributors.

Maybe someday Elm will adopt a more community-oriented approach, but I don’t think that is necessary right now.


If I could upvote this more than once, I would. Spot on.


I looked at Elm a couple years back and decided on Purescript instead: http://www.purescript.org/

Has a much better FFI story with JavaScript than Elm, and has a more powerful type system (higher kinder types, etc). Row polymorphism makes writing pure code a joy, the effects system is unobtrusive. I actually enjoy programming purescript more than Haskell.


https://github.com/slamdata/purescript-halogen is a very nice frontend library with local state per component, such that not the entire vdom needs to be diffed.

However, if you want an easy migration path, there is https://github.com/pselm/core Which is a drop-in replacement of the Elm 0.18 API in purescript. You can almost directly copy your elm code to it and it will work with minor changes


I recall looking at halogen a while ago (maybe a year?) and I didn’t really get where it was going. The goal is to do ui in a functional language and you chooses a functional language for various reasons. But then the halogen components are essentially reimplementing object orientation in purescript (so everything looks weird, there isn’t special syntax for common things, and the types are bizarre):

Components are these things that have a way to initialise themselves, an internal state, a way to receive messages and update their internal state, a way to compute from the internal state (ie rendering), and a way to reply to messages that are querying things (by having the message pass a callback for the reply as an argument). The only thing they have which is missing from standard oo is a way to pass up messages to their parent component.

After all that one must wonder what the point of using purescript for this is and why not just use a more object-oriented language fron the start?


That `core` `README` is really good! It's convinced me to look more into PureScript. Thank-you!


I like halogen, but elm folks may want something more familiar (and with less type variables).


Definitely. One of the 'elm-like' libraries (like the one I also linked) are a great stepping stone to get started.


I don't think you even need to call them stepping stones. They are perfectly fine libraries in their own regard.

Slamdata had some specific requirements with halogen that everyone may not need. And although the free monad approach is nice, it certainly is not fundamental or necessarily the best depending on the task to UI construction.


Exactly, take a look at https://github.com/utkarshkukreti/purescript-hedwig/blob/mas...

I think anyone who is already comfortable with elm can look at that and be productive in purescript very quickly.

* Of course, with purescript there is going to be a bit less hand-holding. But the side-projects I see folks posting about in done with elm are done by folks that already do things on their own.


Is Purescript still around? It’s github repo shows very little activity, but this is not always a bad sign, so I’m curious.


PureScript is only getting more active as a community. Its creator, Phil Freeman, works at my company (Lumi). He recently wrote about how we use PureScript: https://medium.com/fuzzy-sharp/purescript-and-haskell-at-lum...


It was too Haskell for me and used Bower. Seemed like a project that was far to removed from JS and its eco system.


The inventor and lead mantainer giving up on his role a few months ago might have had a detrimental effect.


Could you provide a reference for this? I'm just hoping you are not mixing up Python (by Guido van Rossum) and Purescript (by Phil A. Freeman).



Thanks for the reference -- that's rather unfortunate because Phil Freeman did a great job leading the project.

BTW my question could sound like I am hoping Phil Freeman has given up the project leadership, which isn't what I meant to say. I should have said "I was wondering whether you are mixing up ...".


Oh, I didn't interpret your comment that way at all, your intended meaning was clear (to me).


How is performance and asset size like? For me those are a deal breaker.


I recently took on a contract to fix a busted elm project. There were no runtime exceptions but it was such a Frankenstein monster I couldn’t believe it.

If you’re doing any real work on the web, you’re going to have to use external libraries and code and such. You’re not going to have time to recreate an entire mapping framework in Elm or whatever and the minute you need to commingle in such a way you loose a major benefit of such system.

Also “no runtime exceptions” does not mean the application isn’t broken, does not absolve your developers from real testing and QA their changes.

Also it says a lot when someone wants to call their framework a kernel and patterns their behavior off you know who.


In general, type checkers almost never catch any errors that wouldn't have been found extremely quickly in a strongly typed dynamic language.


Having worked in a large codebase to fix type errors in an evolving language, this is not true. There are plenty of errors.

What is true is that the errors you catch are most often benign and not obvious, user-visible bugs, for one reason or another. (If they were obvious, they would have been fixed sooner.) The code might be abandoned or rarely executed, or it's never called with that argument. Something like that.

Occasionally you do find a serious error, though.


"Extremely quickly" -- if you can always take all sides of every branch in control flow. And you'll find only the same errors -- if you restrict the types you define to those supported by the dynamic language. If you don't restrict yourself, however, you can start catching classes of error at compile-time that in a dynamic language you could only look for by writing your own runtime checks.

And runtime checks aren't free - they cost CPU, they can have their own bugs, and they can be tedious to write. "In general", rather than even try to be as careful in a dynamic language as you could be with an advanced type system, you'll just settle for not being careful.


Type checkers can be of great help when refactoring, though.


I would have liked to have seen custom operators stay around in 19, but I understand the motivations. I think it was in 18 they removed back-ticks for the same reason. Just makes it way easier for newcomers to learn the language.

As far as I understand it, the native modules lockdown had to take place. Period. The one thing that most attracts myself (and many others, I'm sure) to Elm is the guarantees that the compiler gives you towards ensuring no run-time errors. Give folks the ability to author and publish Elm native modules and the entire platform loses that guarantee.


> Give folks the ability to author and publish Elm native modules and the entire platform loses that guarantee.

Note that you can't even compile native modules for your private modules.


I don't. If you ban custom operators, how can you find out future use cases where custom operators make a lot of sense? I am extremely distrustful of a language where certain people are more equal than others. Those elevated people could be blessed library authors, or within the core implementation or whatever, but I hate not being trusted to use my tools properly.


Agreed. Not allowing footguns is great, but custom operators aren't a footgun and in any case the fact that Elm has operators at all kind of indicates that they you're just being treated as a second-class citizen.


Not only that, it was a completely undocumented hack in the first place that it was repeatedly communicated shouldn't be used. And I really don't see how JSON parsing is a task that actually needed hacks to work short of massive GB dumps or something.

I am sometimes frustrated by the way Elm is almost more a DSL for web apps than a complete language, but I also don't see anything wrong with closing an unsafe API in a language that's specifically about type safety.

It seems like complaining that Haskell doesn't give you instructed ASM blocks.


It seems like complaining that Haskell doesn't give you instructed ASM blocks.

Well, Haskell doesn't give them out of the box, but it allows implementing and distributing all sorts of crazy unsafe stuff in libraries, including exactly this sort:

http://hackage.haskell.org/package/inline-c-0.6.1.0/docs/Lan...


> Just makes it way easier for newcomers to learn the language.

I don't think that's true. In either case, being beginner-friendly above all else is a bad design goal.

> the compiler gives you towards ensuring no run-time errors

lol except for the part where you can cause infinite loops at runtime. whoops!


I love Elm. Discovered it last year and loved the language so much that I went on a conference later in the year on my own budget.

I love how low the cognitive load of adding features is. Having a small baby, it helps a lot.

That being said, some things really confuse me, and especially the way the governance model is accepted and cherished. We have a `almighty god` deciding what will and not be done, the accepted reason being that it's not 1.0 yet.

I mean, with 0.19 coming out, all IDE support went out the window and the answer from the author of the language is basically : 'changes are known since last May, things should have been done already. I don't even know what the status is'. Communication is at the minimum lacking tact. Others could say straight offensive for not-core users.

I understand PR is hard and not fun. But then find someone to do it for you, or act as to minimize it.

At the moment, the advantages of elm far outweight the issues for my personal use. But I really wonder about the long run, but I'm really afraid someone gets really pissed and forks the language in the long run, fragmenting and ending up destroying this great community. That would really be a waste.


Are you paying to use Elm? What's stopping you from sticking to 0.18? What's stopping you from building your own 0.19 IDE support?


This is 100% what i mean. This exact way of thinking is what imho might well prevent Elm to strive as much as it could.

And btw, I am (modestly) involved in the IDE support. So do check before being cynical <3.


My exact way of thinking is that you are not entitled to anything, and neither am I.

I run businesses on Elm. I use it because it's the alternative that makes the most business sense (most confidence that I can release things without them breaking, and least chance of failure at runtime). I make money with this tool, and I don't pay to use this tool.

If a better tool came along, I'd use the better tool. So far, Elm is the best alternative in my opinion. Everyone has this freedom to pick and choose their tools.

And what does it mean to strive? Scala was a striving language. And? It's garbage. It tried to be all things to all people, and ended up being an awful tool.

I find it absolutely amazing that so many petulant children in this thread believe they are facing some kind of authoritarian oppression because the language designer said "You know what? Synchronous FFI has almost only been a bad thing. All FFI should be synchronous from now on."


First, I'm not sure what makes you think you can just insult me. I'd recommend you tone it down.

Second, most of what I see here is very similar to my experience:

* I love Elm * I take its pros and cons * The day i don't find what I want there I ll just move out of it. * I do my best to participate in the community as well * Bonus: I also got into Scala and stepped out

The only thing I was trying to convey is : * The creator of the language is very opiniated (that is fine) * The way of communicating is not ideal (that is shame, it doesn't have to BE this way). * I hope someone will not really get annoyed by it to a point where the community will get separated / torn. Because it would most likely lead to a lot of negativity.

Not sure what brings you to call me a pedulant child. I don't remember saying I was oppressed. . .


I'm sorry that my general frustration with most of the comments in this thread materialised as being directed toward you specifically. That wasn't my intention.

People (not necessarily you or I) are going to be annoyed though. And this is fine. A tool doesn't need to be the most popular alternative to "thrive" or to "win" (quoting some other commenter now).

The thought process for most people in this thread seems to be:

1. Get mad when their "freedoms" (dangerous synchronous FFI) are taken away.

2. Complain, because the programmer knows best (ego/hubris).

3. Complain further about "the community" when their egos aren't pandered to.

If the maintainers relinquished their ideals, we'd have a language as dangerous as most of the alternatives. Frankly, I'm not interested in a "community" where everyone gets along but the software is always broken. I need my stuff to work.

To you, specifically, I appreciate that you tried to keep the discussion amicable. Thank you for that, and again I'm sorry.


No big worries. Insults fly a lot on HN, this won't be the last time. Agreed. It's not about pleasing people.

Being opiniated is what brought Elm where it is. And it is not about winning. Definitely agree there too.

I just wish design considerations were communicated in a more positive, rather than autoritative manner. Because it makes people lose motivation and brings negativity.


BTW, I appreciate you apologizing. It takes balls and lots of people just don't like it. Much appreciated, sending you virtual karma kudos :)


For a language aimed at beginners its advocates really make it difficult to recommend getting involved with it

Your response typifies the unpleasant atmosphere I've witnessed around Elm which is a shame as it's author and intentions seem so good.


What you are doing is called Tone Policing.


No‚ it's not. They're not attacking the validity of your statement by criticizing your tone, they are criticizing the effectiveness of your communication. And tone is completely relevant in that context.


I only briefly dabbled in Elm and I'm not terribly qualified to comment on specifics, but I had a similar, negative impression of the way the project is managed. Especially in comparison to many other open source projects.


I would be totally on board with these changes if an alternative way of doing things had been offered.

From the philosophical point of view they are exactly what is needed to allow Elm give as much guarantees as possible.

The sad reality is that very little pragmatism went into this release and now stuff like web-sockets won't work [1]. This is very surprising because web-sockets are an use case where the Elm-architecture really shines.

Things like this reinforces my idea that while Elm is a very nice language I wouldn't want to have it in production, or even in a side project, at least till it reaches 1.0 and becomes more stable.

[1] https://github.com/elm-lang/websocket/issues/27


Not only it doesn't work in 0.19, but the README doesn't mention it, the issue is closed (making it difficult to find) and the Elm release notes/upgrade notes don't mention it.


> From the philosophical point of view they are exactly what is needed to allow Elm give as much guarantees as possible.

Pretty much every language falls apart when it comes to FFI. In Haskell, a foreign function `Int -> Int` can print to the terminal. That's just the way it goes.


Why can't this be done with ports?


I'm not sure. I haven't looked into the issue much.

In any case, I would expect web-sockets to work out out the box in a language that is specifically designed to run in the browser.

If that's not working I'm sure that less common parts of the web platform aren't supported either. Normally I wouldn't care and implement stuff myself and contribute back, but it doesn't seem to be a realistic option in Elm.

I appreciate that what's there is generally of very high quality, but I cannot pretend that there aren't many missing pieces and, more importantly, no clear roadmap


I was experimenting with Elm around .14 or .15 if I remember correctly. At the moment the language allowed you to use prime(‘) to name variables, and that was the suggested way if I had to guess by the tutorials and examples.

During next release they decided that primes as part of a name were not their cup of tea and removed it. Just like that. When I asked why was prime no longer allowed, since I couldn’t see any good reason behind it, I got the usual: “it’s pre-1 release you should expect things to change, etc.”

What left a bitter taste in my mouth was that this change had nothing to do with the language itself. It seemed as if they decided that using prime is not good style, so they forbid it.

I haven’t touched Elm since.


The proposal and following discussion seem pretty detailed too me, and you can even notice that Evan was not convinced at first:

https://github.com/elm-lang/elm-plans/issues/4


> One could argue "if that's a problem for you, don't use it," but in reality we all have to interact with others' code. The fact that it is allowed (and de facto encouraged by convention) means it still impacts those of us who choose not to write it ourselves.

Exactly my point. They decided they don’t want to use it and they decided to enforce it to all their users. The reason I don’t want to have anything to do with their language.


I think rust went through a huge amount of simmilar changes pre-1.0. I think everyone was better for it. I appreciate they're taking their time to get it right pre-1.0. 1.0 is a fairly solemn promise and I respect them for not taking it lightly.


The difference in Rust's case is that whenever anything was removed it was replaced with something that was better designed. It provided escape hatches available to allow for workarounds (ie. unsafe, and a nice FFI), and to allow C libraries to bridge the gap while the ecosystem was still developing.


That’s exactly the kind of answers I got instead of giving me one good engineering reason on why primes were made forbidden just like this. Thanks for the demonstration.


The actual reason is probably "when people use primes, they post code that gets syntax-highlighted by tools that don't know about primes, and this results in Elm code looking broken and stupid."


Which is not a good reason to ban them completely from one release to the next. It only shows that they don’t really care about their users that much.


Rust has FFI that doesn't shit the bed lol


For those of us who desperately want to put javascript (the entire language) behind us, many of the "problems" this article mentions are features.

Refusing, on principle, to be inter-operable with unchecked javascript code is a defining virtue of Elm.

edit: grammar


> For those of us who desperately want to put javascript (the entire language) behind us, many of the "problems" this article mentions are features.

Yes, not having FFI is a feature. Brilliant.


Whilst I get the point about the community leadership being more autocratic than others, the Elm language itself has always been opinionated - thus it's difficult to empathise with the rest of the sentiment of this post. An 0.x release, two years in the making, drops two features such that it can further fulfil its design goals and everything is ruined?


I really enjoy the fable-elmish philosophy, I think that the community is great compared to what the author of this post experienced, but..BUT, the platform on the whole is just too immature. I remember trying to compile jquery using the otherwise great ts2fable.. and it was, and probably still is, pretty much impossible. Or trying to use fable-elmish and getting “undefined behaviour” in a “hello world” test page. Luckily I discovered pretty soon these limitations, but I think that when a platform is not battle tested by millions of people you’ll always get this kind of grief. So just test if elm or fable-elmish or whatever framework work well for you before committing head first into it..


One of Elm’s main focuses is code that is not possible to crash. Once I groked that, native modules going away in favor of ports made much more sense.


Maybe it's my recent living in the Erlang/Elixir community, but writing code that is not possible to crash doesn't make sense to me. Servers die, machines get unplugged, network partitions happen, etc. I don't discount that the type system allows you to specify segments of your program that can be provably impossible to crash, but doing that for entire nontrivial programs seems like an impossible ask to me.


The general aim of error handling in languages like Elm is for types to not lie. If you can either return a result, or an error, the type represents that. The more places that can return an error: the more complex the types, and the more complicated the error handling. You can always pass the error value up through layers, but as people would say, it's just easier to make illegal states unrepresentable.

It's not an aim to pretend that failures don't happen, especially when doing remote calls, and the types will be transparent in showing where this occurs.


> Servers die, machines get unplugged, network partitions happen, etc

These things happen in backend / distributed systems. They don't really happen in the browser, which is basically a self-contained sandbox that can make network requests. The browser code needs to handle those network requests maybe failing, but that's about it in terms of dealing with unpredictability. So, it's totally reasonable to think that a web-app should never be able to crash.


So if I make two API requests and get some nonsensical data that comes back, (say v1 from the first request, and v2 comes back from the second because of some bug in the backend). The program can certainly prepare itself for validating that data, and reject the v2 if it's not prepared for it, but then what do you do? To me "crashing" that entire page is a reasonable way to get around that for some programs. You as a browser can also lose Internet, in which case you also need to be prepared for things that happen on the other end depending on the circumstances of disconnection (did my request go out before it died? should I retry?). The problem is certainly more constrained, to your point, by being in a browser, but it's trivial to get your program in a nonsensical state that such that you cannot render as soon as you introduce external APIs. Perhaps Elm gets around this somehow, but I'm failing to see how.

Edit: redirection --> disconnection


> The program can certainly prepare itself for validating that data, and reject the v2 if it's not prepared for it, but then what do you do? To me "crashing" that entire page is a reasonable way to get around that for some programs.

Such stuff is mostly handled via types like `Maybe APIResponse` or `Either ErrorMsg APIResponse`, so the type systems can guarantee that you handle both cases. And handling the error case (even if it's just a simple message to the user like "Error: Unable to fetch new weather data for location X/Y") is better then just crashing.

I mean it depends on your definition of crashing, but I consider it similar to undefined behaviour. So I greatly prefer

    $ ./fetch_weather $location
    panic: unable to fetch new weather data for location X/Y
to

    $ ./fetch_weather $location
    Segmentation fault
and I guess most people do (where segfault is similar to 500 Internal Server Error which is equally bad).


Browser code often hangs forever and then the user has to reload the page. There might not be an error message, but this is still a bad user experience.

Personally, I'd rather have the error message.


You’re describing errors. Errors do occur in Elm, but they rarely lead to crashing.

The type system forces the developer to deal with the error, so that, hopefully, the user of the app has a better experience than a non-responsive web page.

Crashes we cannot avoid through the type system is stuff like stack overflow or running out of memory.


I could not agree more - and in fact, have no recourse when a developer tells me with a straight face that their code "cannot crash". The worlds best type system doesn't prevent timeouts, running out of disk space, memory, etc. The attitude I typically receive when trying to push pragmatism instead of idealism is usually a "you just don't understand". Throwing an exception is _not_ the worst thing a program can do, by a hell of a long shot!


«Cannot crash» != «cannot fail». There are a bunch of things in Elm that can fail, but the language forces you, «through types», to deal with those situations.

That doesn’t mean developers make apps that deal with errors reasonably, but the developers should be aware of every part of the application that can fail.

Exception, of course, beeing stavk overflows as we haven’t solved the halting problem yet.


> One of Elm’s main focuses is code that is not possible to crash

Unless you have an infinite loop...


In the end I think it's a good thing to be strict in language design and openly reject many ideas and suggestions. I guess it's a case of "too many cooks spoil the broth".

Most languages have too many features anyway so removing them can be a positive thing.


> Most languages have too many features anyway so removing them can be a positive thing.

This is completely unsubstantiated.


And this is why I love TypeScript: It's just JavaScript, with a type system. If it's not in the standard then it's not in TypeScript (by default). There's no risk to adoption.


Typescript certainly is nicer than plain JavaScript, but it doesn’t come anywhere near Elm (or PureScript or GHCJS). It really is a world of difference (edit: in type safety, that is).

The reason for that is simply that TypeScript has to fit in with JavaScript, while the others do not.


I mean, Dart offers this. The difference between Dart and the other strongly typed options is that a massive pile of funding (and production apps) drive Dart and its direction. So it’s quite stable, if a little too risk averse.

That said, I’ve never found Strong Typing to be that essential for front end dev. TypeScript is incredibly pragmatic, which is why it’s been so successful imo.

Nothing stops people from writing all their dependencies in TypeScript. But it’s not necessary to use it successfully.


What is the “this” that Dart also offers? With the languages I specifically called out, a LARGE part of them is purity, and not providing the type system escape hatches that TypeScript/Flow does, to maintain easier gradually switching a codebase. A nice thing at first, but nothing gained for new projects.


this = a sound/strong type system:

https://www.dartlang.org/guides/language/sound-dart


Custom operators make code more readable when used appropriately; and any aspiring general purpose language that refuses to support them is cutting off the nose to spite the face.


Any language with a large userbase has a large group of people who don't use them appropriately. The result is a majority of code that is unreadable. Any language that depends on people to just "don't do that" is a language that will be full of people who do exactly that.


Any language with a large userbase has a large group of people who don't use them appropriately.

Nope. That was what the Java people were saying a decade and a half ago about C# having operator overloading and it just didn't turn out to be apocalyptic madness with nonsense operator overloading all over the place....just didn't happen.

On the Java side we have the BigInteger, BigDecimal mess - https://www.baeldung.com/java-bigdecimal-biginteger


This is a social problem, it can be solved without such a blunt instrument. Single letter variable names also make code more readable, but only when used appropriately. Your argument would suggest banning those!

IMHO it is better to concentrate on getting the semantics right and not waste any goodwill on restricting syntax.


Yes it is a social problem. But I disagree that you can solve the social problem without very opinionated and restrictive syntax and semantics.


I respect your position, which was shared by the designers of Java and Go, but any language I choose to use is one that seeks to empower me. For example, the purity restriction in Haskell exists to ultimately empower me, not because the language designers have contempt for my abilities.


I also respect yours. This is why there are multiple options in the PL arena for people to choose.


> Any language that depends on people to just "don't do that" is a language that will be full of people who do exactly that.

If you make a language for stupid people, that is fine, but I will not use it.


I just wanted to let you know that a possible alternative functional MVU is Mithril/Meiosis

http://mithril.js.org - vdom lib - for the V http://meiosis.js.org - state pattern in conjunction with streams to give you the M/U

Check out the gitter chatrooms, the communities for both are very FP oriented, and working on making it easier to do FP all the time.

this guy came from elm: https://github.com/pakx/the-mithril-diaries/wiki/Coming-From...



Can somebody take pity on me and explain why the ports system that's supposed to be used for JS integration can't replace the native modules?


As far as I know, ports are strictly asynchronous, compared to native code, which can be synchronous. This means that if you are using native code for something that is 100% synchronous, you now have to go through a huge hassle of every calling site becoming async.


Another proof that sticking with platform languages is a better option for productivity, even if the languages might miss a few shinny features.


Yeah, productivity.

Because spending 10 minutes writing asynchronous FFI code is definitely worse than spending a couple of hours debugging a runtime failure.


It is always funny when people compare external languages to platform ones, but keep forgetting to include tooling, libraries and eco-system when comparing them, reducing it to grammar and semantics.


> Because spending 10 minutes writing asynchronous FFI code is definitely worse than spending a couple of hours debugging a runtime failure.

Or... more than 10 minutes, because you have to re-implement a library that was already written.


Sounds like reasonable removes to me, custom operators are a pain.


> Sounds like reasonable removes to me, custom operators are a pain.

Not the point. The point is that it breaks backwards compatibility for the arbitrary prejudices of the Supreme Leader.


had to realize for a sec that they weren't talking about the mail client


Article aside, the HN and Reddit comments really reflect our toxic entitlement culture. How many comments here seem to exist for no other reason but to get in a negative jab of their own?

I'm reminded of Rich Hickey's great response (https://www.reddit.com/r/Clojure/comments/73yznc/on_whose_au...) to a similar situation.


And that, in short, is why I prefer really freakin' old languages for doing real work.


These kind of stories make me value Go's approach a lot more. You just have to worry about making your product better.


Go's approach? Go has the same extreme BDFL approach to ignoring community feedback in favor of the leader's vision. I don't see what is different about Go.


I think the difference with Go is the Google backing. Go ignores community feedback, but not the feedback it gets from within Google. So at least it can justify itself by saying, this works for Google, and that's proof enough its pretty damn good.

Elm's BDFL cannot make such claim, since he doesn't use it to build products closely similar in nature to most users of Elm. I think this is both good and bad. Its good in that Elm is more ideological in nature, since the creator is not motivated to break his ideologies by real world constraints. Its bad, because people who have those constraints must find ways to shoehorn the beautiful ideology, into the dirty real world. That's where a lot of the user pain comes from.


Evan works at NoRedInk which has 250 000 lines of Elm in production.


Interesting, I thought he had moved on to doing a masters or PhD.

Well then, I take back some of my hypothesis.


I meant go's compatibility promise https://golang.org/doc/go1compat which is totally different, and no they are not ignoring the "community" they are just keeping that promise which I and many Go users appreciate.


Go broke a lot of things pre-1.0. Rust did as well! Just think it's worth remembering before making comparisons like this.


They communicated it a lot differently, though.


Yeah, I agree. And when Rust removed poorly designed things, they were often replaced with alternatives that solved the problem better, so it was never felt like a pain.


Common with many languages, actually.


That has its downsides, but if the people maintaining the language are too lacking in strong opinions about the language, you'll have other kinds of problems: bad features get into the language and now everyone must be prepared to deal with errant use of those features in the wild; there are too many too similar ways to do the same thing, so the language becomes hard to learn and teach; there are features you never normally use, so you're terribly confused when you accidentally invoke them (suppose you have `val` and `var` as two ways to bind variables, but everyone only uses `val` because `var` has some weird extra semantics.)


Basically, you get C++.

This is what I love about programming languages; it's a true marketplace of ideas. There's something out there for everyone, and a project of every flavor. You see this happen all the time with languages projects, especially opinionated ones, where someone likes the general vibe of the language, but wishes it were more like something they are more comfortable with. But sometimes a language just wants to be where it is, especially when it's run by one ego. Programming languages can be very personal things for their authors, much like a novel is for writers.


Go has a rigid backwards compatibility promise. Any language feature you rely on is sticking around.


This was confusing to read if you only knew about the email client Elm. In general I wish people spent more time naming things better, and reducing naming clashes.




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: