DEV Community

One year of Elm in production

Tomáš Zemanovič on March 06, 2018

About a year ago, I started a new job where I build tools aimed at helping others provide a better user experience. As is common in the software in...
Collapse
 
kspeakman profile image
Kasey Speakman

We've also had Elm in production for about a year now. I really can't say enough good things about it. Using Elm is probably the first time after I deployed a UI app that I didn't feel dread about maintaining it. And my team feels the same way.

We did make some mistakes that hurt maintainability initially. But Elm is really safe to refactor.

Collapse
 
ok32 profile image
ok32

Hey. I'm curious to know what were the mistakes.

Collapse
 
kspeakman profile image
Kasey Speakman • Edited

Probably the biggest mistake was in how we designed the Model. We tried to use it as a "database". So for example, when we switched to a page, we would load the data for that page into a property on the model. Then we would reuse the data on another page. However, this led to a lot of code just to check/maintain this shared data. And it was the source of many state-based bugs. (One page left the data in X state, but another page expected it in Y state. Or one page needed field Q but the other page didn't for its view.)

Ultimately, our use cases required the app not be offline-capable and our users really preferred getting the latest data from the API any time they switched pages. So we dropped this shared state model. We went to a strategy where the model represents the state of UI elements. So, pages are part of a DU (aka custom type) because only one can be shown at a time. So when the user switches pages, all the data from the other page is gone. No need to reset it or make sure it is left in a certain state.

So in the end, our model ends up looking more like this. Code is off the top of my head.

type alias Model =
  { apiToken : String
  , currentPage : Page
  }


type Page
  = Home
  | OrderView OrderViewState
  -- others like OrderList

type alias OrderViewState
  { orderId : String
  , orderData : Remote OrderData
  }

type Remote a
  = Loading
  | LoadFailed Http.Error
  | Loaded a

The way we initially did state also led to weird extra routing types. Those became unnecessary once we switched to this model. It maps very cleanly to/from navigation URLs. It is easy to parse the URL /order/{orderId} and convert it into the OrderView page:

let state = OrderViewState orderId Loading
    page = OrderView state
in { model | currentPage = page }
   ! [ Api.getOrderData model.apiToken orderId ]

Going the other way is easy too.

case model.currentPage of
  OrderView { orderId } ->
    "/order/" ++ orderId

Anyway, I think modeling as a database was the main thing that we did wrong at first.

Thread Thread
 
ok32 profile image
ok32

Got it. Thank you!

Collapse
 
eljayadobe profile image
Eljay-Adobe

I have a tough time talking about FP to OO developers. I often get the deer in the headlights look.

Must be my fault, as the messenger.

I have not been able to convey to OO developers that things like Design Patterns and SOLID are important OO disciplines to shore up the areas where OO has weaknesses.

And that the strengths of FP -- such as immutability, recursion, pattern matching, higher-order functions, code-as-data, separation of behavior from data, referential transparency -- are, collectively, game-changers from the OO paradigm.

Collapse
 
tzemanovic profile image
Tomáš Zemanovič • Edited

I can definitely relate to that. I can think of some things that are probably at play:

  • what is unknown can appear as scary
  • the common myth that FP is somehow harder than OO; I would say this is more to do with education, which is better in OO than FP. OO also receives much more focus.
  • FP is sometimes perceived as elitist
  • for some areas FP might not be the best pick, if your main focus is to get something done as quickly as possible; For example, there is a very handy State of the Haskell ecosystem post, in which Gabriel Gonzalez rates Haskell for each programming area. For many applications the trade-off can be diminished if you're planning to support them for some time, which we usually do anyway.