1. 34
  1.  

  2. 4

    Sounds very interesting, thanks for sharing!

    Can this allow writing CLI apps in Elm, by compiling them with the OCaml compiler after being translated with Phillip2? What are your thoughts on this?

    Also, super curious what was your reason to migrate from Elm! Could you share some quick “executive” summary? (I imagine you may be planning to write a separate blogpost with details, but I’m afraid I might overlook it, so would be grateful for any quick notes that you could share now!)

    1. 9

      Can this allow writing CLI apps in Elm, by compiling them with the OCaml compiler after being translated with Phillip2? What are your thoughts on this?

      It’s really intended to be used with Bucklescript, which compiles to JS. Once you have the code in OCaml though, and assuming you don’t use much of the Elm Architecture, it would lovely be pretty straightforward to wire it up. So I’d be optimimistic, if those assumptions hold.

      Also, super curious what was your reason to migrate from Elm!

      Long story short, the Elm 0.19 decision to remove the ability to call native JS was the breaking point for us. We had a bunch of frustration with different parts of Elm and how it’s developed, but elm 0.19 gave us a point at which we needed to make a decision. Our backend is in OCaml so that was a major factor too.

      1. 2

        So I’d be optimistic, if those assumptions hold.

        Isn’t the point of this to aid a one-time migration of an existing Elm codebase to OCaml rather than to be a compiler backend for Elm?

        1. 1

          Oh yeah, good point. I was thinking they’d be doing a once-off conversion to OCaml but that’s not what they’re asking I think.

          You could probably cobble together a janky script to do the whole conversion, if you really wanted a high-maintenance project :)

    2. 4

      It’s interesting, I’ve seen way more ReasonML content of late, are people still working on Elm? I’ve had a few clients come in with Elm apps, but otherwise it seems like ReasonML won out. I’d be curious to know more, if anyone has any data or the like on it…

      1. 6

        My few interactions with Evan were disappointing - he is too closed. I love the developer experience that Elm offers, but I wouldn’t trust it for the long term.

        1. 2

          The developer experience is great, when everything is going to plan, but otherwise terrible for me. Littering _ = Debug.log "x" x around is awful. The fact that there’s a known compiler bug preventing the debug mode working in some cases, and appears to be ignored (or is just under Evan’s philosophy of to not say anything) is amazing. There’s also no good/official equivalent of something like the React developer tools AFAIK too.

          1. 2

            Littering _ = Debug.log “x” x around is awful.

            Yeah, Elm could really benefit from typed holes for this kind of stuff! (OCaml/Reason also lacks these AFAIK, which is a shame)

            1. 1

              Merlin apparently has typed holes ( https://github.com/ocaml/merlin/commit/ef97ebfa23bb81c4b4b1c8fb2316a29f7052a514 ), but I’m not totally sure how to trigger it.

          2. 1

            Interesting; do you still use it often, or have moved away? was it because of that?

          3. 4

            Yes, it’s safe to say there’s high interest in Reason. The annual State of JavaScript survey backs this up: https://2018.stateofjs.com/javascript-flavors/conclusion/ –long story short, it’s something to ‘keep an eye on’ for mainstream users.

            There are other interesting data points in there. Strikingly, Elm’s ‘heard of it, would like to learn’ metric is declining: https://2018.stateofjs.com/javascript-flavors/elm/ and Reason’s is shooting up.

            1. 1

              ah, that’s a great pair of posts, thank you! it’s interesting to see the shift away like that, even if it’s likely from people who may not have used Elm first.

              I wonder if that’s just natural settling or issues folks see with it or what?

            2. 4

              Folks are still using Elm heavily, and there’s lots to like about the language. I prefer much of the language design and library design to ReasonML (although the Reason/OCaml’s module system would be super nice to have in Elm). But it just seems very hard to trust the community model. There doesn’t seem to be much work put into trying to upskill contributors to be as effective as Evan at design and implementation, and the compiler has a high bus factor.

              1. 1

                Agreed! It seems to have some nicer design points compared to Reason, and I like what it has done (tho last I seriously looked at it Signals were still a thing).

                That’s interesting re: Evan, as that’s been mentioned here and elsewhere. I wonder why that is?

              2. 2

                In case you’re interested, I reviewed Elm’s ports here: https://lobste.rs/s/1jftsw/philip2_elm_ocaml_compiler#c_prnskf

                1. 1

                  oh very much so, thank you for the link! That’s an interesting set of critiques as well… I need to try rewriting something large in Elm again to see where the edge cases are (I was originally waiting for Elm in Action to be finished and work through that, but…)

                2. 2

                  Popularity and hype are not excellent indicators of whether a technology functions well and/or is appropriate for your use case.

                  I will continue using Elm. It’s good. It works. Aside from the common “community” complaints — which I don’t care about — most people’s complaints around Elm seem to be that it makes synchronous IO hard. None of my projects need synchronous IO. I can’t think of a case where anyone needs synchronous IO. Every time I have this discussion, it goes along these lines:

                  “Elm broke us! We can’t use synchronous IO anymore!”

                  “Why can’t you use ports?”

                  “Umm…”

                  1. 2

                    Are the issues mentioned here still valid? https://www.reddit.com/r/elm/comments/7fx856/seeking_problems_with_ports/?st=jqiy249g&sh=54bfcdb4

                    • Ports are one-way only and don’t support the request/response model
                    • Ports can’t be packaged and distributed for reuse. So, ports aren’t portable?

                    Here are the issues I see from reading https://guide.elm-lang.org/interop/ports.html :

                    • They provide a very restricted window to the JS world–the more dynamic interactions you have with third-party libraries, the more complex your messages grow
                    • Message complexity and and handling logic complexity grows on the JavaScript side as well as on the Elm side–not only do you have to remember to implement the JavaScript side of things, but your JavaScript code grows along with your Elm port code. In time, you might find yourself managing quite a lot of JavaScript–and your original goal was to get rid of JavaScript and just write Elm
                    • Some of the advice and commentary there doesn’t really gel. It talks about how using ports isn’t ‘as glamorous’ as rewriting everything in Elm, but it helps when you’re trying to port an existing JS project to Elm. Well, no, I don’t have an existing JS project–I have a pure Elm project but now I need to write ports because Elm doesn’t support my use-case
                    • Apparently ‘package flooding’, the desire to contribute Elm bindings to JS packages, would be a pitfall of allowing FFI or other non-port techniques.
                    1. 1

                      The first two issues seem like non-issues to me.

                      Ports are one-way only and don’t support the request/response model

                      Ports are one-way insofar as one would use subscriptions as the other side of that coin. It’s not clear how this would not “support the request/response model”. I have written an application that did HTTP requests through ports. It’s not a difficult concept — you call the function that Elm is subscribed to in the response callback.

                      For those curious: I only needed to use ports because the server streamed back JSON, which I needed to incrementally parse.

                      As for making it awkward to call external JS math libraries — that’s the cost of safety. If you want an escape hatch from type safety, you could use Html.Attributes.attribute. Otherwise you can use ports.

                      The people complaining about this don’t understand why it has to be this way. They also don’t understand that they don’t understand, and they complain that Evan Czaplicki has an “I know best, you are wrong” attitude. I’ll address an example from this post:

                      In response to these kinds of problems, the current supported way to integrate Javascript in Elm is to use ports. Ports are often fine for side-effectful code that you do not trust. Compared to using Tasks (which compose in ways that ports do not), they can be very ugly. But without a shadow of a doubt they are very often hopelessly inadequate when it comes to pure code [12], and anyone who tells you differently is smoking something or a victim of Stockholm syndrome.

                      In the linked post, Evan assumes that if it possible to get rid of native modules from your project, then that will be an acceptable solution. It ignores the many reasons why people might not want to get rid of native code. These include:

                      • The overall architecture of my app is much better if I have this native code implementing a pure function or Task, as opposed to using ports.
                      • My current code has been thoroughly reviewed,
                      • or was compiled to Javascript/WebAssembly from a much better language than Elm,
                      • or has been subject to formal analysis techniques,
                      • or has been war-hardened by years in production.

                      I don’t smoke, so apparently I am qualified to address this.

                      It does not matter if an Elm user thinks some arbitrary JavaScript function is pure. It is fundamentally, mathematically impossible to guarantee this, probably for more reasons than I know of. One of those is the halting problem. You don’t know that a supposedly “pure” function won’t recursively call itself infinitely and blow the stack resulting in a runtime error.

                      The overall architecture of my app is much better

                      This is subjective, and context specific. If the argument is that everyone knows what’s best for themselves, I’d argue that no, they just don’t. There’s an incredible amount of stubbornness and cargo-cult in the JavaScript “community”.

                      My current code has been thoroughly reviewed

                      Elm cannot prove this, so it is irrelevant.

                      or was compiled to Javascript/WebAssembly from a much better language than Elm

                      ??? ¯\_(ツ)_/¯

                      or has been subject to formal analysis techniques

                      Elm cannot prove this, so it is irrelevant.

                      or has been war-hardened by years in production

                      Elm cannot prove this, so it is irrelevant.

                      Ports can’t be packaged and distributed for reuse. So, ports aren’t portable?

                      It is not clear to me why anyone would want to package and distribute highly context-specific glue code.

                      nb. My understanding of the word “portable” in a software context is that it’s synonymous with “cross-platform”. That doesn’t really apply here.

                      They provide a very restricted window to the JS world–the more dynamic interactions you have with third-party libraries, the more complex your messages grow

                      That’s quite necessary, and it’s not like collaborators having to adhere to a protocol is an issue specific to Elm and/or its port system. Everyone sends JSON back and forth between their server and client — I don’t hear anyone complaining about this.

                      Message complexity and and handling logic complexity grows on the JavaScript side as well as on the Elm side–not only do you have to remember to implement the JavaScript side of things, but your JavaScript code grows along with your Elm port code. In time, you might find yourself managing quite a lot of JavaScript–and your original goal was to get rid of JavaScript and just write Elm

                      ¯\_(ツ)_/¯

                      Nobody is forced to work this way. Programming is always about trade-offs. You can implement things in Elm, or you can implement things in JavaScript. I don’t really see what the issue is here. It seems like the argument is “I want to use JavaScript, but also I do not want to use JavaScript.”

                      Some of the advice and commentary there doesn’t really gel. It talks about how using ports isn’t ‘as glamorous’ as rewriting everything in Elm, but it helps when you’re trying to port an existing JS project to Elm. Well, no, I don’t have an existing JS project–I have a pure Elm project but now I need to write ports because Elm doesn’t support my use-case

                      I have no idea what this means. Why is “glamour” being used as a unit of measurement here? If anything, this would show that other people’s advice and commentary can be incredibly [and in this case, this word functions in the sense of the commenters being not credible] irrational.

                      The final “issue” I won’t comment on, because I don’t maintain a package repository and so it isn’t something I care about.

                      1. 1

                        It’s not clear how this would not “support the request/response model”

                        Let’s look at a concrete example, https://package.elm-lang.org/packages/evancz/elm-http/latest/Http#getString :

                        getString : String -> Task Error String
                        

                        This is a pretty standard example of a request/response model, a function from an input to a ‘promise’ of an output. To my understanding you can’t implement this with an Elm port. Happy to be proven wrong.

                        As for making it awkward to call external JS math libraries

                        I didn’t actually bring that up, but are you saying that Elm makes it difficult to do pure mathematical operations?

                        If you want an escape hatch from type safety, you could use Html.Attributes.attribute.

                        I don’t see how that is an escape hatch for calling JS math functions.

                        I don’t smoke, so apparently I am qualified to address this.

                        Did you forget the other criterion? ;-)

                        Elm cannot prove this, so it is irrelevant.

                        I think this is the crux of the problem. Elm is not the be-all-and-end-all of type safety. There are lots of things it can’t prove. Other languages have explicit escape hatches for these cases, look at Rust unsafe for example. I hate to say ‘people say’ but really, this is why people say Elm’s philosophy is ‘my way or the highway’.

                        It is not clear to me why anyone would want to package and distribute highly context-specific glue code.

                        Yes, the ports guide encourages writing ports in a highly-coupled way to your application, but even looking at the simple LocalStorage example, it’s clear that they could be written in a more generalized way, just wrapping the actual LocalStorage API. E.g.:

                        port module Main exposing (..)
                        import Json.Encode as E
                        port setItem : E.Value -> E.Value -> Cmd msg
                        

                        JavaScript side:

                        app.ports.setItem.subscribe(function(key, value) {
                          localStorage.setItem(key, value);
                        });
                        

                        I understand that the guide specifically discourages that, but that just means it’s possible but we’re being told not to do it. (Why? It’s not clear, but it seems mostly because Elm doesn’t allow packaging and distributing ports.)

                        My understanding of the word “portable” in a software context is that it’s synonymous with “cross-platform”. That doesn’t really apply here.

                        It was a pun :-)

                        You can implement things in Elm, or you can implement things in JavaScript. I don’t really see what the issue is here. It seems like the argument is “I want to use JavaScript, but also I do not want to use JavaScript.”

                        Well, no. If you want to write Elm, and do anything outside of its supported API surface area, you are forced to write and maintain glue JavaScript for ports. If you need sophisticated behaviour, the JavaScript side might grow quite complex with business logic to support that. In fact, the more tightly coupled it is to your Elm app, the more complex it would become. This is one reason to make ports just dumb wrappers over JS APIs, because you want Elm to own the business logic, not JavaScript.

                        Why is “glamour” being used as a unit of measurement here?

                        You should ask the person who wrote that–Evan, re: https://guide.elm-lang.org/interop/ports.html#notes

                        The final “issue” I won’t comment on, because I don’t maintain a package repository and so it isn’t something I care about.

                        This sounds like:

                        Elm cannot prove this, so it is irrelevant.

                        :-)

                        1. 1

                          This is a pretty standard example of a request/response model, a function from an input to a ‘promise’ of an output. To my understanding you can’t implement this with an Elm port. Happy to be proven wrong.

                          I don’t think this maps 1:1 exactly, but I achieve this with:

                          app.ports.foo.subscribe(function(a) {
                            // …
                            app.ports.bar.send(b);
                          });
                          

                          I didn’t actually bring that up, but are you saying that Elm makes it difficult to do pure mathematical operations?

                          No, that is not what I am saying.

                          I took that example from this post which you linked to. I was addressing the comment “This makes simple use cases, like calling out to a synchronous Javascript math library, more unwieldy than you would expect.”

                          I don’t see how that is an escape hatch for calling JS math functions.

                          window.foo = function () {
                            return Math.ceil(Math.random() * 100);
                          }
                          
                          // …
                          
                          input [ attribute "oninput" "this.value = window.foo()" ] []
                          

                          It works.

                          Did you forget the other criterion? ;-)

                          Sadly, I did spend some time living in Stockholm, so I may not be qualified to chime in after all :-/

                          Also, yes, I know what SS is. :)

                          I think this is the crux of the problem. Elm is not the be-all-and-end-all of type safety. There are lots of things it can’t prove. Other languages have explicit escape hatches for these cases, look at Rust unsafe for example. I hate to say ‘people say’ but really, this is why people say Elm’s philosophy is ‘my way or the highway’.

                          I’m not sure it’s claimed to be any kind of be-all-and-end-all either. It takes a strong position on how much safety it wants to enforce. If a developer finds it too much, there are less safe alternatives available. Personally, I want constraints and safety. I don’t want to shoot myself in the foot or struggle with runtime errors.

                          Yes, the ports guide encourages writing ports in a highly-coupled way to your application, but even looking at the simple LocalStorage example, it’s clear that they could be written in a more generalized way, just wrapping the actual LocalStorage API. E.g.:

                          I think localStorage probably ought to be wrapped, and in this case I think it’s the exception rather than the rule. I’m also not sure what assumptions tooling can make around where Elm code is being run. Should a package give you both some Elm port code and some JavaScript port handler code?

                          This sounds like:

                          Elm cannot prove this, so it is irrelevant.

                          :-)

                          I don’t intend for it to sound that way. I think the claim is that the package repository maintainers don’t want to worsen the signal:noise ratio. The claim may be valid, I don’t know. Since I’m not a package repository maintainer, I don’t have the insight to form a valid opinion.

                          1. 1

                            I don’t think this maps 1:1 exactly,

                            Right, you need two ports to achieve a simple request/response model like a -> Task b c. Why am I harping on this btw? Because this is a function (an effectful function to be exact) and functions are composeable. That’s the essence of FP and Elm breaks that model for ports.

                            It works

                            It does but it’s a classic case of what happens when the philosophy is ‘abstinence is better than protection’.

                            I’m not sure it’s claimed to be any kind of be-all-and-end-all either.

                            It’s implied by saying: ‘Elm cannot prove this so it’s irrelevant’.

                            If a developer finds it too much, there are less safe alternatives available.

                            Of course. But the claim I’m responding to right now is the one that people have no valid arguments against ports. As you can see, they do.

                            I don’t want to shoot myself in the foot or struggle with runtime errors.

                            No one does :-) but the fact remains that Elm forces you to write JavaScript for anything but the most trivial example code. Here’s an assertion: when people talk about how Elm eliminates runtime errors, look carefully at how they phrase it. I’ll bet you they say it like this: ‘We’ve had no runtime errors from our Elm code!’ Of course they’re not gonna talk about how Elm eliminates runtime errors from the JavaScript port handler code! It’s classic Stockholm Syndrome.

                            in this case I think it’s the exception rather than the rule.

                            And other people think that other cases are exceptional :-) But don’t you find it interesting that the one case you think is the exception, is pretty clearly called out by Evan himself, in the ports guide, as not a good idea to wrap?

                            Should a package give you both some Elm port code and some JavaScript port handler code?

                            That’s a good question that arises when you try to package up interop code that relies on writing JavaScript handler code. I don’t know the answer because I haven’t thought through it deeply. I don’t know how deeply Evan thought about it, but as we can see, his answer is to disallow packaging as a reuse mechanism for ports. Of course, this won’t stop people from just packaging and distributing them as npm packages. So in practice you may end up with a hybrid Elm/npm application.

                            Since I’m not a package repository maintainer, I don’t have the insight to form a valid opinion.

                            Fair enough!

                            1. 1

                              Because this is a function (an effectful function to be exact) and functions are composeable. That’s the essence of FP and Elm breaks that model for ports.

                              Fair point. Perhaps monadic IO like in Haskell would work here. Or, perhaps it would alienate less experienced developers. I don’t have a strong opinion on which of the two is more true.

                              it’s a classic case of what happens when the philosophy is ‘abstinence is better than protection’.

                              In earnest, I do not know what is meant by this and/or how it applies here.

                              the fact remains that Elm forces you to write JavaScript for anything but the most trivial example code.

                              That’s quite a bold claim, and I don’t believe it would stand up to much scrutiny.

                              It’s implied by saying: ‘Elm cannot prove this so it’s irrelevant’.

                              Perhaps I was too terse, and I should unpack what I am driving at with that repeated sentence. I mean it doesn’t matter if the user “knows” some foreign code to be safe. Elm can’t prove that in the general case. Elm makes guarantees about the safety of the code it manages, and makes no guarantees of the code it doesn’t.

                              Elm cannot make guarantees about the safety of foreign code unless it can prove that it is indeed safe. The language could of course be modified to allow for developers to arbitrarily declare some parts of foreign code as “safe”, but then Elm is no safer than e.g. TypeScript. It would then not be able to make the safety claims that it does.

                              when people talk about how Elm eliminates runtime errors, look carefully at how they phrase it. I’ll bet you they say it like this: ‘We’ve had no runtime errors from our Elm code!’ Of course they’re not gonna talk about how Elm eliminates runtime errors from the JavaScript port handler code! It’s classic Stockholm Syndrome.

                              I’m confused by this. Why would people talk down Elm for not protecting them against runtime errors in foreign code, when the tooling has never made the claim that it can do that?

                              So in practice you may end up with a hybrid Elm/npm application.

                              This is what all my projects have anyway. Perhaps I’ve been doing it wrong this whole time?

                              1. 1

                                In earnest, I do not know what is meant by this and/or how it applies here.

                                It’s an analogy referring to the ineffectiveness of abstinence-only birth control education ( https://www.npr.org/sections/health-shots/2017/08/23/545289168/abstinence-education-is-ineffective-and-unethical-report-argues ), which fell flat. But the point was that I just fundamentally disagree with the ‘disallow all unsafety’ philosophy. You can’t stop people from doing what they’re going to do, you can try to protect them though.

                                That’s quite a bold claim, and I don’t believe it would stand up to much scrutiny.

                                I’m surprised to hear that, because there’s port handler JavaScript code even in Richard Feldman’s Elm SPA starter example: https://github.com/rtfeldman/elm-spa-example/blob/c8c3201ec0488f17c1245e1fd2293ba5bc0748d5/index.html#L29 . And I’m not the only one making the claim, people experienced with Elm are saying, ‘In my experience (with 0.18), the JS part is likely to be large’ ( https://www.reddit.com/r/elm/comments/99bzf8/elm_or_reasonml_with_reasonreact/e4n83jk/?context=3&st=jqk20s8w&sh=7e54fe5e ).

                                By the way, note how The Elm SPA example port handler code above has business logic (both adding and removing data from the cache in a single port).

                                Elm makes guarantees about the safety of the code it manages, and makes no guarantees of the code it doesn’t.

                                Right, it does that by disallowing code that it can’t verify.

                                The language could of course be modified to allow for developers to arbitrarily declare some parts of foreign code as “safe”

                                No, you don’t make that code as ‘safe’, you mark it as unsafe, like Rust, so that people know where to look if things are wonky.

                                but then Elm is no safer than e.g. TypeScript.

                                There’s a wide gap between Elm-level and TypeScript-level.

                                Why would people talk down Elm for not protecting them against runtime errors in foreign code,

                                Because Elm forced them to write the foreign code. You seem to keep seeing port handler code as ‘some weird JavaScript stuff that we don’t have to worry about’, whereas it’s an intrinsic part of your Elm project, by design.

                                Perhaps I’ve been doing it wrong this whole time?

                                Perhaps! Certainly the ELM Spa example project is a pure Elm project, no sign of npm anywhere. That’s presented as the idiomatic way to write an Elm project, and yet we both know in practice you need more.

                                Edit: Richard Feldman says you don’t need NodeJS or npm to use Elm: https://news.ycombinator.com/item?id=17810088 , which at the very least deserves an asterisk and a footnote.

                                1. 2

                                  No, you don’t make that code as ‘safe’, you mark it as unsafe, like Rust, so that people know where to look if things are wonky.

                                  Perhaps we’re arguing two slightly different things here. My point was, Elm already treats all foreign code as unsafe. The guy who wrote the article I quoted from wanted a way to say to the Elm compiler “don’t worry about this part, I’ve checked that it’s safe with formal proofs/code review.

                                  It seems you’re saying Elm should have something akin to unsafePerformIO (sorry, I’ve never written any Rust) directly in the language, without the need for ports.

                                  Because Elm forced them to write the foreign code. You seem to keep seeing port handler code as ‘some weird JavaScript stuff that we don’t have to worry about’, whereas it’s an intrinsic part of your Elm project, by design.

                                  It seems this boils down to a difference of opinion on how much JavaScript there ends up being in a non-trivial real-world business application. In my experience, the JavaScript code doesn’t grow that much. You may call this Stockholm Syndrome, but I’m going to back it up with numbers.

                                  Two of my three businesses are taking revenue. I quit my day job, so these are now all I have (I say this to defend against the idea that these are “toy” projects). All three are primarily Haskell and Elm.

                                  In one of my revenue-generating businesses, JavaScript accounts for 3% of the UI code I wrote:

                                  github.com/AlDanial/cloc v 1.80  T=0.12 s (456.3 files/s, 64683.8 lines/s)
                                  -------------------------------------------------------------------------------
                                  Language                     files          blank        comment           code
                                  -------------------------------------------------------------------------------
                                  Elm                             19            243              6           3194
                                  CSS                             11            260             38           1612
                                  Haskell                         20            287            199           1578
                                  Sass                             1             38              0            191
                                  JavaScript                       2             25              0             98
                                  HTML                             2              0              0             28
                                  -------------------------------------------------------------------------------
                                  SUM:                            55            853            243           6701
                                  -------------------------------------------------------------------------------
                                  

                                  In the other revenue-generating business, JavaScript accounts for 8% of the UI code I wrote:

                                  github.com/AlDanial/cloc v 1.80  T=0.12 s (504.6 files/s, 66595.5 lines/s)
                                  -------------------------------------------------------------------------------
                                  Language                     files          blank        comment           code
                                  -------------------------------------------------------------------------------
                                  Haskell                         42            608             90           4252
                                  Elm                             14            196              0           2357
                                  JavaScript                       1             48              0            196
                                  Markdown                         1              5              0             25
                                  HTML                             1              0              0             10
                                  -------------------------------------------------------------------------------
                                  SUM:                            59            857             90           6840
                                  -------------------------------------------------------------------------------
                                  

                                  I’m not suggesting the small amount of necessary JavaScript code isn’t something people should worry about.

                                  1. 1

                                    It seems you’re saying Elm should have something akin to unsafePerformIO (sorry, I’ve never written any Rust) directly in the language, without the need for ports.

                                    Right, basically a section of the code you can jump to, to look for potential issues.

                                    In one of my revenue-generating businesses, JavaScript accounts for 3% of the UI code I wrote

                                    I see, thank you for providing that analysis. I personally don’t have any Elm apps, but I took a look at Ellie as a ‘representative sample small app’ and it seems to have six ports: https://github.com/ellie-app/ellie/search?q=app.ports&unscoped_q=app.ports . Let’s look at one of these ports: https://github.com/ellie-app/ellie/blob/45fc52ef557e3a26162b33b950874002b43d9072/assets/src/Pages/Editor/Main.js#L25

                                    This looks like a catch-all logic port for doing various top-level operations. It’s quite clever, in the way that it uses a JavaScript array to encode a sum type. But again here’s my problem. This is quite a significant chunk of logic, in JavaScript. I have to test this, and update it in parallel with the Elm code. I get no help from Elm’s compiler while I’m doing that. So let’s say realistically, port handler code is somewhere around 5 to 10% of a typical Elm project. It’s likely to be the most stateful, effectful, and complex code in the project.

                                    Here’s another thing that goes to your initial question, about people not being able to come up with good reasons to not use ports. The thing is, they did, e.g.: https://www.reddit.com/r/elm/comments/81bo14/do_we_need_to_move_away_from_elm/dv3j1q3/ . But often they did it in one of the forums controlled by the Elm mods, who very quickly shadowbanned those posts and their entire discussion threads from visibility. They did it, ostensibly to avoid flamewars and hurt feelings. But a side effect is that a lot of valid criticisms got swept under the carpet as well. Elm’s community management does this, effectively–it hides valid criticisms and gives people the impressions that everything’s good.

                                    That post I linked above, and its surrounding thread, from ten months ago–all the criticisms of ports are there. Exactly as I came up with in the past couple of days during our discussion. But I bet you never saw that post before, or if you did, you quickly forgot about it. Out of sight, out of mind.

                                    Edit: look at the overall Philip2 discussion thread, btw. You’ll see it’s hidden by 4 users (maybe more by the time you read this). Want to bet whether they’re Elm users or not? :-)

                                    1. 1

                                      But again here’s my problem. This is quite a significant chunk of logic, in JavaScript. I have to test this, and update it in parallel with the Elm code. I get no help from Elm’s compiler while I’m doing that. So let’s say realistically, port handler code is somewhere around 5 to 10% of a typical Elm project. It’s likely to be the most stateful, effectful, and complex code in the project.

                                      I’m not sure what you suggest as the solution to this. Ideally, you’d implement whatever you can in Elm. Where that’s not possible, you can use ports. JavaScript being unwieldy and dangerous is hardly an argument against Elm.

                                      It’s not reasonable for you to criticise Elm for not managing the code that it explicitly states it will not manage for you.

                                      But often they did it in one of the forums controlled by the Elm mods, who very quickly shadowbanned those posts and their entire discussion threads from visibility. They did it, ostensibly to avoid flamewars and hurt feelings. But a side effect is that a lot of valid criticisms got swept under the carpet as well. Elm’s community management does this, effectively–it hides valid criticisms and gives people the impressions that everything’s good.

                                      I don’t agree with this either. Let’s take a look at that user’s comments (emphasis mine).

                                      Asking people to write their own port implementation every time they want to use LocalStorage is insane.

                                      Typical Elm-speak for “this is fine” while the entire room is on fire.

                                      I understand what ports are and how they work. They suck.

                                      Frankly, the guy is being a total asshole. A petulant child. This is not valid criticism, it’s one prima donna saying that everything is terrible because he can’t get his own way.

                                      I would have banned him too.

                                      It’s tiring enough for me to hear people make the same arguments over, and over, and over again. “I want to run unsafe code from anywhere!” “No.” “But reasons reasons reasons! Screw you, community! I’m leeaaavvviiinngggg!!!”

                                      …And I’m not even a maintainer!

                                      look at the overall Philip2 discussion thread, btw. You’ll see it’s hidden by 4 users (maybe more by the time you read this). Want to bet whether they’re Elm users or not? :-)

                                      To be honest I wouldn’t be surprised if they weren’t Elm users. You should know that programming is mostly pop culture these days, especially in the front-end world. Everybody cargo cults and adopts a technology as their own personal identity.

                                      I think it’s pretty unfair of you to project malice onto other people based on the technology they choose to use.

                                      1. 1

                                        I’m not sure what you suggest as the solution to this. … It’s not reasonable for you to criticise Elm for not managing the code that it explicitly states it will not manage for you.

                                        Look, I’m not here to criticize Elm and provide armchair solutions to all its problems, Elm is a great language, I’m just replying to your original assertion that people can’t come up with good arguments against ports. The fact is that ‘Elm explicitly makes you write JS for interop’ when the alternative could be that you could bind explicitly typed Elm values to existing JS code, is a valid criticism of ports.

                                        This is not valid criticism, it’s one prima donna saying that everything is terrible because he can’t get his own way.

                                        Really? The criticisms he made included that ports can’t be composed, which you’ve already acknowledged. But you don’t acknowledge it if it’s put in the wrong tone? I get it, those words can be hurtful. They shouldn’t have said it like that. I don’t think it takes away though from the strength of the arguments though. People expressed similar thoughts in the thread. Those comments got plenty of upvotes. Not everybody spoke up, but they did vote up.

                                        “I want to run unsafe code from anywhere!” “No.” “But reasons reasons reasons! Screw you, community! I’m leeaaavvviiinngggg!!!”

                                        From my perspective, it was more ‘I want to run unsafe code.’ ‘Why would you want to do that? You haven’t understood Elm, you need to rearchitect.’ ‘I need it because of X, Y, Z.’ ‘That’s not valid and we’re not going to support that.’ Then people getting frustrated and speaking rash words. Rinse and repeat. It’s not fair to either side, I think.

                                        I think it’s pretty unfair of you to project malice

                                        OK, in retrospect, I shouldn’t have added the last bit. I wasn’t projecting malice, but more a stubbornness to not see criticism. But I guess that’s more a human quality than an Elm community quality, so it’s not really valid. I apologize, and retract that.

                    2. 1

                      Popularity and hype are not excellent indicators of whether a technology functions well and/or is appropriate for your use case.

                      Well, I wouldn’t argue that, I just noticed that there seems to have been a shift from hype about Elm to hype about Reason. I’ve used both and like both, so I was curious what others were seeing here, yknow?

                      Every time I have this discussion, it goes along these lines

                      yes, these are good points. is it education based? like the Elm in Action book is… still going. I remember hearing the same arguments about Node way back when, and those mostly died off as folks figured out how to do things.

                      1. 1

                        I couldn’t say definitively, but pessimistically I suspect people are resistant to change. Like “Oh, this is how I’m used to things working in JavaScript; why can’t it just work the way I expect.”

                        I can empathise — I remember on multiple occasions wanting to use unsafePerformIO when I knew less in Haskell. I also recall being frustrated when several experienced Haskellers on IRC told me not to do that.

                        1. 2

                          Again, I’m not sure which complaints or discussion you’re referring to. It would be helpful to provide some links or quotes. Of course there will always be some people who want to do things with a tool that it wasn’t meant to do. But that doesn’t account for all of the recent frustration. Here’s an example that might help you understand better:

                          My company uses an app-shell architecture for the UI and uses hash routing to route individual apps. Hash routing was supported in 0.18 and is not supported in 0.19. Upgrading to 0.19 means a non-trivial architectural change for all of the apps. We can’t justify the cost of making that change, so we won’t upgrade to 0.19 unless hash routing is supported again. The specifics are described here: https://github.com/elm/url/issues/24

                          1. 2

                            Can’t you just copy the code of the old parseHash and vendor it with your codebase in 0.19? At first glance, it seems to be written in pure Elm, so no Native/Kernel code required that would preclude such a copy? edit: also, first page of google results for me seems to show some workaround

                            1. 1

                              The point is not that there aren’t workarounds. And even if we were to use a workaround, it would still be hours of work because we have to update, test, and deploy every app that uses hash routing (which is around 20). The point is that there are legitimate frustrations related to breaking changes in 0.19. I offered this example as evidence that a frustrated user should not be assumed to be incompetent, e.g. “Oh, this is how I’m used to things working in JavaScript; why can’t it just work the way I expect.”

                              1. 1

                                I don’t really understand the part about “assuming incompetence”. Other than that, I do understand that migration from 0.18 to 0.19 is a non-zero cost and thus needs to be evaluated, and accepted or rejected depending on business case. I only replied because I understood the last sentence as if parseHash was the only thing stopping you from upgrading. That made me curious and confused, as on first glance, this seemed to me easily enough resolved that it shouldn’t be a blocker. Based on your reply, I now assume I was probably just misled by the last sentence, and there’s more missing than just parseHash for you; or otherwise maybe you generally just don’t have resources to upgrade at all (regardless if it is Elm or something else; React also has breaking changes on some versions I believe). Though I may still be wrong as well.

                      2. 1

                        Can you provide a link to someone making a complaint about synchronous IO? I’ve read a lot of the user feedback and discussion about Elm 0.18 and 0.19, and I’ve never seen anything about synchronous IO. I’m curious what the use case is.

                        1. 1

                          This article was widely discussed when it was published: https://dev.to/kspeakman/elm-019-broke-us--khn

                          There are two complaints:

                          1. No more custom operators — solved easily enough with a find/replace across the project.
                          2. No more native modules — this is what I’m referring to when I say “synchronous IO”.

                          In Elm, IO effects should all be run asynchronously. Any effects not modelled by an Elm library should go through ports. Despite having asked many people, I’ve never seen a clear answer for why any given problem can not be solved with ports.

                          1. 1

                            Now I understand. Yes, I was also surprised by the amount of people who depend on native modules. But not every use case is related to effects or impure function calls. Sometimes native modules are used to wrap pure functions that haven’t been implemented in Elm. One example is currency formats. Not every currency uses the same numeric notation (number of places to the right of decimal varies), so you need a big dictionary to map currency to format. Last time I looked (it’s been awhile), this doesn’t exist in Elm. Of course, you could use a port for this and you could implement this is in Elm, but both incur a complexity cost that isn’t clearly justified.

                            Here’s another example of when using a port is not clearly the correct answer: https://github.com/elm/html/issues/172. There’s a legitimate use case for innerHTML, which has recently been disabled, and using a port means that you have to do a lot of accounting to make sure the inner html isn’t clobbered by Elm.

                            Elm’s inner circle has lately recommended using custom elements. To me, this is essentially a tacit acknowledgment that some form of synchronous foreign function interface is called for. The argument has always been that FFI is unsafe. But custom elements, which run arbitrary JavaScript, are just as unsafe. And they’ve been used to wrap libraries like Moment, not just to create custom DOM elements. So they are essentially foreign functions that you can’t get a return value from. The same people encourage the community to use a less-useful form of FFI on the one hand and respond with very opinionated criticism of full FFI on the other hand. This is the kind of thing that causes frustration in the community.

                            1. 1

                              Here’s the previous discussion (I was curious about it).

                      3. 1

                        What was the motivation for switching away from Elm?