Skip to content

badlydrawnrob/elm-playground

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

772 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

README

📅 "If I miss one day of practice, I notice it. If I miss two days, the critics notice it. If I miss three days, the audience notices it." — Ignacy Jan Paderewski

It's super easy to forget so aim to practice once per week! A little every day is even better This is the killer; if I step away from programming for a few weeks, you really feel the rust gathering. It doesn't take too long to learn Elm, but give yourself 6-12 months to learn it deeply and you'll be a better programmer for it. Then you can build bigger!

An Elm playground

This is a testing ground for all things Elm; a great place to start learning. I'm squarely focused on statically typed functional programming in this repo, which will shape you as a programmer, instilling good habits. Elm compiles to javascript, but there's no need to learn js to use it (although there's a good guide here).

As you evolve as a coder, you should carve out a clear learning frame.1 If you'd like a study aid, I've built a great little tool for memory called Anki flashcards, which really helps lock-in best practices and syntax. While they're great for revision, it's no substitute for building things.

🔰 Beginners

Elm is great for learning how to engineer.

Start with How To Design Programs and follow that up with Elm. It's better designed, more consistent, with better compiler error messages than Python.

After these two languages you'll have a decent grounding in Computer Science theory, and they're not too academic. I'm rubbish at maths, and am mostly focused on rapid prototyping, so I like to avoid deep academic learning. An artist ships!

🚀 Getting started

📖 The first and most important thing is that you've got to have a goal and a vision — Arnie

My learning goals:

  1. Brutalist, minimal, zen code, writing as little as possible2
  2. Cut code down and cherry-pick the important stuff (just-in-time)
  3. Validating a business idea with paper prototyping
  4. Teach beginners, pre-CompSci, and entrepreneurs (kids and adults)

If I were learning from zero I would ...

  1. Begin with HTDP and then Elm (not Python!)
  2. Build quickly and build that muscle memory (with live users)
  3. Find a great teacher and mentor (good habits / what not to do)
  4. Learn how to frame and structure my learning (barely learn it deeply)
  5. Understand best practices for APIs, HTTP, and JSON (essentials)
  6. Use Ai as a teacher and pair programming (with caution)
  7. Take a subset of CompSci3 and learn it thoroughly (e.g: web apps)

My personal coding style

  1. Ai prototype design routes (but solidify by hand)
  2. Barely any state (Tesla 5 steps)
  3. Brutalist dependencies (what can be removed?)
  4. Cut code down (the web obesity crisis)
  5. Reduce complected code (see "Simple made easy")
  6. Remove complexity and simplify

Basic commands

# Initialise an Elm project
elm init

# Install a package
elm install elm/<package>

# Make a HTML file from an Elm one
elm make src/Main.elm

# Compile to javascript file
elm make src/Main.elm --output=app.js

# Has `elm.json` file but no `elm-stuff` folder
# re-install packages (from local cache)
elm make [file]

# View in the browser
elm reactor

Useful commands

# Try Elm in terminal
elm repl

# Check package changes
elm diff elm/http 1.0.0 2.0.0

⭐ Why Elm, then?

Types and inference ...

Why do we need types? It's best illustrated by the old joke ...

A programmer's wife told him "Go to the store and buy milk and if they have eggs, get a dozen." He came back a while later with 12 cartons of milk!

Computer Science is specific, it requires some discipline. If you've used a language like Python before, you'll know that runtime errors can be frustrating and opaque. Elm's compiler solves this problem by inferring (or reading) types. An example below:

shopping : List String
shopping = ["oat milk", "chocolate", "marshmallows"]

checkout : List String -> List Float
checkout list =
    List.map (\item -> (priceCheck item)) list

priceCheck : String -> Float
priceCheck item =
    if item == "oat milk" then
        2.50
    else
        1.50
>> checkout shopping
[2.5, 1.5, 1.5] : List Float

Types are checked at compile time by running elm make, so we can be confident our program works. The .js file Elm creates does not contain types, but holds program logic with our values and functions. We don't really care what that javascript looks like, as it isn't meant to be read.

... But don't go crazy with them

Types take a while to get used to ... but don't go type crazy!

There's a tendency when learning custom types to use them everywhere. Don't! Unless there's a very good reason not to, you can safely stick with basic types! As your program grows, you might decide to group functions around a particular type, then you may decide to gradually increase type complexity as and when it's needed (see "Life of a file" below). Here's an example of the evolution of a type Album ...

-- Simple list of song titles
[ "Absolute beginners", "Get back", "Those shoes"]

-- Add more detail with a record
[{ title = "Absolute beginners", runtime = (5,36)}]

-- Gradually increase complexity as needed
type Album
  = Album (List Song)

type alias Song
  = { title : String, runtime : (Int, Int) }

-- `Maybe` is best saved for potential missing values
type Album
  = Album (Maybe (List Song))

It's worth nothing that Maybe is not needed here. Not at all! It complects things. We now have to "lift" the List Song to update it and "wrap" the value inside the Maybe type again. A lot of unnecessary work! Always, always, ask "how can my types be simplified?" for inputs, outputs, and everything in between.

Beautiful error messages

This is where Elm really shines, everything is easy to install and just works! The error messages are clear and helpful, guiding you towards a correct solution, and everything is built in (unlike Python with it's cryptic error messages, None values, and many type checking packages). Errors such as bad types, reusing variables, missing branches, help a lot when refactoring code.

checkout ["oat milk", 2, "marshmallows"]
-- TYPE MISMATCH ---------------------------------------------------------- REPL

The 2nd element of this list does not match all the previous elements:

16|   checkout ["oat milk", 2, "marshmallows"]
                            ^
The 2nd element is a number of type:

    number

But all the previous elements in the list are:

    String

Hint: Everything in a list must be the same type of value. This way, we never
run into unexpected values partway through a List.map, List.foldl, etc. Read
<https://elm-lang.org/0.19.1/custom-types> to learn how to “mix” types.

Hint: Try using String.fromInt to convert it to a string?

Other benefits

There's a lot to learn, but lots of upsides!

  1. Argument order (and types) can be enforced
  2. Built-in tooling and package management (no million things to install)
  3. Built-in type inference and nicer annotations (than Python)
  4. A style-guide to format your code (best practices and plugin)
  5. Fewer dependencies and longer-lasting packages (no constant updates)
  6. It becomes easier to read code by their signature
  7. Mixing of types isn't allowed (in certain data structures)
  8. Type signatures give rules for "what" (don't always need to know "how")
  9. No mutations make for safer programs (immutable data)

Self-documenting code

You eventually get used to writing types, but also elm-format

You eventually begin to read code by it's type-signatures only. Aliases and Types become self-explantory (less documentation) and act as style-guides (interfaces).

Dependencies, release cycle, testing

😌 Piece of mind compared to other languages (like Python)

  1. Less unit testing (if it compiles it works)
  2. No dependency hell (versioning and rarely break)
  3. No runtime errors (if it compiles it works, unlike javascript)
  4. Speeds up development (confident prototyping and correctness)
  5. Types are enforced (without having to write them)
  6. No None, null, or undefined!

I'm lazy, and rarely test prototypes, but it's good practice to. This mindset will land you in trouble if you're using Python, or any language without strict typing. In general I test my apps like a visitor would, or with a GUI, like Bruno. Remember, Elm only checks data types; it won't guarantee your inputs and outputs are what you'd expect. For mission-critical production projects, finance, larger programs, or working in bigger teams, a lot can go wrong without tests!

Learning Elm

It's also helpful to keep an offline library/docs when your wifi breaks (or the internet rots) — there's an app for that.

Here's a wealth of resources for getting started with Elm. Each list is ordered by difficulty (beginners and easiest first). I'd suggest starting with "Beginning Elm" or "Welcome to Elm" to get a taste, move onto the books (or online courses), peruse the helpful talks, check out some real world examples, then it's up to you! You can also run Elm in the browser with Ellie and Elm.run.

📖 Books

📚 Useful documentation

🧑‍🏫 Online courses

🎞️ Helpful talks

  1. Let's be mainstream!
  2. Life of a file
  3. Teaching Elm to beginners (good for teams)
  4. Scaling Elm apps
  5. Making impossible states impossible4
  6. Make data structures
  7. From Rails to Elm and Haskell
  8. Simple made easy

🗺️ Real world examples

❓ Some helpful FAQs

🛠️ Tooling

🚧 Larger programs

Footnotes

  1. A learning frame is what you are, and are not, prepared to learn. It's helpful to sketch this out upfront and stick to it. It can change over time, but setting goals and limits keeps you focused. What is it you need to learn, exactly? What's the outcome you're looking to achieve? Here's an example of a learning frame.

  2. Meaning efficient with my time, not lazy (ok, a little lazy). A simple route with less code and complexity? I'll take it. Take this carousel in Elm, for example. Want to be a great programmer? Go ahead, take your time! Prefer the easy route? Use scroll-snap with CSS. It all depends on your vision: I'd rather have more time to pursue other things, keep things simple.

  3. A lot of programming books can be highly academic, and although books like these can be very thorough (and suit some learning styles), for me that was the wrong approach. For example, learning how to code recursively can be intellectually stimulating and it teaches you a lot, but after a certain point, it provides diminishing returns. It's hard to know exactly what you need to learn, but if your goal is to build things, aim for industry knowledge and pragmatic goals, rather than academic ones. Learn the basics well and start building things. You'll learn a lot along the way (with help from mentors), and you can do the heavier academic texts later if you enjoy that kind of thing!

  4. All (or most) of Richard Feldmans talks

  5. I'm being a little lazy here. I don't consider myself a "proper" computer scientist (and I don't code that often); more a pragmatic programmer. My goal is prototyping, so I rarely write unit tests. This isn't the correct way, but Elm types give a lot of guarantees: testing the program as a regular user (like a QA) might be enough. If any bugs arise, a visitor can raise a ticket and tests can be written.

  6. Hopefully these three examples give an understanding of how to do Elm on a larger scale. Elm without a framework like Elm Land needs quite a lot of plumbing, so hopefully there'll be "one way to do it" when Elm Studio eventually comes out. Alas, even that's imperfect as (I've been told) it's not plain old SQL and has abstractions to make the backend work.

About

A repo for Elm tutorials, projects and tests

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors