In a comment below the main post they say that the app they wrote is approximately 45,000 lines of code over 170 Elm files.
That's one of the largest (and apparently most useful) Elm apps in the wild that I've heard of, and the fact that it exists and they had an overall good experience with it inspires me even more to try to learn and use Elm.
> In a comment below the main post they say that the app they wrote is approximately 45,000 lines of code over 170 Elm files.
One thing I've found a bit weird about the Elm community is how common it is to highlight or even brag about the size of the codebases.
One of the reasons I have come to particularly enjoy Clojurescript on the front-end is because of how much code I don't have to write. I once ported an Elm app to Clojurescript and it was only one-third the line count for the same functionality, and generally a smoother experience.
I think some developers just love building up these huge, complex worlds and living inside them, and then telling others how big those worlds are.
I think you should read this as being an otherwise 150kloc project in Java.
Seriously, at this point IBM should fork a couple hundred thousand dollars just to know how anyone on their staff can recreate this in idiomatic java (or whatever OO language they want) and have you do it in idiomatic clojure/script. I'd be surprised if you managed less than 30k and very surprised if the OO implementation managed less than 100k.
Hard data on this kind of debate being hard to come by, we'd learn something from the experiment.
An apples and oranges comparison. Any difference would surely be made up by all the tests one would have to write to be sure the clojure code was free of basic errors.
That's a completely baseless assumption. I have not found the need to write any more tests in Clojure than I have in statically typed languages. The only tests my team ends up writing tend to be end-to-end specification tests, and you would want to have those in any serious project regardless of type discipline.
Tests prove the presence of bugs not their absence. You should be writing more tests if you have no static type system, in order to find the bugs that you would otherwise prove don't exist.
A static type system like Elm doesn't fix logical bugs like wrong indexing, wrong predicate etc, it just finds issues with types. Types do not contain the real logic of your program. Types are somewhat useful for verifying data is passed around in the correct shape in a program, but to say it prevents most obvious errors is naive. Not to mention in Elm you will waste tons of time doing useless tasks like writing encoders/decoders and code that could be moved to macros in a more powerful language.
> A static type system like Elm doesn't fix logical bugs
Static type systems can absolutely catch logical bugs! Proof that a list is non-empty or that a reference is never null are simple examples.
> Types do not contain the real logic of your program.
Types can completely determine the logic of many parts of your program.
In Haskell, I often don't have to write custom traversal code, it's selected for me based on the types.
> Types are somewhat useful for verifying data is passed around in the correct shape in a program
This is only the start of what types can do, try learning Haskell.
> but to say it prevents most obvious errors is naive.
No, naive is dismissing static type systems to the point that not even extra testing is done to compensate.
> Elm you will waste tons of time doing useless tasks like writing encoders/decoders and code that could be moved to macros in a more powerful language.
Elm's aim is to be a basic easy-to-learn language (no operator overloading etc). Personally, generic (polytopic) programming feels like a more elegant approach than macros. Haskell offers both.
Haskell was my first FP language, I used it for about a year, and worked with Scala briefly after. My experience is that you're really trading one set of problems for another in practice. Static typing can guarantee that you avoid a certain class of errors, but it often results in code that's longer and more difficult to understand opening opportunities for different kinds of errors. There is absolutely no empirical evidence to suggest that the trade off is strictly superior in practice.
Static typing can catch inconsistencies at the cost of structuring your code in a way that the type checker can understand. This is often at odds with making it readable for the human. A proof only has value as long as the human reader can understand it. Here's a concrete example of what I'm talking about: https://github.com/davidfstr/idris-insertion-sort/blob/maste...
An insertion sort written in Idris has over 250 lines of code that you have to understand to know that it's implemented correctly. A python version would have about 10 lines or less. I'd have much easier time guaranteeing that the 10 lines do exactly what was intended than the 250 lines of type specifications. Of course, you could relax the type guarantees in Idris as well, but at that point you accept that working around the static checker has benefit and it's just a matter of degrees of comfort.
In general, the more constraints you specify via types the more of your program logic moves into type specifications. What this really means is that you're writing a metaprogram that emits your logic. However, there's no automated checker for that metaprogram that you wrote using the types. You still have to understand it to be correct in order to know that it's doing what you want it to. At this point you're basically living in a programmer version of the Plato's Cave.
The real question is not whether you can do something using a static type system or not. The discussion has to center around how that compares to alternative approaches such as testing, gradual typing, and runtime contracts.
Sorting (TimSort) was broken for many years in Python. It was good old-fashioned logic and theorem proving, not tests or runtime assertions, that got it fixed. There is merit in proving properties of any critical implementation, no matter how difficult. However the gist you referenced looks to be someone's learning effort, so hardly a model example.
Your view on the role of types is unfortunate if you think term logic is simply mirrored at the type level (Plato's Cave). The Curry-Howard correspondence tells us to think of types as logical propositions with terms as their proofs.
Sure, you can use formal methods to prove properties that are hard to test. The point you seem to have missed is that it takes a lot of effort to do that.
The reality is that in most cases there's a cost benefit analysis regarding how much time you can spend on a particular feature and the strength of the guarantees.
>The Curry-Howard correspondence tells us to think of types as logical propositions with terms as their proofs.
Hence my point that you end up writing a metaprogram that emits the logic. Ensuring that the metaprogram is correct is a manual process. The more complex the proof, the harder it becomes to understand.
Consider Fermat's conjecture. It's trivial to state it, it's trivial to test it to be correct for a given set of inputs. However, proving it for the general case is quite difficult, and only a handful of people in the world can follow that proof.
> onsider Fermat's conjecture. It's trivial to state it, it's trivial to test it to be correct for a given set of inputs. However, proving it for the general case is quite difficult, and only a handful of people in the world can follow that proof.
This is a strawman, conventional static type system can't prove all general cases either and no body is saying that they do that. Yet, they being a superset of dynamic typing, they allow to have the same expesiveness as such by providing a bypass like 'Object' or 'any'.
I’d say it’s more “look how many lines of code there are vs how many ways this could break in production”.
This will always be very different from your average ClojureScript/Reframe/Reagent app, which may or may not have fewer lines of code (in my experience they’ve been roughly equivalent), but vastly more opportunity for runtime errors.
Meanwhile, my team has been working with ClojureScript for years, and runtime errors are a really rare occurrence in my experience. Especially if you use Schema or Spec around the API between components.
There's absolutely no empirical evidence to support the notion that static typing has a significant impact on errors https://danluu.com/empirical-pl/
Of course, if it makes you feel better personally that's great for you and you should keep using it. However, if you're going to make wide sweeping claims regarding that, then they have to be rooted in more than just your personal experience and rationalizing.
There is also a big difference between spending one hour debugging runtime errors on any given day or week vs. the time it takes to write 3x or 4x the code and handle compiler errors.
Ultimately there are tradeoffs in how time is spent in both languages. Each developer will prefer different tradeoffs. I think in terms of net time spent on the whole dev cycle, it's hard to beat Clojurescript.
The problem with static typing confidence is it suggests no runtime errors, but really all it does is handle a certain class of runtime errors. To say that Elm has no runtime crashes is one thing, but it still suffers from all the runtime problems of any language when logic isn't written properly to account for different and unexpected values. Does your compiler guarantee that you don't have a "cents" value that is greater than 99? Or, for example, consider division by zero, which is another interesting case.
Clojurescript, on the other hand, has a novel system in place for handling runtime issues of any kind (types, values, whatever), because this is where it excels -- runtime dynamism.
There is a cost associated with eliminating this class of errors using static typing. The cost is that you're restricted to a set of statements that the type checker can verify to be correct. Writing code for the benefit of the type checker is often at odds with writing it in a way that conveys the meaning best to the human reader. This is necessarily less expressive than the dynamic approach. Code written in dynamic languages tends to do a better job of expressing its intent because it can be written in a more direct fashion.
Here's a concrete real world example of what I'm talking about:
>When I first wrote the core.async go macro I based it on the state monad. It seemed like a good idea; keep everything purely functional. However, over time I've realized that this actually introduces a lot of incidental complexity. And let me explain that thought.
>What are we concerned about when we use the state monad, we are shunning mutability. Where do the problems surface with mutability? Mostly around backtracking (getting old data or getting back to an old state), and concurrency.
>In the go macro transformation, I never need old state, and the transformer isn't concurrent. So what's the point? Recently I did an experiment that ripped out the state monad and replaced it with mutable lists and lots of atoms. The end result was code that was about 1/3rd the size of the original code, and much more readable.
>So more and more, I'm trying to see mutability through those eyes: I should reach for immutable data first, but if that makes the code less readable and harder to reason about, why am I using it?
So really what you're doing with static typing is trading one set of problems for another. This is perfectly fine if those are the kinds of problems you prefer to deal with, but it's important to recognize that you are making a trade off as opposed to getting something for free here.
In my opinion you get more than just "eliminating a class of bugs", in my (arguably limited) forays into functional programming languages I really liked the type-guided programming.
One aspect is "I refactored the code, fixed all the type-errors and everything works", another is "I don't know, what should I write here, compiler, tell me!" with typed-holes, along-side some nice search, such as hoogle (or elm's fancy search [1]) In simmilar fashion, I remember Elm was enforcing a version bump, if you break package public api.
On the other hand, you definitely are replacing one set of problems for a different set, and it is up to you to decide what kind of problems you like solving better.
For me, access to fast immutable data-structures seem to have the best return-on-investment, and easiest to introduce (i.e. even Javascript or Python have somewhat decent libraries for these).
With Clojure the approach is to use REPL driven development. It's tightly integrated with the editor, and any time you write a function you run it to see that it's doing what you intended. Because you're evaluating code as you're writing it, there's generally no confusion regarding what the code is doing. [1]
Meanwhile, immutability as the default makes it natural to structure applications using independent components. This helps with the problem of tracking types in large applications as well. You don't need to track types across your entire application, and you're able to do local reasoning within the scope of each component. You make bigger components by composing smaller ones together, and you only need to know the types at the level of composition which is the public API for the components.
Spec [2] is a contract system that's often used to define API boundaries in Clojure. I find it provides an advantage over static typing because it directly focuses on semantic correctness. For example, consider a sort function. The types can tell me that I passed in a collection of a particular type and I got a collection of the same type back. However, what I really want to know is that the collection contains the same elements, and that they're in order. This is difficult to express using most type systems out there, while trivial to do using Spec.
Nice, if I ever will have the pleasure of working in clojure, I will remember the re-find thing you mentioned here :-)
But to show more of I really liked when developing with types, take a look at this slightly contrived example [1]. I once tried to refactor a medium size haskell code-base, and ability to just ask the compiler "Hey what should I put in here?" really made my life easier :)
Yeah, there are nice aspects of having static typing as well. Haskell was the first FP language I've used actually, and it was a lot of fun. Eventually I ended up working with Clojure professionally, and I don't find that one approach or the other is strictly better. There are pros and cons to each, and they're both productive.
I never had that impression when I was building projects with Elm, in fact, it felt like I was focusing on the app itself instead of the language and boilerplate. I compared it with JS mess though.
Encoding and decoding of JSON usually adds huge amounts of boilerplate (in some small-to-medium-sized apps it might account for more than half of the LoC), and separating an app into modules requires you to add some boilerplate in your update functions.
Elm code is hard to measure because you encode HTML in Elm as well. I'm currently working on a form which takes 1.5K code in just one module. But there's a lot of HTML generated :)
Now that we have ReasonML, which has a more predictable and sustainable release cycle, and large support, as well as functional programming from OCaml and smoother JS interop when that's desired, what does Elm still bring to the table? When Elm was originally developed, there wasn't a viable alternative for those who wanted good statically-typed FP in the browser, but it would seem now that new apps in this style of development have far more choices with generally fewer pain points.
- the ones you do get from your JS code – will be shown as console errors not involving Elm runtime in their stack trace
- guaranteed support for time-traveling debugger and model serializability. How would one do this in OCaml if it allows mutable data (possibly cycle dependencies), side-effects and JS FFI?
- better dead code elimination. All the code in Elm is pure
Basically, OCaml brings almost nothing to the table out of things Elm provides because it tries to suit everybody.
> guaranteed support for time-traveling debugger and model serializability. How would one do this in OCaml if it allows mutable data (possibly cycle dependencies) and JS FFI?
The ReasonML debugger supports time-travelling as one of its features.
The Bucklescript compiler that backs ReasonML provides dead-code elimination.
> The ReasonML debugger supports time-travelling as one of its features.
Yes, but the question is: does it guarantee to be working or is it only "sort of" working? Can your view do side-effects? Can it call JS? If yes -- it will break soon.
> The Bucklescript compiler that backs ReasonML provides dead-code elimination.
I didn't say it doesn't. I said it will be way more poor than the one Elm has. I don't have the link, but Todo app in Elm weights less than the one made in React, that's how good it is.
Well, it's one thing to not be mature yet (UI could definitely be improved, it's very minimalistic currently, but works ok for me), and another to not work in principle :)
Elm has managed effects and purity, and I had immense satisfaction in using it... Managed effects means that doing http requests and working with DOM events are managed from a runtime and from the point of view of the programmer they are just returning values that describe the effectful actions they want to perform, and any changes to programming state from those actions are apparently as function arguments...
The language has its warts, but I would use it if it were not for the poor project management and support.
And to be fair it's the best documented platform for web development I have worked with. Have you looked at documentations of random npm packages lately? No, fun.
Shameless plug - I'm trying to improve upon Elm and React's programming model with my Concur UI framework. Its programming model is a bit unusual, it uses Monads to sequence widgets in time, but it is designed to be extremely simple to use, and you don't need to actually understand Monads to use it. An advantage is that it's not artificially restricted like Elm, and allows you to use advanced Functional Programming tricks when you can and want to.
I've always thought that Elm was like Purescript in the way that it was just another way to bring the good parts of Haskell (a good type system) into JS.
I didn't consider elm's architecture as the main value proposition -- if you squint it looks just like every other data management model for component-based approaches these days -- flux, redux, etc all work in a similar way, +/- immutability.
Hopefully people aren't out there thinking that Elm has done something to magically make it much harder to write runtime errors -- they added a powerful ergonomic-where-possible type system.
If developers out there are still thinking types are "bad for JS" or that using types in code is weird/unnecessary, they might be suffering from Blub[0], and need to go out and see what other languages are doing maybe widen their perspectives. Not every language with a good type system looks like Java/Dart/Typescript/etc (some people can even argue that Java doesn't have a good type system) and I often meet engineers who think adding/checking types basically means making javascript into java.
Flux/Redux were inspired by The Elm Architecture. I don't know if there was something similar before Elm popularised this style of building user interfaces.
Concur was first written in Haskell, so it's got the Functional Programming part down :) The other interesting part was TEA, which is hard to simplify further, but I think Concur's model manages to do that while simultaneously being more powerful.
Edit: I don't know what gave you the impression that I don't like types. I love types! I wish JS had more of them. I write Haskell/Purescript (and Javascript) in my day job.
> Flux/Redux were inspired by The Elm Architecture. I don't know if there was something similar before Elm popularised this style of building user interfaces.
Didn't know that, weird how it's come full circle in a way, I don't think people would consider Elm as approachable as they do now if React, Flux and resultingly redux hadn't gotten so popular. There was also a bit of a hype train behind FRP for a while but that seems to have petered out a bit.
To be fair I'm not an Elm expert but try to keep up with it, I have yet to write something big in it, but I absolutely believe the hype because the hype isn't hype, those of us that are writing or at least peeked at the Haskell/ML world have been living in this world up until now.
> Edit: I don't know what gave you the impression that I don't like types. I love types! I wish JS had more of them. I write Haskell/Purescript (and Javascript) in my day job.
Sorry I didn't mean to imply that you don't, I will go back and edit my comment -- I meant I still run into that sentiment, most recently at a meetup last month. It's not like I don't understand it, you can move faster without type checking but a lot of times that just means letting code with silly bugs through that you're going to find @ runtime.
> You can move faster without type checking but a lot of times that just means letting code with silly bugs through that you're going to find @ runtime.
Oh yeah absolutely. And also, FP and immutability makes everything much easier to understand. Getting new devs making changes to 50k lines of JS is much much much worse than with a Haskell/Purescript/Elm project of similar complexity.
Elm isn't a general purpose programming language. You can only write web-apps with it you can only use TEA (the elm architecture). In that way, the TEA really is it's main value proposition.
Elm is quite different from purescript or Haskell... It shares syntax and the purity semantics but its type system is much less expressive (no type classes); no do notation; no monads...
Edit: I changed my response after I took the time to understand what you meant.
Concur has a very uniform programming model where you do everything by composing widgets. `elm` is a widget. `render` also is a widget. As such both can perform arbitrary async effects, and they take place inside event handlers, or inside lifecycle methods like componentWillMount. Concur takes care of managing that for you.
So to log all actions to the console, you can add a logging call to `elm`.
elm render update = go
where go st = do
action <- render st
liftEffect (log ("Action - " <> show action))
go (update st action)
Meanwhile, the render function itself can be a widget which performs side effects -
view = do
evt <- button [onClick] [text "Click me"]
liftEffect (log "Button was clicked")
return evt
There are lots of examples of effects in the Concur repo - Take a look at some running examples here -
That's how literally every VDOM-based framework looks, including React. Elm isn't special in this regard. It is special in many other regards, however, and I appreciate it for that. It's also interesting as one of the few languages that have parametric polymorphism but no ad-hoc polymorphism, which leads to some interesting designs. I don't like how only the BDFL is allowed to do certain things, like create new operators in external libraries, but overall, it's a very pleasant experience.
OCaml and Standard ML are two others, and I'd say OCaml is established enough that Elm is not really breaching new ground here. The main difference is that OCaml has other features you tend to use instead, particularly the module system.
OCaml is a very fun language in my experience. Apparently they were going to add ad-hoc polymorphism in the form of modular implicits, but I have no idea what happened to that.
If you're interested in this then you might like how Nim approaches this with its React-like framework Karax.
No square or other brackets, just indentation defines the structure and it's beautiful. The only oddity is that 'div' has to be written as 'tdiv'. Here is an example from the Nim forum's frontend code: https://github.com/nim-lang/nimforum/blob/master/src/fronten...
This is actually cool. Scala.js + scalatags has this looking like this:
```
val model = div(
button( onClick := decrement )( "-" ), // decrement is typechecked as being a function of event
div( float := float.letf )( model.toInt ) //left float is typechecked and hence the misspelling is caught at compile time so big changes can be made with confidence
)
This was also the thing that attracted me to Elm years ago. But at that time Elm doesn't have a CSS module yet. Now with Elm-UI, it is like my dream HTML/CSS replacement.
However that was all for static UI though. Writing Elm app is still a pain if you are not a Javascript expert. Elm's implementation of the web API is minimal and the progress is extremely slow. In addition, most function that's in libraries in Javascript's ecosystem can not be directly used as you have to write Ports, which requires a lot of Javascript experience.
Elm-UI is in a questionable state. Their github page originally said it was no longer maintained. Now they acknowledge that they are struggling to update to the latest version of Elm which caused many syntax breakages.
Elm is fascinating since it shows that with the right architecture functional programming for GUI is possible without hatches like monads or hiding state in closures or green thread stacks.
Another things is that in Elm the state of the whole application is very visible. Typically just by looking at data structures and message types one gets how things work. The code just fills details.
To test render() in the clicked state with the explicit state I can call it with the corresponding state. With the implicit state at the very least I have to write a driver to run render() into the clicked state and then test the next state transition.
To understand the effects of the code on other parts of the application with the explicit state it is sufficient to look at the definition of the state. The implementation of render is only relevant if one wants to look at details. With the implicit state I have not only understand what render() is doing, but also understand how to properly call it from other parts of application. I.e. what happens when the render is called the third time?
Testing could be much easier for the implicit state version. I've not defined it, but you are assuming things about how the render function or the implicit state behaves.
In my hypothetical implicit state framework - A widget is composed of a sequence of steps, each of which is an independent widget. So it's easy to write invariant properties for each step -
For (text "String") it's guaranteed that the widget never returns a value or raises an event. i.e. the type is
forall a. Widget a.
So the issue of calling render a third time never arises.
The explicit version we can understand how it works just by looking at it. The implicit version we need knowledge about how your hypothetical framework works.
Of course you need to understand how the framework works. But that's a fixed cost vs. having to understand a program from scratch everytime when your framework is not clear or powerful enough.
Would you also say using "goto" is easier because it's a simple jump, vs. having to understand how while/for loops work?
Sure, I'm not going to argue the meaning of "state". You know what I mean. Hiding internal state is good for abstraction.
> Besides, your example get much of its gains by hiding the behavior of that 'if' statement than by hiding that 'buttonClicked' stores the button data.
The if statement is intrinsically linked to the boolean state. If your render function forgets its implicit state on every update, then it needs to painstakingly analyse an external state and recreate its internal state. That's a cost which can be avoided.
JS is very bad at this analyses of the external state. With Elm syntax it is less an issue.
The biggest drawback of the explicit state is that the separated GUI stages has to be named so the code can refer and match on them. This naming requires to spend mental energy when writing the code and is the reason behind complains about extra boilerplate in Elm.
In implicit state the stages are anonymous and hidden behind a semicolon (yield statements in your example). So it is faster to write code. Yet these nameless yet present states exist and makes much harder to grasp the code. One has to recover the states from the code, not from declarative description as with the explicit state.
You've used an example similar to a continuation, what is explicitly about flow control. Unless you are suggesting appropriating flow control into data (because you will need an interpreter) means that everything is "state".
I don't think that 'buttonClicked' value will make into the page state of an Elm program.
It might seem like that, but javascript generator semantics mean that execution pauses at the first yield, and awaits external input before it resumes computation.
Also, the framework I described is only partially "hypothetical". [Concur-js](https://github.com/ajnsit/concur-js) gives you almost the same syntax and semantics. So you can try it out yourself :)
[@bs.val] external pi : float = "Math.PI";
let tau = pi *. 2.0;
[@bs.val] external alert : string => unit = "alert";
alert("hello");
Elm was trying to make it very hard to use JS in Elm which is always a bad idea when you are a transpiled language. Clojure is so popular because you have access to most of Java and the libraries written in Java. ReasonML makes super easy to use JS (or other languages, same FFI interface) while being an ML family language just like Elm. For me ReasonML is clearly the winner of type safe JS for these reasons.
> Elm was trying to make it very hard to use JS in Elm which is always a bad idea when you are a transpiled language.
Your code will produce runtime errors which will be rendered into console with ReasonML runtime guts in it. I would disagree that this is in any way superior to Elm's promise of no runtime errors, and JS via ports having no runtime in its errors.
I was(am? will be again?) a fan of Elm. Constraints can be helpful and even liberating(in some sense). But that's something that should be balanced carefully with other things, like rules\laws that are clear, universal and follow the common sense.
Unfortunately, during my Elm journey, I've found that's not the case for Elm.
> Freedom is the rope with which I can hang myself.
If you'd like to, why not? It's up to you. You're not forced to do, though.
On the other side, being a hostage of some secret plans in "people in power" minds, you might be forced to do any kind of staff. New constraints are here and they're enforced on anyone. That's a catch with constraints, you can be OK with existing, but you'll never know what comes tomorrow.
> If you'd like to, why not? It's up to you. You're not forced to do, though.
Well, yeah. If I'd like to hang myself, I'd pick a technology with fewer correctness guarantees. As it turns out, that's not what I want. This is self-evident.
It's also not a valid criticism for you to say that you've found Elm doesn't "follow the common sense". This is too vague to be any kind of constructive criticism.
> Well, yeah. If I'd like to hang myself, I'd pick a technology with fewer correctness guarantees. As it turns out, that's not what I want. This is self-evident.
That's exactly what I'm talking about. I'd also like to pick up some "silver bullet", like Elm(if I got you right), for any new project.
Unfortunately, real world is usually a little bit more complicated than "I'm free to pick up any technology that I want. All browsers are the same, their behavior is consistent, mobile web is pure joy and all of our users know their plugins might cause an issue to our app, therefore they're disabling them when they launch our app".
Anybody who does webdev knows that. Different projects, different requirements(sometimes very, very strange), brand new requirements right before the release, deadlines, etc. There're lots of things you have to deal with to end up with a good website\app. Picking a language with nice "correctness guarantees", is not going to help you much.
There were times, long time before 0.19, when there were at least discussions on effect managers, extending the type system, native modules were a nice escape hatch to fix your problem at hand with nasty browser bug or even Elm runtime issue. Those were times when I was so excited to use Elm, did some tiny (sub)projects in it, did a workshop at the office.
Since then, many things have changed to the worse, imho. Some features were removed, not even deprecated. No public discussions, no roadmap, no bugfix releases, no escape hatches for special cases. But who cares what some random guy on the internets thinks :)
Don't get me wrong, I think Elm is a great experiment in the land of static languages, lots of great stuff is getting into mainstream languages(like Elm architecture, error msgs). I still hope we'll see the 1.0 version and things might get better in terms of feature stability, feedback and building things a little bit more complex than counter apps without hitting even more pain points than we have with modern JS\TS.
I am not saying that everything is going to be super easy to do this way but taking away my ability of using Math.PI because of document.getElementById is silly.
Again, I could not care less what is in core (which might not be there anymore without a deprecation warning) but I want to use JS functions if I need to. How hard is to understand these requirements?
This confirms my belief that most criticisms of Elm are emotional responses, not rational ones.
Allowing arbitrary JS functions to be called from wherever would indeed break everything. Want to use `Math.random()`? Whoops! There goes referential transparency. Want to use `document.querySelector()`? Hello runtime errors my old friend. Want to use `function(x) { return x / 0; }`?
Yeah, have fun with that. I'll stay in North Korea ¯\_(ツ)_/¯
I literally have never experienced a bug because of the existence of Math.random(). I don’t think I’ve ever used it outside of a coding interview. Similarly, I don’t spend time debugging runtime errors caused by document.querySelector()? If I do it’s usually only once when I write the initial query. I don’t understand your last argument: why would I divide by 0?
Everyone’s relationship to their tools of choice has an emotional component. Check out this talk, you might find it interesting:
No the core question is “does the time and effort spent on those cases justify the cost of switching to elm?” I have to weigh the benefit of no runtime exceptions against things like having to write interop code for libraries that I may have to integrate with.
It might make sense for other people. It doesn’t add up for me.
This matters in the real world question of “should I use elm” even if it doesn’t matter to the theoretical question of “is the elm language sound?”. Especially since there is only one implementation of elm that updates very infrequently.
What are you talking about? You said no runtime errors because you have pure functions and now it turns out that actually implementation also matters?????
>>> I literally have never experienced a bug because of the existence of Math.random().
You must be doing something wrong than. :)
The world is impure. Pretending it is not is just laughable. I am saying this and I am actually writing only functional code for living, but I understand when I cannot solve the problem with functional concepts.
Few example:
- now()
- random()
- runSqlQuery()
- open a file
My code might be 90% pure functions but the rest is mutations, handling non-idempotent things, dealing with state, as the world is.
Uhh… What? I’m confused. Did you think this would be a smoking gun to your argument?
Of course Time.now() is an effectful routine.
This is not a strange idea to “FP purist zealots” either.
How is it defined in Elm?
Time.now : Task x Posix
How is it defined in Haskell?
Data.Time.Clock.getCurrentTime :: IO UTCTime
And no, you have not given any examples of using JavaScript in a type-safe way. If you think you have, it means you don’t adequately understand type-safety.
Elm is different to ML in that it is explicitly designed to be a pure language. Purity is important for the Elm architecture and allowing arbitrary side effects in your apps would break everything.
That said, you could add effects and a JavaScript FFI using monads or algebraic effects. You could also get rid of a lot of boilerplate in the Elm architecture using existential types. It's a safe bet that the Elm developers know this. But all of these extensions would make the language less approachable. I think one of the reasons why Elm is doing so well is because it's not Haskell. :)
Coming in from the other (Functional Programming) side, Elm can be restrictive (no typeclasses, do notation etc.). But it does walk a fine balance, and I love that there's a popular-ish language that is exploring the ease-of-use design space.
Yeah, it's pretty restrictive for FP people, but I think that's fine. I think it's easier for FP people to deal with that amount of restriction, than it is for the average JS developer to quickly understand how to wield as much power as e.g. PureScript gives you.
Also a little surprised you mentioned do notation; it's just syntax sugar after all :)
> Yeah, it's pretty restrictive for FP people, but I think that's fine. I think it's easier for FP people to deal with that amount of restriction, than it is for the average JS developer to quickly understand how to wield as much power as e.g. PureScript gives you.
I have heard this sentiment earlier, and I believe that there is a better middle ground. Use a less restrictive platform and simply don't use features you deem too complicated. That's a better approach because as the developer becomes more familiar with typed FP, they would be able to use better and better tools at their disposal. i.e. the toolset will grow with their skill.
> Also a little surprised you mentioned do notation; it's just syntax sugar after all :)
Oh it's less of an issue, but syntax does matter a lot. I find that Monads become intractable for new devs without do-notation.
> I find that Monads become intractable for new devs without do-notation.
That's a fair point, and I agree, however this somewhat negates your previous point:
> Use a less restrictive platform and simply don't use features you deem too complicated. That's a better approach because as the developer becomes more familiar with typed FP, they would be able to use better and better tools at their disposal. i.e. the toolset will grow with their skill.
I can't say definitively, but you could argue that on a team, a more experienced developer might use more advanced features of a technology, which less experienced developers then have to just deal with. They might feel intimidated by that, and discouraged from working with the technology at all. I'm using "team" in a more abstract sense here too; it could be all the collaborators (existing or potential) of an open source project.
I'm not arguing the anti-intellectual position that everything should be dumbed-down. I just believe — as you said — there is some middle ground, which also isn't universally applicable.
> I can't say definitively, but you could argue that on a team, a more experienced developer might use more advanced features of a technology, which less experienced developers then have to just deal with.
Actually in practice that's much less of an issue when using typed FP. I've found that it's easy to modify parts of code which you do understand the implementation of, while still being able to use the interface of other potentially more complex parts. Of course YMMV.
The alternative is less Javascript logic written by the app developer (and instead use packages and APIs directly).
Elm disguised as a beginner friendly language for web front-end, which is not true with the case of ports. Where you need to be fairly familiar with Javascript.
The Elm community also advertise a lot on the advantage of development happiness of Elm over Javascript, and mention the Javascript fatigue a lot. But the truth for a beginner is that you can't escape the Javascript ecosystem when using Elm, and sometimes it requires even more Javascript experience than using just Javascript framework like React to build something that require a web api that's not in the tiny list that Elm provided.
If web development is not someone's main job and they are just finding a tool with better dev experience to build some side projects, the fact that they need to design an interface for almost every external package is not aligned with their original reason to use Elm.
> sometimes it requires even more Javascript experience than using just Javascript framework like React to build something that require a web api that's not in the tiny list that Elm provided.
I don't understand. You're saying a codebase of 5% JavaScript demands more JavaScript experience than a codebase of 100% JavaScript.
This seems self-evidently false.
> If web development is not someone's main job and they are just finding a tool with better dev experience to build some side projects, the fact that they need to design an interface for almost every external package is not aligned with their original reason to use Elm.
Seems like the argument is Elm isn't ideal if you're not a programmer, because you can't cargo-cult it.
> You're saying a codebase of 5% JavaScript demands more JavaScript experience than a codebase of 100% JavaScript.
A codebase of 100% Javascript does not usually mean 100% written by the app developer. If you are coding in react for some basic app, like some basic crud with react using existing backend with Firebase, it almost only require you to learn the some basics of react and Javascript syntax, and fill in the template. You can also use other modules by just follow their documentation when you need some extra functionality.
However in Elm Ports we are required to pass async messages for everything which is much harder. I am OK with the boilerplate for the JSON encoder and stuff, but I have to wrap around my head for how to write a port for lots of basic web APIs.
Port is hard even for people that familiar with Javascript. There are plenty of experienced Javascript programmers willing to dive into the source code of Elm to write native modules to avoid ports even. https://www.reddit.com/r/elm/comments/81bo14/do_we_need_to_m...
In addition, even the 5% of Javascript will eventually leads to the full Javascript stack, where Elm, without an official recommend JS stack, feels more like additional choice as a part of the JS fatigue. Beginners ends up looking up on browserfy/gulp/webpack etc and figuring out a way to integrate Elm in.
> Seems like the argument is Elm isn't ideal if you're not a programmer
I program daily for machine learning, and I have used many programming languages. But Javascript is not the language that I want to dive in too much, which it is the main reason I learn Elm. If your requirement of being a programmer is having a job as a software developer, then I am not. But I think a language with an aim of going into education and scientific computing shouldn't limit itself to that. https://www.youtube.com/watch?v=uGlzRt-FYto
I enjoy Elm. It is one of my favourite programming language. But I have not gotten anything done with it mainly because of ports. If I am pursuing a career in front-end related development, I would dive into Javascript and build stuff. I understand the decisions from Evan and friends and I am just thankful for Elm as it is, there is no right for me to demand anything anyway.
What I find most interesting about Elm as a designer who also "codes", is that because of its bottom up frontend philosophy (types first instead of components first) it somehow assumes that the UI designer and coder is the same person, that there is no clear designer to developer handoff where you hand over a thought out design to implement. It's more the other way round, where you'd hand over what you've come up with in Elm to a designer (which might be another problem because it reduces the designers job to just make things pretty).
So the best use would be for designers who code, basically unifying frontend design and engineering.
A designer can hand over a design to an elm programmer, who would then need to translate that into Elm. It's not hard to do and there are even HTML->Elm tools such as https://mbylstra.github.io/html-to-elm/ for example.
isn't Storyboard based on components (parts) first (atomic design)?
Elm is the opposite, it's focused on the whole which you then use as the basis for parts at actual design time (thinking about layout and hierarchy).
So in a way Elm is about form following function (creating visuals from existing functionality), while most of the other environments are focused on function following form instead (creating functionality based on existing visuals).
You can perfectly do the design-first process with Elm, I thought you were maybe accustomed to Storyboard or something like that.
IMHO you can do design-first process with Elm just like with React. There is no Storyboard equivalent that I'm aware of, but I never found it useful enough but maybe it is, for toolkit/library designers.
I have found that I work differently depending on environment. I do model/types/functionality first if working on my own, but in a corporate environment I typically get designs handed together with some kind of specification, so there I often do "design-first".
>I have found that I work differently depending on environment.
That's the best way to handle this I think. I didn't mean that you can't do specific workflows in either framework or language, just that there is a "native ideal way" of how things are done.
Philosophically it's an interesting topic, because there are many arguments for and against either method. It's also way bigger than programming, it's about evolution itself.
I'm don't mean to nitpick, but did you mean storybook instead of storyboard? That's the only project that I've found which sounds like what you're talking about
Anybody who liked elm is highly recommended to check out Scala.js. With scalatags, it looks close to what elm does AND you have the entire power of jvm libs.
And you get code sharing across client and server as its just plain old scala. I believe elm is not big server side.
Scalatags is not a VDOM implementation, though, and the Elm architecture (one giant state machine) is very different to what you'd typically see in a Scala.js application. A closer analog, in my opinion, would be ClojureScript + Reagent, but even that doesn't feature Elm's focus around immutability.
ScalaTags is indeed not relevant here, but Scala.js has its fair share of reactive UI libraries such as my own Laminar https://github.com/raquo/Laminar
I wonder how many of today's Linux users don't get why the editor is named nano. The fact that you can trace the name back to "electronic mail" is my favorite command-name joke in an environment full of them.
(If you missed the chain, it was
1. "electronic mail" = "elm"
2. "pine" as a joke on "elm" -- though I believe they deny the "PINE Is Not Elm" derivation, it's still pretty clearly a reference to elm
Maybe they like jokes about it somewhat but think people joke about it too much, and the way to get them to joke about it less is to pretend not to like it at all.
I think that's what happened with Clojure and people coming up with jure puns on it (compujure, etc).
I think maybe this is when you decide 'maybe our language has failed'. Or at least admit it's a toy language that hasn't been properly engineered to be effectively used commercially. When it's a 'worth mentioning' when a single app has been written in it by some dying company.
I mean, imagine if we had a headline on the frontpage of HN for every app written in C or C++ or Java or Python (or Objective-C or Swift or C# or even Visual Basic or Rust)
I'd say the products themselves are fairly uninteresting :)
NewBusinessMonitor[0] is a tool for marketing your products/services to businesses in the UK. This company is a bootstrapped SaaS, built and run by just me. It's been taking money for over a year.
Comparestack[1] is a price comparison platform. Our first product is Moneygains[2] — a way for residents of Northern Ireland to switch to a better deal on their home electricity. I'm building this with a partner — he's taking care of all the business stuff.
I have a third in the reinsurance space, and it has taken funding. I'm not going to talk about this one so much for the time being — it's still in stealth mode.
You have to take into account that Elm is relying on organic growth. If React had been released by a bunch of enthusiasts with day jobs, and no Facebook behind it, it may be in the same position as Elm.
> Steve Yegge said it best almost 10 years ago
That reads like a Register article. It's just a bit of fun.
> Functional languages are great toys but nobody uses them to get work done, almost without exception.
Just because you don’t use functional languages for serious commercial work and don’t personally know anybody who does, doesn’t mean the rest of us don’t too. Plenty of us use functional languages for serious commercial work, now or in the past. I’ve persobally used ClonureScript and know plenty of people who use Clojure. I know people who use Haskell. Jane Street famously use OCaml. Lots of people use Scala. Clearly an IBM team uses Elm. I mean, the article itself is a counter example to your statement.
I think this is interesting to me because Elm is an extremely unique way of doing frontend development on the web by utilizing functional programming concepts.
IBM, in my mind, is a massive dinosaur. To know that some group there was able to make a consumer facing product using the latest tech is cool. Definitely gives IBM a better favorability rating in my view.
Not to be glib, but what you are saying, kind-of, is "the worse a company is at staying up to date with technology, the more meaningful it is if they use a fringe langauge"
Yes? If a company sucks at trying new things, then it's probably noteworthy when they bothered to try a new thing, because it implies that either 1. they're pulling out of their rut, or 2. this thing was so great that even they wanted to use it.
(Note, I'm not convinced that IBM is quite so ... obsolete... as this discussion implies, either.)
Considering they sold most of their hardware department, such as harddrive to Hitachi and thinkpad to Lenovo, and went focusing on patents, this is a change in their image. I had the view they were risk adverse, to be using Elm is interesting.
I don't see that as a criticism. It just means a new technology is noteworthy enough that even those slow to catch up get interested. Like when senior citizens started using iPads.
It's noteworthy that as you have shown, functional languages still have this insane opposition in the industry because they are perceived as toy languages.
Despite the fact that they have a very bullish minority in the industry doing some very serious and lucrative work using them.
See for example Jane Street and Ocaml.
The fact that functional languages are not yet "mainstream" is more of an artefact to two things unrelated to their capability as languages in an industrial setting:
a) Historical baggage. Industry has always taken the first tool it has come up with, declared it the "professional tool", which has become a self fullfilling prophecy then. Despite when looked from historical perspective, there is no technical reason why the chosen language was best among many. See for example C, C++, Java, and my favorite example JavaScript (ship it! 9 days is quite enough to make a language).
I.e. industry has taken anything that creates an abstract syntax three that can be transferred to the chosen execution context, and maked it work by adding tooling and education, no matter how crude the language itself was.
b) Lack of industrial quality tooling. See a). Industrial quality tooling is often a sane prequirement to choose a language for a project. There might be instances where you feel you might want to hire a legion of developers, and then you choose probably one of the most popular languages because it has tooling and it has a large number of candidates who know the language.
Note: neither the fact that the language has tooling, nor that is popular, does mean it's the "best" for an arbitrary scenario.
We believe in iterating on/alongside product teams in order to create the best infra. The product teams' and open source folks' feedback has changed our strategy a few times, for the better. As of today, Reason and BuckleScript are also deployed on a WhatsApp internal tool, Instagram Web (small scale), plus some critical Ads internal tools. We'll be working closely with these teams over the next year."
> I mean, imagine if we had a headline on the frontpage of HN for every app written in C or C++ or Java or Python (or Objective-C or Swift or C# or even Visual Basic or Rust)
IBM writing an app of this size in Rust would definitely be headline-worthy.
For other languages you list, it's not, but only because they're already well-established, and have been for decades. But it could have been a headline when they were new and unproven.
I think it is newsworthy because it details the strengths and weaknesses of Elm in what looks like a fairly complex project. I already use and like Elm better than any other front-end framework, but if I was considering Elm as a possibility, this information would be quite useful, I think.
Definitely not the most important thing on the list, but I use Vim to code so IDE integrations mean absolutely nothing to me. I hate IDEs. To companies and teams that can't go to the bathroom without an IDE, that may be a deal breaker. Either way, it is good information to have.
One of the reason I like Elm is that it has great potential for great IDEs. If you use Vim with lot of plugins then it is already sort of a minimal IDE right? Most IDE you hate perhaps have a lot of UX design problems. Good IDEs should aim for a better overall coding experience, by this definition.
For example in Elm, all the nice error messages can appear next to the line, and all the case-of statements can be auto-completed according to the type definition, it all adds to an overall better coding experience.
>I mean, imagine if we had a headline on the frontpage of HN for every app written in C or C++ or Java or Python (or Objective-C or Swift or C# or even Visual Basic or Rust)
Doesn't this already happen almost every day for Rust and Go?
>Functional languages are great toys but nobody uses them to get work done, almost without exception.
As somebody who's been working with Clojure professionally for nearly a decade that's big news to me. Also, you might be surprised to know that it's used by companies like Walmart for critical infrastructure https://clojure.org/community/success_stories
That's one of the largest (and apparently most useful) Elm apps in the wild that I've heard of, and the fact that it exists and they had an overall good experience with it inspires me even more to try to learn and use Elm.