Elm
The future of frontend web dev, I think.
- Overview and motivation (scroll down)
- Elm is faster than React, Vue, Angular, and optimising it is both safer and easier than the others
To check out
- better language server https://github.com/WhileTruu/elm-language-server
Annoying
- Elm doesn't support view transition API because of its VDOM. At least I feel less crazy for being unable to reproduce proper examples. I'll just do FLIP animations instead, since they rely on transitions after a DOM update (which Elm can do).
- No variable shadowing, but encourages large files. You have to come up with your own variable name aliasing convention to avoid running into name clashes. Even pattern matching shadowing is not allowed.
Notes
Modules.
- Creating
- If you're going to make a module at all, build each module around a type.
- Assume first that two code usages are similar, and if they end up being exactly the same, only then should you consider refactoring them out into a module.
- Many issues come from premature module creation; you start with a helper function that should make life easier, but add increasingly many and complex arguments to handle the individual cases.
- If you have many helper functions for one particular type, consider making a module, but don't do it prematurely.
- Importing.
import Package exposing (a, b)will make your file more confusing the more things you expose because you can't tell ifais defined in the file or elsewhere; minimise your use of this.import Package as Pis fine, sinceP.ausages make it obvious thatacomes from some packageP. - Do Not Plan Ahead. Start with pages as modules, and do not make any others. If something really is common or wrong, you can easily refactor it thanks to Elm's beautiful safety. In JavaScript, refactoring is expensive and risky, so getting architecture more correct upfront is important. But thankfully, we don't have to live in that world anymore.
- Long files are fine! Being averse to long files is a defence mechanism from working in unsafe languages like JavaScript, because the longer it is, the more annoying it'll be to debug the logic when something goes wrong. But this is a fallacy anyway; just because the logic is in different files, does not mean it is easier to debug, especially when they depend on each other.
Performance
- The DOM is almost certainly the bottleneck. Fix this with
Html.KeyedandHtml.Lazy. You can verify this by profiling your website. If scripting is only 10% of the time, then painstakingly doubling your Elm code's performance only nets you a net 5% performance gain.
Questions
Questions, for after the tutorial. Thank you for not running away with hyperfocus, executive function. You're getting chocolate later for sure.
- How do I make reusable components? For example, how do I make my own button to replace the inbuilt HTML one, so that I can customise its styles etc internally? In short, how do I type a button function? Or rather, how can I return to the dice example and easily add a second dice?
-
- I'm pretty sure the answer is to track time using a subscription to the clock, noting the time when the user stops typing. On each change to the model input, note the time in the model under
lastModifiedand also pass this time to a delayed callback (eg using the delay package), which checks the model'slastModifiedtime and fires a sequential callback (eg the search) if it matches.
- I'm pretty sure the answer is to track time using a subscription to the clock, noting the time when the user stops typing. On each change to the model input, note the time in the model under
- Come back and look at the local storage example because it'll let us get immediately demoing without having to hardcode a filepath.
- Come back and do the clock exercises (styling and SVG) if it feels relevant to your mission.
Side tangent questions that are not helpful for productivity, only interest
-
- It's "when you introduce a feature is a feature", and (roughly) "you should try to solve problems with simpler tools first so you keep the language accessible".
-
- Oh, it's because you can call a record constructor on an aliased record just like a regular multi-parameter constructor!
type alias Person = { name: string, age: Int }means you can callPerson "Jack" 27.
- Oh, it's because you can call a record constructor on an aliased record just like a regular multi-parameter constructor!
- How often do we really need monads? https://discourse.elm-lang.org/t/best-way-to-write-intensely-monadic-code-in-elm/10434/6
Black holes of complexity that I swear I will not research unless they directly affect me
- Code splitting and lazy loading code in Elm
Errors in guide
- https://guide.elm-lang.org/effects/json. "The main difference is in the getRandomCatGif definition" should be "The main difference is in the getRandomQuote definition"
- Add hyperlinks for package dependencies in the package docs, ie
Taskshould have a hyperlink on this page - https://guide.elm-lang.org/webapps/url_parsing is missing the example under "Synthesis", but I can probably write it
Helpful examples
Not useful for any brain but my own. Code that I have written, and hence, hopefully can remember, which showcases Elm features.
Does a password have at least 8 characters, an uppercase character, a lowercase character, and a digit?
- Result type
- Function composition
- Piped function application
validatedPassword : String -> Result String ()
validatedPassword p =
let
-- Function composition | (f >> g) x === g (f x)
containsNumber = String.toList >> List.filter (Char.isDigit) >> List.length >> (==) 0 >> not
in
-- Regular function application | f x
if String.length p < 8 then Err "Password must be at least 8 characters"
else if String.toLower p == p then Err "Password must contain at least one uppercase character"
else if String.toLower p == p then Err "Password must contain at least one uppercase character"
-- Piped function application | x |> f
else if p |> containsNumber |> not then Err "Password must contain at least one numeric character"
else Ok ()