Elm 0.19 Broke Us
dev.to
dev.to
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.
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.
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.
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.
Use custom operators? Fuck you. Now go rewrite all the libraries you contributed to the community.
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.
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.
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?
When you update this quickly (and for no reason), the learning resources go out of date too.
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.
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.
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.
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.
"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"."
The data and methods are coupled in the record definition. The methods depend on having the records fields in scope.
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.
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.
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.
That book defines OO from the point of view of Smalltalk and C++.
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.
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.
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.
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.
> 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.
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?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.
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 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.
> 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.
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").
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.
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
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.
Maybe the Elm website shouldn't advertise the fact that people are using it in production.
Not true on either front. Elm's compiler does not have good error messages, and breaking code is absolutely a problem.
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?
This is false.
That's the point of 0.X.Y supposedly.
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 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.
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.
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
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.
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.
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.
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.
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.
Under Supreme Leader Evan's rules, apparently talking about puppies would be "Conflict"
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.
...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 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.
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.
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.
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.
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.
If you're comparing it to ReasonReact, you still have the React/ReactDOM Javascript dependency which is still ~30kb gzipped
- 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...
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.
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.
Not according to the homepage. 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 suffering the direct consequences of his actions because he tried to recruit people to the community and then fucked over their production projects.
Maybe someday Elm will adopt a more community-oriented approach, but I don’t think that is necessary right now.
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.
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
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?
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.
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.
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 ...".
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.
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.
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.
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.
Note that you can't even compile native modules for your private modules.
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.
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...
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 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.
And btw, I am (modestly) involved in the IDE support. So do check before being cynical <3.
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."
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. . .
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.
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.
Your response typifies the unpleasant atmosphere I've witnessed around Elm which is a shame as it's author and intentions seem so good.
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.
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.
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
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.
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.
Refusing, on principle, to be inter-operable with unchecked javascript code is a defining virtue of Elm.
edit: grammar
Yes, not having FFI is a feature. Brilliant.
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.
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.
Edit: redirection --> disconnection
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).Personally, I'd rather have the error message.
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.
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.
Unless you have an infinite loop...
Most languages have too many features anyway so removing them can be a positive thing.
This is completely unsubstantiated.
The reason for that is simply that TypeScript has to fit in with JavaScript, while the others do not.
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.
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
IMHO it is better to concentrate on getting the semantics right and not waste any goodwill on restricting syntax.
If you make a language for stupid people, that is fine, but I will not use it.
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...
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.
Not the point. The point is that it breaks backwards compatibility for the arbitrary prejudices of the Supreme Leader.
I'm reminded of Rich Hickey's great response (https://www.reddit.com/r/Clojure/comments/73yznc/on_whose_au...) to a similar situation.
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.
Well then, I take back some of my hypothesis.
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.