You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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
Keep language simple
Make sure there's not too many learning outcomes per chapter
Keep your writing concise and to-the-point
Use images where you can to simplify concepts
Keep the book (or chapter) around a central theme or story
Don't use unnecessary language!!!
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
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=Inttype alias Photo={ id :Id, url :String, caption :String, liked :Bool, comments :ListString, 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.
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
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.↩
The text was updated successfully, but these errors were encountered:
Keep it simple, stupid!
🤦♂️ Avoid word salad at all costs!
This above excerpt might be technically correct, but it does terrible job explaining what's actually going on.
Msg
is a custom type, of whichUpdateComment
is a variation.UpdateComment
as a function, that takes aString
.String
later to update our model.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?
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
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
photoDecoder
)The Bad
Show don't tell
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!
Let the compiler do some of the explaining
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.
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
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
This section could really do with some imagery to show how, for instance, a decoder passes through values to
succeed
, and then onto ourfunction
(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 requiredString
decoder (making it aDecoder String
), through to another decoder (succeed
? I think), through to the record we create withdog
.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 aboutrandom
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 hiddenFollowing 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
Msg
Random
you'll need aRandom.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 toRandom.generate
along with. 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
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 aString
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. ↩The text was updated successfully, but these errors were encountered: