ELM setup in the server

How are you supposed to setup ELM in the server? What hosting options are available? Does ELM uses Node.js? Sorry for the newbie questions. Couldn’t find it in the docs.

Elm currently only targets the browser. That being said there are ways to execute Elm on the server using e.g. a https://package.elm-lang.org/packages/elm/core/latest/Platform#worker but you will be constrained to ports and possibly modified XmlHttpRequest. Take a look at https://package.elm-lang.org/packages/the-sett/elm-serverless/latest/ for an example. The same principle applies if you want to deploy inside express or similar.
If you search for “serverless” here in the fortum you’ll find more info.

One option to set up a server programmed in Elm is using Elm-fullstack (You don’t have to include a frontend, can use it for server only).

This framework includes an HTTP server, and it maps HTTP requests to Elm types so that you can handle them using Elm code. You can see an example illustrating this at https://github.com/elm-fullstack/elm-fullstack/blob/3f17943b44c61182dcfd6eb3f75b7e65fa6badec/implement/PersistentProcess/example-elm-apps/default-full-stack-app/elm-app/src/Backend/Main.elm#L22-L50

I found one of the more valuable aspects of using such a framework is not having to worry about the mapping between a database and types in my application. Application code to connect to a database is not necessary because the framework automatically persists the state of the Elm app. Or put in other words: The type you use in the update function of your server Elm app is your database. More details on how this works at https://github.com/elm-fullstack/elm-fullstack/blob/3f17943b44c61182dcfd6eb3f75b7e65fa6badec/guide/persistence-in-elm-fullstack.md

There is a docker image containing the whole framework. Building on that, you can set up your server by adding the Elm modules of your server application on that.

Documentation is sparse at the moment, but I am glad to help if any questions come up.

3 Likes

You can find some simple demos for elm-serverless here, along with reasonably detailed instructions on how to go about deploying them as AWS Lambda functions:

===

For a simple Node.js program without the above frameworks, all you need is a Platform.worker as opposed to one of the Browser program types:

https://package.elm-lang.org/packages/elm/core/latest/Platform#worker

Elm uses XMLHttpRequest underneath elm/http, so include the npm package xhr2 if you want to use that.

1 Like

Sorry to be somewhat off-topic here, I’m unsure as to the correct etiquette here, direct message, start a new thread, or simply reply here and let the topic become what it may, which is what I’ve opted for.

Anyway, I’m interested in elm-fullstack. My main question is, what happens when the type of Message in your Elm application changes? Or do you have to make sure that your Message type is at least compatible with all currently stored messages (I understand that you truncate the list of messages).

1 Like

When you change the message type in Elm-fullstack: You don’t have to make sure it is still compatible with stored messages because you can explicitly instruct the framework to set the application state to a new value. Once you do that, older messages from history don’t need to be considered anymore to restore the state.

The recorded history contains not only the messages which went into the update function but also these kinds of ‘meta’ events. So when you implement your type change, you can add an event to the history to set the app state when the types change.
(This is the same kind of event generated as when you use the HTTP endpoint at /elm-fullstack-admin/api/process/state to set the app state.)

This way of handling a migration seems not ideal, also because just by looking at the event in the history, we cannot tell anymore if it was done to perform a migration or for some other manual intervention. It would be nicer to have functionality specific to handle atomic migrations, including cases where the types change.

1 Like

Okay thanks for the informative reply.

@Viir elm-fullstack looks pretty interesting - there are some similarities with Lamdera too? https://www.youtube.com/watch?v=nSrucNcwlA8

One question, do you have to be careful not to have any functions in the Model? Since this periodically gets saved as a ‘reduction’.

Very interesting talk. Watching this, I see similarities. Especially when considering the title of this thread here: It says ‘in the server’, but one of the motivations for Elm fullstack was to benefit from sharing implementations between frontend and backend implementation. As far as I understand the talk, this is also the case for Lamdera.
And then, Mario also mentioned how the implementation of ‘Persistence’ for a full-stack solution could become exhausting with older approaches. From today (mainstream) perspective, where many popular approaches separate database and application server (separate compilation units resulting in additional interfaces), Lamdera looks very similar.

Yes, you have to be careful, at least at design/planning time, because for now having a function in that Model is not supported.
At implementation time, maybe you don’t have to be as careful since such an incompatible type will fail the compilation of the backend. The type tree for the backend state is parsed at compile time to generate the code to serialize the reductions.

Interesting, I wasn’t sure if it was necessary to manually write Decoders/Encoders or if the serialization is automatic.

What happens if there is an opaque custom type in the backend state? How are you able to automatically generate serialisation for that? Or does the backend serialisation not involve generating Decoders/Encoders, but perhaps works more directly on the javascript side (by modifying the Elm runtime or …)?

In the instances I have seen, this opaqueness only has the function to prevent accidental use of a constructor/tag of this type (outside the defining module). But to serialize the backend state, I don’t need this opaqueness, so I ignore it during compilation. But how can I tell the Elm compiler to ignore this? I don’t expect this will be possible using the standard binaries. The next best option seems to create an intermediate representation of the Elm code with additions in the exposing syntax. This compilation step is not implemented yet, so today, you would have to expose the tags in the application code.

Currently, it works by generating Encoders and Decoders in Elm and then using the official Elm compiler from there. I did not invest much into things specific to javascript since I am not sure how long the backend will run on javascript.
There is also a command-line option to save this intermediate Elm code to files. This helps understand what the framework does by looking at the diff between application code and intermediate compiled Elm code.

To summarize, following is an overview of today’s process to get from Elm code to running app. It is heavily based on the Elm compiler we can download in form of executable files from https://github.com/elm/compiler/releases:

  • Assert the file tree is a valid Elm app (compiles): Invoke an operating system process to run the standard Elm compiler from an executable file.
  • Save us from maintaining serializing functions: Parse Elm types in the backend state model tree. Add a new Elm module with serializing and deserializing functions and the entry points. Add a main function to support the dead-code elimination by the standard Elm compiler.
  • Compile to javascript: Invoke an operating system process to run the standard Elm compiler from an executable file.
  • Some minor changes in the javascript. (Global variable for the state, name functions)
  • Run in a javascript engine.

That is what I was thinking - you could add in generated decoder and encoder to the modules exposing list:

module Whatever exposing (Opaque, opaqueDecoder_g283gsh, opaqueEncoder_g283gsh)

type Opaque = 
    Blah

opaqueDecoder_g283gsh : Decoder Opaque
opaqueDecoder_g283gsh = 
    ...

opaqueEncoder_g283gsh : Opaque -> Value
opaqueEncoder_g283gsh = 
    ...

The _g283gsh bit would be some hash of the name Opaque to try and prevent a clash with existing functions that might already be in the code.

1 Like

Thank you for the idea Rupert! Looks like I can implement support for opaque types soon. :smile:

Edit:
Support for opaque custom types is now implemented. You can use them in the backend starting with version 2019-12-16.

1 Like

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.