Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

We are doing a fairly large Elm app right now. And especially the refactoring reasons and static type system are appealing to us when you work in a team.

Also being a functional language it is easy to crank out features in an afterthought, e.g. we added an Undo/Redo feature for operations the user does in this single-page client app. And being side-effect free this "merely" boils down in wrapping function calls in an "undo/redo" function.

So Elm is really exciting stuff. But still the Api is not completely stable, yet. So you have to invest some time to adapt to these (small) Api changes. Also interoperablility with so-called "Native" JavaScript browser functions, e.g. JavaScript typed arrays is possible but not finalized, yet.



Are you using ports much? The examples in the documentation aren't very clear, and I'd be interested to know how much plumbing is required to talk to external Javascript in real life.


It's not much.

On the Javascript side, the Elm `app` is like an event emitter where you listen for updates and send it data.

    var app = Elm.Main.embed(...)
    app.ports.gameState.subscribe((data) => ...)
    app.ports.tileClick.send([x, y])
On the Elm side, you subscribe to incoming ports and map them into Msgs. And you emit to ports with Cmds from your update function.

    -- Ports.elm
    port gameState : Json.Encode.Value -> Cmd msg  // Outgoing
    port tileClicked : ((Int, Int) -> msg) -> Sub msg  // Incoming

    -- Main.elm
    update msg model =
      case msg of
        Tick ->
          -- Define `encode` to Json.Encode the game state
          (model, Ports.gameState (GameState.encode data))
        TileClicked (x, y) ->
          -- Handle tile clicks sent in from Javascript
          
    subscriptions model =
      Ports.tileClicked TileClicked


Yes, but just like the ones in the docs, that's a nearly trivial example, and so doesn't demonstrate the complexity.

What I'm concerned about is the case when I want to call a Javascript function from deep down inside, say, a computation function. Do I have to split my computation into two halves, one which sends the event which calls the Javascript function, and another which receives the result of the function? How do I pass the in-progress computation state from one to the other? Normally in a functional language I'd get around this by simply passing a function reference into the Javascript function which the handler would then call, so allowing me to have both parts of the computation in the same place and operating on the same data, but apparently in Elm function references don't survive being passed through Javascript.

Plus, each port only has a single incoming event, so if I'm calling the Javascript function from multiple places I can imagine it can very easily turn into a labyrinth of state passing code.

How do you avoid this?


Some interactions with 3rd party libraries (like rendering stuff, leaflet.js, pixi.js) fall along natural event-driven faults, like the game update tick finishing, something managed by Elm being clicked, that sort of thing. In these cases, ports (events) are the obvious fit.

Then there's synchronous 3rd party library stuff where you just want to execute a Javascript function, like a math function that you aren't going to reimplement in Elm. In that case, I wrap the function with a native module and call it like an Elm function. Since you talk of computation, is this what you're talking about?

The Elm community highly discourages native modules. For one thing, they aren't very well documented and the API seems to have recently changed. But I'm not sure what the alternative is.


Ah, right --- I have heard of native modules, but only in the context of things I shouldn't using. I'll look more closely. Ta!


From what I can tell, basically all of the reasons you state for using Elm are also present in Scala.js.


Elm has a sound type system, no compromise and is purely expression based. Scala, not quite. You can still have runtime exceptions in "normal" situations, you can still have statements that aren't expressions, etc.

Scala gives you the options to do it right, but Elm enforces it. Heck, because of how the application type works, its pretty much impossible to make an Elm app that doesn't follow proper architecture. It just won't compile.


Definitely, but I see that flexibility as a good thing. I can either depend completely on the type system at compile time, or I can move some things to runtime, depending on what I'm writing and how much time I have to complete it. The boiler plate introduced by immutability and pureness can become unwieldy in large projects from what I've read.

Another cool thing is the option to use either a pure expression-based functional approach or a OOP approach, or even a mix between the two.


Yup, I know :) it's a matter of opinion. I was just saying that that's a significant difference that will make a segment of engineers pick something like Elm. Elm is also more minimalist and easier to pick up if you're not into FP. Even knowing Haskell, some Scala types make me go cross eyed.

On my side, if i have the choice between boilerplate or having to debug runtime errors, I'll pick the boilerplate any day. If my product manager thinks its worth it to push a feature earlier (and risking having it blow up), well, I'll go work for someone else ;)


> Also being a functional language it is easy to crank out features in an afterthought.

I am a big fan of functional languages, but I don't take for granted that they allow us to crank out any additional feature easily. The ease of adding of undo/redo functionality is a common selling point, but it seems to fall out directly from a preference for immutable data.


> We are doing a fairly large Elm app right now. And especially the refactoring reasons and static type system are appealing to us when you work in a team.

Do you miss this in the backend with Elixir?


The backend is not done in Elixir, but Go.

I would love to do it in Elixir with Phoenix, but this would mean the team has to learn another tool. Go is really easy and fast to pick up. Here the Go Toolchain/Ecosystem matters, which is easy to grasp, and you get a type system, which is not as sophisticated as in Elm, but fits the job.

An older project was done with Grails/Groovy. This was fun and fast to iterate. But after a while, when you come back to the old project code, it is hard to get the dynamic types into your head. So actually I would prefer a static type system. I know there is some tool for Elixir to handle this. But I did not look into this.




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

Search: