Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

📖 Programming Elm: The Good and the Bad #35

Open
badlydrawnrob opened this issue Jun 24, 2024 · 0 comments
Open

📖 Programming Elm: The Good and the Bad #35

badlydrawnrob opened this issue Jun 24, 2024 · 0 comments

Comments

@badlydrawnrob
Copy link
Owner

badlydrawnrob commented Jun 24, 2024

Keep it simple, stupid!

Writing about intermediate (or even beginner) Elm is quite tricky because there's so much to learn, and you've got to be very specific. I feel though that you want to reduce words and terminology down to an absolute minimum, and stick to simple language, simple explanations, simple visuals, wherever possible. Break down the learning points, simple learning outcomes per chapter, so on.

Chapter 03, pg.58 (PDF)
"Notice what you’re doing with the UpdateComment value. This is where pattern matching really starts to shine. When a custom type constructor receives its arguments, it constructs an instance of the custom type and also holds on to its arguments. Pattern matching allows you to match the constructor and bind the wrapped arguments to identifiers later. In the update function, you match UpdateComment and bind the String value to a constant called comment."

Chapter 04, pg.76 (PDF)
Tuples can hold multiple elements like a list, but those elements don’t have to be the same type. You can think of tuples as records that organize values by position instead of a field label. You can create tuple literals similar to list literals by using parentheses instead of braces to surround the tuple members. The most commonly used type of tuple is the pair, which contains two items. [Elm only supports tuples up to 3 in size] In fact, the unit type is basically an empty tuple ().

🤦‍♂️ Avoid word salad at all costs!

Keep keywords to an absolute minimum. Be consistent.
To understand the above, first we need to know what "custom type" means,
what a function is, a variable is, and the data type of String.
Pattern Matching is fairly easy to explain.
Junk the rest as garbage 🗑️!

This above excerpt might be technically correct, but it does terrible job explaining what's actually going on.

  • A Msg is a custom type, of which UpdateComment is a variation.
  • You can think of UpdateComment as a function, that takes a String.
  • We can use that String later to update our model.
  • You might want to explain what Pattern Matching means.
  • You might want to explain that a variable (not a constant) just holds a value.1

There. Simple. We're using terms we'll recognise without fucking constants, constructs, constructors, binding, and use simple everyday English. Simplify wherever possible. Don't make it any harder than it has to be.

Words are important

It's a case of picking the ones that kids need to know as they develop their skills. For instance, what is an expression? How does the compiler (or Dr.Racket) evaluate this expression?

Where do you start absolute beginners?

I'm not sure the guys on Elm are super into teaching, so might not be the best resource. However, starting with simple data, simple shapes, simple animations, and NO type signatures. Don't try and teach types to kids at a young age, or you'll confuse them (probably) ... teach it silently, not explicitly.

You’ll eventually discover type signatures through the compiler (and errors) so gradual introduction, when they're a little older (16?) — starting with simple types, makes sense to me.

Clear and simple learning outcomes

  1. Keep language simple
  2. Make sure there's not too many learning outcomes per chapter
  3. Keep your writing concise and to-the-point
  4. Use images where you can to simplify concepts
  5. Keep the book (or chapter) around a central theme or story
  6. Don't use unnecessary language!!!
Screenshot 2024-11-12 at 21 20 09

And make sure the code you're writing is stripped back to as little as possible to drive that point home. Ideally you'd have functions in isolation (such as documentation, glossary, or file) and then in practice. I find that's helpful when trying to reference it later. ⚠️ Writing those notes out yourself is very time consuming.

The Good

Gentle introductions

  1. Exposing one of the functions in a module (pg.72, exposing the photoDecoder)

The Bad

Show don't tell

Chapter 04, pg.63
Open up your Picshare.elm file. Rename the Model type alias to Photo and then create a new Model type alias to the Photo type. The type alias rabbit hole can go as deep as you want, but be wary, there be dragons down that hole.
While you’re at it, create a type called Id that aliases to Int. This will help make your later type annotations more readable when you want to treat an Int argument as an Id. Right underneath your imported modules, you should now have this code:

I don't know if I have some brain fart, but sometimes explaining a thing is more convoluted than just showing me what to do. Might've been better to just have the code chunk with concise comments explaining what you're doing? It's important to explain why you're doing certain things, but by now you should've explained what these things are.

One glance at the code below, I understand exactly what's going on without rambling on!

type alias Id = Int

type alias Photo =
  { id : Id
  , url : String
  , caption : String
  , liked : Bool
  , comments : List String
  , newComment : String
  }

type alias Model = Photo

Let the compiler do some of the explaining

An indirect dependency is a dependency of some other dependency in your application.

This just needs a couple of words. Do this because this. The compiler is explaining things for us. Ideally with beginners, they shouldn't be doing ANYTHING difficult on setup. Once they're ready for building things, they can worry about this stuff.

⚠️ Avoid dependencies at ALL costs

Will your book's examples work in 2 years? 5? 10?

Ok so 10 years might be a stretch for any programming language, but I think the author did himself a disservice by cramming it full of so many npm dependencies. Tons of errors, outdated packages, 99 vulnerabilities, way too many packages and so on.

Why make life harder than it needs to be? Whether Racket Lang, Elm, Python, whatever, just use stock code and a sprinkling of reliable packages if at all needed. When in doubt, don't.

Things that are HARD

Elm works quite differently to Javascript and Python. It does it's best to avoid side effects, which in some cases (such as decoders and random) makes it a hard sell.

Python and Javascript do some things a lot easier (for instance, random here and here). Another example is requests, when consuming json ... it's quicker than setting up decoders in Elm.

Decoders

Once again, it's WORD SALAD
You need to rewrite this whole section with easier sentences
Stepping through this complex type annotation with images would be a lot easier (I think) than using words (or words alone) – go find a few other videos explaining decoders and make the language easier to follow.
required : String -> Decoder a -> Decoder (a -> b) -> Decoder b
— pg.70 (pdf)

This section could really do with some imagery to show how, for instance, a decoder passes through values to succeed, and then onto our function (in this instance, dog name age = { name = name, age = age }.

I understand it somehow intuitively, but following the thread of knowledge from a Decoder a (variable type) through to the required String decoder (making it a Decoder String), through to another decoder (succeed? I think), through to the record we create with dog.

It's just fucking confusing with the way complex Types are written, and takes quite a bit of effort to wrap your head around the way it works. I think it's just difficult to translate all this |> piping of decoders by words alone.

It ends up making you sort of understand how your "master" decoder function is working, looking at the function you've created; but stepping through it with Types adds some confusion. And it only gets worse later on when you're trying to reason about random functions (with type signatures), recursive types (I have zero idea how these work), and the rest.

You also have to understand that (because we're using |>) we're using curried functions, although I don't suppose it'd make life much easier even if we weren't.

A picture might really bring the thing to life. Whiteboard it out.

<Internals> are hidden

No side-effects

Following on from Decoders, you have to get comfortable with <internals> in Elm [ ... reasons here ...] because some things are purposefully hidden from view. You need a decoder, then a decoder function to apply that decoder and work it's magic.

Similarly with Random you'll need a Random.generator function (which you'll see that <internals> thingy along with the type annotation if you pop it in the elm repl ... but it won't do a damn thing unless you give it to Random.generate along with Msg. We create a generator function with generator, but we consume it (and do some magic) with generate`.

Confused? I'm not surprised. Another reason why I feel images are really necessary to make the whole thing easier to understand. In Python and Javascript you have a simple random function and it works right there and then.

So why in the seven hells am I needing to do it this way? [ ... reasons ... no side effects ... ].

Footnotes

  1. It's debatable which term to use, but here it says a constant does not change it's value over time, whereas a variable changes it's value depending on the equation. So technically, comment is a String that could hold any value, so it's a variable — a constant would be something we'd set up that never changes. I guess with CompSci it's best to be explicit and precise, but you gotta balance that up with being easy to understand. Too many keywords to remember is not an asset.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant