Getting started wasn’t easy. Was the steep learning curve worth it? At this point I’d say it was. A quick list of first impression pros and cons:
- When it compiles, it works.
- Elm-UI makes it possible to avoid complex CSS.
- Forces good program structure.
- Pure functions and static types make runtime errors go away.
- S l o w to rapidly test new app features. Always have to tie up all loose ends.
- Have to build up from low level. In JS you’d just
npm installa package.
For example, I originally had a user data record that was passed to functions building the user interface views. It worked but was difficult with side effects like save to database. So I tagged the data record with a custom type RemoteData that explicitly models writes and loads. With
RemoteData User it was nice to build a user interface that doesn’t leave the user wondering if something’s happening or not.
It was even nicer was to find out about phantom types. I could use a phantom type to restrict function parameters to e.g. only “write done”
RemoteData User. So now the compiler would check—in compile time—that a function is called with only Users whose data are safe in the database. Proper types and the compilers type checking would help me write an app that would have no runtime errors!
Conclusion: for a simple app Elm was a delightful experience. The result is fast and efficient. I have a feeling tha the code will be reasonable easy to maintain. Specifically, building the user interface with Elm-UI was great. I spent much less than usual time tweaking CSS.
-- 1st version, won't work with side effects userUpdated : User.Model -> El.Element Msg userUpdated user = -- ... -- 2nd version, with remote data userUpdated : RemoteData User.Model -> El.Element Msg userUpdated rUser = case rUser of Loading -> -- Show spinner Stored -> -- Show the updated view -- 3rd version with phantom types type ValidData a = ValidData type Loading = Loading type Saved = Saved userUpdated : ValidData User.Model -> El.Element Msg userUpdated validUser = user = case validUser of ValidData u -> u -- Now we have a guaranteed valid user record