1. 12
  1.  

  2. 11

    The more you tighten your grip, Elm, the more users slip through your fingers.

    Note that this sort of subversion is essential to Guix, Nix, or any other tool which aims to treat packages as capabilities. Language-specific package managers need to justify their inflexibility, specifically why they cannot invert control of the packaging namespace and be composed with some bigger system.

    1. 1

      Are there any language-specific package managers that play nice with system packaging managers?

      1. 2

        Hmm, what would playing nicely entail? The problem with Elm is less that the package manager doesn’t play nice, it’s more that the compiler can’t easily be decoupled from the package manager. So maybe the question is rather, are there languages with integrated build tools that present a clean interface to the build part separately from the package management?

    2. 5

      I admire the effort but these restrictions in Elm seem impractical. This is why I prefer Reason. All of the static typing + FFI escape hatches when needed.

      1. 1

        How important is synchronous IO to you? I’ve built a few business applications with Elm and all IO is done asynchronously, through ports when necessary. It works fine. Why doesn’t this work for you?

        1. 1

          I’m curious, how would you solve the specific issue here? To localize all the timestamps in your model, send out a Map Id Timestamp via a port, wait to receive a Map Id String back, merge it back into your model, and then switch out the displayed model?

          1. 1

            Yeah, something like that. I’d also delegate anything to do with time formatting to moment.js.

            FWIW, I really enjoyed the hackery described in the article — I love that kind of thing, but I don’t agree that Elm’s restrictions make it at all an impractical language.

          2. 1

            It’s not just IO though–right? To my understanding every single JavaScript call needs to go through that async boundary. E.g. if I’m binding to a function:

            function exclaim(message) {
              return String(message) + "!";
            }
            

            This isn’t doing any IO but it has to pass through an async IO layer. In Reason (or TypeScript/Flow/PureScript for that matter) I can type this directly as string => string.

            1. 1

              Right, but then Elm doesn’t know that. Elm wants to promise no runtime errors, but if you can inline potentially unsafe JavaScript code then you can’t keep that promise. What happens in those other languages when that pure function causes a runtime error?

              You could argue that good programmers should be disciplined enough to not make silly mistakes like that, but languages take a position on a spectrum between affordances and a certain kind of safety, and Elm is further along to one end of the spectrum than the other languages you mentioned.

              1. 1

                Even in haskell, a pure function can fail to allocate memory.

                1. 1

                  That is true, but I am not sure what point you’re making.

                2. 1

                  That’s all fine (although it’s arguable that Elm is ‘purer’ than PureScript), but my point is that it’s not just about IO. Specifically I’m talking about your previous comment:

                  How important is synchronous IO to you? I’ve built a few business applications with Elm and all IO is done asynchronously, through ports when necessary. It works fine. Why doesn’t this work for you?

                  I want to recognize the fact that you’ve accepted that it’s not about IO, it’s about Elm’s desire to eliminate all runtime errors. Btw, that is a worthy goal of course. In the other languages the most we can say is that we want to eliminate runtime type errors. Which, depending on how you look at it, can be quite a lot.

                  1. 1

                    Well, the context of the post was doing something in IO.

                    As for your example, I understand it’s a trivial amount of code just for the sake of example and that anyone would just reimplement it in Elm. You could make the case that there are some pure functions that are already written in JavaScript and are too expensive to reimplement in Elm, but in practice I have never seen this, and I can’t even imagine what that function might look like.

                    1. 0

                      Well for example, https://github.com/Qix-/color

                      The equivalent in Elm seems to be https://package.elm-lang.org/packages/avh4/elm-color/1.0.0/ , with the message:

                      Future versions of this package will include additional common conversions and color manipulation functions.

                      1. 1

                        Perhaps we have different ideas of what “too expensive” means. Personally, I don’t think porting a few hundred lines of JavaScript is a big deal. If I needed those missing colour functions, I would just write them.

                        1. 0

                          Maybe we do, because that’s one library. There are tons of useful, pure modules that you would have to rewrite and lock into the Elm ecosystem, maintaining them when bugs are fixed or new features added in the source JS versions. It’s not sustainable.

                          1. 1

                            And how many times have you specifically encountered this in your specific business? Like I said before, I have never seen this in practice. Arguing in hypotheticals this way is not constructive.

                            1. 0

                              Encountered what exactly? Wanting to use a third-party library and not wanting to rewrite it? Everybody wants to do that, that’s why FFI exists. Languages boast how easy their FFIs are to write.

                              Anyway I simply gave you an example that you couldn’t imagine existing:

                              You could make the case that there are some pure functions that are already written in JavaScript and are too expensive to reimplement in Elm, but in practice I have never seen this, and I can’t even imagine what that function might look like.

                              Now you want to know exactly how often it happens. OK. It’s often enough that it’s repeatedly been an issue for people using Elm. You just don’t know the full extent of it because those kinds of threads are quickly shut down in the Elm discussion forums.

                              1. 1

                                Have you specifically encountered a specific situation in specifically your business while writing an Elm application where you wanted to use some existing JavaScript code but found it not financially viable to port that existing JavaScript code to Elm?

                                You say “everyone wants to do that”. In the tens of thousands of lines of Elm I’ve written spanning multiple projects which make up 100% of my living, I have never encountered a case where some incidentally-pure JavaScript code was not financially viable to port to Elm. For those times I’ve needed FFI, Elm’s port system has worked just fine.

                                Since you cannot be specific and point to genuine pain-points you yourself specifically have experienced, and you refuse to stop arguing in generalities and hypotheticals, it is clear you are arguing in bad faith and no further discussion is warranted.

                                1. 1

                                  I don’t understand why this is about me specifically? Do I personally need to be using Elm to make a living to understand that it has limitations? Presumably you’ve been in the Elm community for a while. You’ve seriously never heard of anyone’s frustrations with Elm FFI? Or you have and you refuse to even acknowledge that people might have had issues?

                                  Some concrete examples:

          3. 3

            IIRC the reason you can’t officially write native code in Elm anymore(!) is that it might give a bad user experience if they crash, giving Elm a bad name. Is this still the official reason, or is there any hope of improvement?

            1. 4

              The official reason is because it interferes with dead-code elimination. Without native code they can eliminate all unused functions in all modules.

              1. 1

                That, and if there is ever the decision to retarget to webassembly instead of javascript, you kinda get all the libraries working for free.

                1. 1

                  That seems a lame excuse on Elm’s part. Webassembly can call out to JS just fine. Takes a bit of work to generate bindings, but, that’s what compilers are for.

            2. 2

              When making it a Task, wouldn’t that require going through the similar “message-based” call style as for ports? I’d think making it more deterministic and taking an arg with timezone (pre-retrieved from browser during init) would feel nicer - unless there’s no api for that in js? Though I understand it’s more of a tech demo; thanks a lot for this, just couldn’t resist discussing ;P

              1. 1

                Note that the Task is just about fetching the formatter, not about calling the format function itself. Fetching the formatter is indeed port-like, though. We could think of it as a

                fetchFormatter : Task x (Posix -> String)
                

                And yes, it would be straightforward to implement what you’re suggesting:

                import Time
                
                {- this gives us
                type Zone
                here : Task x Zone
                -}
                
                -- same thing for the user locale
                type alias Locale = String
                fetchLocale : Task x Locale
                fetchLocale = -- implemented as a native function
                
                -- and two pure functions for the rest of the work
                -- (a fully featured module would also expose all those
                -- formatting options in Intl.DateTimeFormat)
                numericFormat : Locale -> Time.Zone -> DateTimeFormat
                format : DateTimeFormat -> Posix -> String
                

                Second yes, we could also fetch the time zone and locale from Javascript via startup flags or ports, except that it would be hard (impossible?) to use the Time.Zone type since that’s opaque. Using ports would have the advantage of reducing the amount of native code we need. Using flags would have the advantage of avoiding the explicit initialization state and related logic to get the zone and locale into the model.

                1. 2

                  Ahhh, I didn’t grasp that it’s a Task fetching a function, thanks!

                  As to the rest, I think I overestimated my Elm familiarity, and it flies over my head; and/or it may be just that it’s late Friday here already… thanks for the reply anyway, and sorry for being too dumb! :)