Do you have an opinion/advice on functional backends technologies?

By defining all the side effects as Procedures, and then chaining them together with Procedure.andThen:

https://package.elm-lang.org/packages/brian-watkins/elm-procedure/1.1.0/Procedure#andThen

This is basically Task ports. :slight_smile:

I think also this is closer to IO Monads in Haskell?

3 Likes

At work we currently have a combo of Elm + F# + Hasura/GraphQL (and some other stuff as well but that’s legacy for you). We have been moving over to F# from TypeScript on the backend for almost 2 years and now the majority of our code is F#. IDE-wise experience is fairly good, but mixed, people tend towards JetBrains Rider over VSCode Ionide (even though that works too). GraphQL gives you shared types in Elm. F# gives you type providers.

If I could pick something else it would be that mature statically typed BEAM language that does not yet exist. :slight_smile: (be it Alpaca, Caramel, Whatsapp’s yet unreleased thingy, Hamler or Gleam. Elchemy seems pretty dead in the water tbh)

I was also on lambda + dynamodb (Http.task) with choonkeat/elm-fullstack

But recently our startup is using elm-fullstack with Postgres (through Hasura), so we get to keep our Http.task chaining.

my thoughts exactly. lambda function environments doesn’t have traditional server side affordances, but then with elm you don’t have those apis anyways, so you’re not missing out lol

Do you have any example code using this? Would love to see how it looks with Task chaining.

Thanks for starting this thread – it would be great to see further developments in this area (particularly as it pertains to Elm integration).

I played around with an Em + Haskell servant with servant-elm + Haskell persistent backend for SQL (from the Yesod folks) stack last year.

Similar to some of the things mentioned above, I wanted to try to write data and API definitions once, and have that serve as the basis for:

  • automatically creating the SQL backend with type-safe bindings (I used Sqlite, but persistent works with Postgres among others)
  • automatically generating the Elm decoders on the front end

It works as a proof of concept (the various Hello Server examples in this repo are fairly straightforward, to get a taste). A few caveats:

  • Even with simple data definitions, I had to write some custom generators for Elm code (if there was a way to do this more directly in servant-elm or underlying elm-bridge, I couldn’t work it out from the docs :frowning: )
  • The monadic state gets a bit complex (at least for my intermediate Haskell level – the composition of monads for server + storage is hard to follow beyond basic usage)
  • servant is not hard to get started with (much easier than Yesod, I gather), but its state of support seems suspect, with a backlog of issues and PRs piling up; and things like cookies are not supported out of the box, though folks have written modules for it

Really? And here I wrote my own http look-alike; I guess I must have never actually tried the default package.

You need to have an XMLHttpRequest implementation to work with http 2.0.0 on the server side. Such as this:

Busy week, sorry for not getting back sooner.

Thanks all for the valuable feedback, I’ve got many avenues to explore now.

I’ve been mainly doing research since creating this post, not much coding yet. So no specific questions related to previous replies yet.

So as I said, I like the idea of a language constraining what one could do, not allowing or making difficult to shoot oneself in the foot. And, although I’m barely scratching the surface, I can feel this is what Elm will give me…to be continued :wink:

Backend wise, I’ve been thinking there is another option I hadn’t considered: just applying functional programming concepts! (doh!)

I feel this requires quite a bit of discipline though, one could also translate that to just “be a better programmer”.

Anyhow, here’s an interesting video demonstrating this concept in a practical way: https://youtu.be/kPLGftyQ5ho

That’s JavaScript, and the applied concept is “functional core, imperative shell”.

Having first class functions, it feels pretty natural in js. I anticipate this technique will feel a little clunky in Java or Ruby but still doable (not considering performance issues though). Throwing typescript into the mix could turn out to be interesting refactoring/tooling wise, I’ll have to try it.

Staying on the topic of a functional backend, from what I gathered, the Elixir community seems to be much much more active than any other. There’s a ton of resources available.

Interestingly, I stumbled upon this video where the speaker applied a similar technique to the one mentioned above: isolating a statically typed functional core component, implemented with the Gleam language (which looks lovely) on a Phoenix/Elixir/LiveView backend : https://youtu.be/UCIcJBM_YDw

Si maybe just having a little more discipline is what I’m looking for, not sure yet.

Having so much resources available means I’ll probably dig into the Erlang/Elixir ecosystem next. Kinda makes sens for my use case and background. Also considering the perspective of a better BEAM language, which looks like it could happen.

For completeness, I didn’t mention that I started integrating the sorbet gradual static type checker to my ruby code base. Definitely helps and glad this project exists. But it’s quite a lot of work to this after the fact, much better to consider static typing from the get go. And tooling is a little rough.

Anyways a little of topic again :wink:

Please do keep posting suggestions if you have any though. I’m also interested in feedback on experiences with switching technologies, etc.

Purescript didn’t get a mention and looks like it could be an interesting option with a nodejs backend. Has anyone dabbled with it?

1 Like

Here’s also an excellent talk from a Clojure programmer, demonstrating how to apply FP principles to a js code base: https://youtu.be/vK1DazRK_a0

It was this video that triggered my realization mentioned in my previous post, but couldn’t find it anymore.

I’m posting it for reference, definitely will try to apply this knowledge.

I found the highlighting and isolating of mutations to be an eye opener.

Hope it’ll be useful to others too.

Redacted a bunch but should work as example

First, requests from our SPA arrive as a lamdera-style MsgFromClient, which I immediately ship off to a function elsewhere to detail.

updateFromClient : RequestContext -> Time.Posix -> MsgFromClient -> ServerState -> ( ServerState, Task String MsgFromServer )
updateFromClient ctx now clientMsg serverState =
    case clientMsg of
        ForgotPassword email ->
            ( serverState, HandleClient.forgotPassword serverState.hasuraClient now email )
 
        ... ->

I can do what I need and as long as I end up with Task String MsgFromServer all is good.

forgotPassword : Client -> Time.Posix -> String -> Task String MsgFromServer
forgotPassword hasuraClient now email =
    let
        sendForgotPasswordEmail recipient =
            let
                mailBody = ...
            in
            -- wraps `AWS.SES.unsignedRequest` + `AWS.signRequest`
            Server.Email.sendEmail awsConfig mailBody

        recoveryToken =
            ...
    in
    HasuraAPI.CreateRecoveryToken.createRecoveryToken { email = email, recoveryToken = recoveryToken }
        |> Server.HasuraAPI.do client now       -- Task that updated DB with recovery token + return the affected user
        |> Task.andThen sendForgotPasswordEmail -- Task that construct mailBody for user and deliver
        |> Task.map (always (ToastSuccess "Reset password" "Check your email for the password reset link."))
        -- in this case, we're not interested in success payload, just let client know to show success message

Yes indeed :+1:

I actually have a slide for this lol https://speakerdeck.com/choonkeat/life-without-footguns-part-1?slide=9

Elixir is my favorite language for backend.
The needs of the backend are very different from the needs of the frontend. In the backend you need the kind of concurrency and scalability that BEAM provides.

You want the layers of indirection because you want to focus on solving your problems not the problems you have created for yourself because of poor management of resources.

At a minimum, you would want to program in a memory managed language, preferable one that also has the concurrency handled for you.

My suggestion is to take a look at RealWorld backends. Download the implementations with the technologies that look interesting to you. Read through the source code (it’s only about 1k LOC for most).

There are no solutions, only tradeoffs. Each technology stack makes different tradeoffs. You need to take a good look at them and decide which of the tradeoffs you like more.

I tried BEAM and Elixir some time ago but rejected it because of its huge RAM consumption for small programs. With default settings even trivial “Hello World” consumes 60 MB RAM. I’ve read that if you know settings really well you can get it down to around 20 MB, but less than that is not possible.

Those numbers are far too high for me. (Rust is now my backend choice.)

This is why I said that there are no solutions, only tradeoffs. If you need the webapp to run in as little ram as possible and this is the most important thing to you, of course you would choose a language that allows you as much control over resources as you need.

Full resource “paranoia” — not always a bad thing — could lead one to unikernels and ML. But that’s a bit extreme.

Where I work we use Scala for our backend. It is a language that crosses different paradigms, but using a functional programming library like Cats gives a relatively pain-free experience with a good “if it compiles then it works”-feeling.

Good libraries for building a functional backend might be http4s and doobie. If GraphQL is your thing then Sangria is really mature. We use it together with elm-graphql for great happiness.

I had a good laugh looking at your slides. I sens I’m following a similar path as you, I like the boulder analogy :slight_smile:

Yes and no IMO. By that I mean that I don’t see any reason why a language couldn’t in theory cover the frontend + backend. I feel we split technologies because of practical reasons and history.

Thanks, great suggestion. I had completely forgotten about that project.

Sure I agree :slight_smile: I’m not looking for a solution. Any problem can be tackled in any language at the end of the day. Rather, I feel annoyed with what I know on the backend side. In the same way that I felt annoyed with frontend technologies, until I stumbled once again on Elm (I had a look at it 2-3 years ago). Of course this is very early opinion for me, and as I tried to explain, this language really seems to click with my frame of mind. So I thought maybe other Elm programmers went through the same process and would have an opinion on the matter.

I suppose this is due to the VM. Java technologies have the same problem. There’s probably a tipping point where the RAM consumption becomes worth it and the benefits of the platform outweigh the downside of wasteful resources.

Thanks for the suggestion, I’ll look into it. Scala looks rather tough to learn though, because of it’s multi-paradigm approach (many ways of doing things). If I understand things correctly, Kotlin is quite close to it in terms of features and functionality.


For the record I stumbled upon another project today, having the objective to bring a statically typed language for the BEAM (based on Ocaml): https://caramel.run/

Not anything production ready but I can see there’s definitely a push in that community to have a statically typed language, interesting…

The backend needs a very good concurrency story. Frontend… not so much. But I agree, a language could cover both and there is a huge advantage to this.

I’m liking Go:

http://bec-systems.com/site/1625/why-are-go-applications-so-reliable

I’ve used Go+Elm a lot in the last couple years, and it feels pretty natural. The language values (ex simplicity) and tooling matter a lot – Elm and Go have a lot in common there.

Go defies most conventional thinking when it comes to programming languages and does not fit in neat little boxes, but in practice it seems to work well.

Deployment for Go apps is generally very simple

Everyone has their values/priorities, so it is nice we have so many options for programming languages! It really comes down to what is important to you.

1 Like

I love go, it’s a superb technology! I used it on a couple of projects and indeed it is rock solid. I feel that if the problem domain is well understood, then it’s a great fit.

However, I often don’t know what I’m building, I’m discovering along the way. And golang never felt great to me for exploration. So for that reason, I never felt good about using it for web dev either.

I’ve gotta say that my web apps have a small user base, but the business complexity is high, the domain is big.

Here is what bothered me at the time (code below was me fooling around, trying to get a feel of things, so warning code is very messy in some places).

Most golang programmers don’t feel the urge to consolidate knowledge into a common framework, thus rely upon golang’s stdlib. As a result, one can waste time trying to solve problems that have been solved many moons ago in other ecosystems.

Here’s an example, have a look at this code: golang-crud-example/main.go at 82cbcd5a047cffab071d3e7193c37ebb8d716ed0 · benjamin-thomas/golang-crud-example · GitHub

I was annoyed having to convert incoming HTTP strings params to ints. So I had the idea of creating an intKeyProvider function that would wrap around the the showCountry function.

Seemed to make sens, it would be a useful abstraction and would remove a lot of uneccesarry work. At the time I was used to ruby where the incoming params are already converted (after routing) for the programmer (which is just practical).

In the end, there was a lot of stuff similar to this, so it felt like re-inventing the wheel so I decided to just drop the idea of using golang for web dev.

Here’s the equivalent java/spring version: spring_address_book/CountryController.java at master · benjamin-thomas/spring_address_book · GitHub

It’s a solved problem, one has to learn the abstraction of the framework that’s all. From where I stand, I don’t anticipate all the problems I’m gonna face so I can appreciate if some of the work is already done for me.

As I said at the top of the post, SQL felt like a drag with golang (my apps are very database driven, like most business apps I would imagine).

Have a look here: golang-crud-example/countriesCRUD.go at 82cbcd5a047cffab071d3e7193c37ebb8d716ed0 · benjamin-thomas/golang-crud-example · GitHub

Here, the programmer has to remember to check for a possible error (triggered by the SQL library) after iterating on rows. I hate stuff like that, it’s so easy to forget. And in practice, I found there’s quite a lot of similar gotchas which I’m not fond of.

At the time, I remembered looking at rust, where not checking an error is made impossible, so that made me even less interested in the language (at least for my typical use case).

So to sum up, I’m context switching a lot, and can’t fit my main code base into my head. So that’s why I’m looking at other things. I want my tool to guide me along the way, scream at me if I forget something :wink:

So golang doesn’t look too good for me with that constraint.

Note: I’m aware of Buffalo, but not compelling enough for me comming from a rails background. In fact I really dislike all the string injection here: Querying · Buffalo

The golang/sql solution that appealed to me the most at the time was: GitHub - Masterminds/squirrel: Fluent SQL generation for golang

Java side, I think JOOQ is one of the most appealing options to me: change something db related, and everything breaks at compile time (which is what I want).

There was actually another library which I found that was even better but can’t remember its name, the concept is the same though.

So I know I’m going all over the place. All that to say I’m looking at a combination of great tooling at the language and library level but I also want constraints. The kind of good constraint I’m feeling from writing elm code, this is what pushed me to write this post. And I think I’d like to find that on the backend.

Thanks for your feedback though, I’m all ears if the golang ecosystem changed since I last looked at it :slight_smile:

1 Like