Elm Guide stumbling blocks

Continuing and summarising from my two previous posts here and here

…as a person very new to programming in general I decided to try and learn Elm.
To do this I am trying to go through the Elm-Guide and, so far, I have stumbled upon two rough patches.

The first was at the end of the Elm Architecture / Form section.
One exercise asked the user to add a “Submit” button that would show eventual validation errors only after being clicked.
To me this was quite difficult.
Later I discovered that the following chapter Types actually clarified a lot of the things that had puzzled me, and even talked about refactoring … if the Submit Button exercise had been offered here, I believe my life would have been much easier.
As already stated, this would be my ideal Guide order:

  • Introduction
  • Install
  • Core Language
  • The Elm Architecture
    – Buttons
    – Text Fields
    – Forms ( without the final button exercise )
  • Types
    – Reading Types
    – Type Aliases
    – Custom Types
    – Pattern Matching
    – Forms ( refactoring + final button exercise )

The second moment of panic is happening right now, at the end of the Commands and Subscriptions / Random section.
The problem is, again, the last part of the exercise where I am asked to “have the dice flip around randomly before they settle on a final value”.
Even asking for help on this forum AND in the official slack channels (were I got some help from a few very nice people) I am far from solving the task.

One guy helped me figure out that instead of calling the function to roll dice multiple times, I could instead generate a list of random numbers (a list of dice rolls).
And then I could pass that, one head at a time, to the view.
But the syntax handling all of this is not clear to me.
And then any attempt I make seems to fuck up the Update function, because the Types mismatch… I can’t just write functions to do what I want, they have to be of the right Type… and this is making me go nuts! :frowning:
It was suggested that I might need stuff from the Task package in order to fix this.
And I have not even started to think about how am I supposed to make the multiple dice rolls be visible to the user; if I ever manage to fix all my current problems, the end result will be a number-change so fast that no one will see it.

Maybe I’m missing something obvious, but this task at the moment feels insurmountable and very disheartening.
After about a week of banging my head on it, I’ve decided to ignore it for now, and move on to the next Guide section, hoping to one day come back to with the sufficient knowledge to solve it.

2 Likes

I also struggled with the type system in Elm as I came from a background of being self-taught programming with ruby and a little bit of python. I actually gave up on Elm for a year or so. During that time I forced myself to learn Rust. Coming back to Elm after that year I found the type system in Elm refreshing because at least I didn’t have to worry about lifetimes/borrowing. Now I find languages without strong typing harder to parse. If you can get in the habit of actually writing the types in the function signature I think you’ll find that the compiler helps you figure out the types you need. When you don’t write the types out it can seem like the compiler is being arbitrary, which is frustrating. When you have the types you think you need explicitly stated you can at least figure out where the compiler disagrees, which is the first step in figuring out why.

1 Like

I agree that dealing with random generators is very difficult at the start.
Here is an Ellie that may answer your question: https://ellie-app.com/3RrgYQ7phTLa1
It is adapted from this example.

1 Like

Agreed that the random bounce exercise is a big jump in difficulty from the others! :scream:

In particular, it’s hard because it requires tapping into a lot of other parts of Elm, not just random generators. It might make sense to either add a label to it saying it’s a much harder exercise or remove it altogether in favor of something else.

Below is a discussion on how to solve it, starting with the simplest solution and building up to more complex. I’ve linked a full implementation in Ellie for each iteration. For easy reference, I’m listing them all here at the top as well:

  1. All the faces at once (text)
  2. All the faces at once (SVG)
  3. Faces over time (text)
  4. Faces over time (SVG)
  5. Moving faces over time

Technically counts?

Executable example

The simplest approach is to just generate a randomly sized list of die rolls and print out that list. It’s not exactly what the exercise asks for, but it gets at the spirit of what the exercise is asking for. It’s also solely focused on random generators so it’s actually exercising the skill learned in this section.

Key lesson: Random.andThen allows you to chain dependent random rolls. First you roll to know how many bounces there will be, then you roll an n-element long list of random die rolls.

dieRoll : Random.Generator Int
dieRoll =
    Random.int 1 6

bounceCount : Random.Generator Int
bounceCount =
    Random.int 1 10

randomBounces : Random.Generator (List Int)
randomBounces =
    bounceCount
        |> Random.andThen (\number -> Random.list number dieRoll)

Add some visuals

Executable example

The exercise seems to assume that you’re building on the earlier ones where you added a visual representation of the die face. Let’s use that to display n die faces, one for each bounce rather than just showing a list of numbers.

I used the elm/svg package but if you wanted to be fancy you might want to look at ianmackenzie/elm-geometry and the associated ianmackenzie/elm-geometry-svg.

Key lesson: Elm has both first and third-party packages to draw graphics to the screen

Faces over time

Executable example (text)
Executable example (svg)

Displaying all the bounces at once isn’t really what the exercise is asking for. We’d like to show them all in sequence over time. In order to do that we’ll need to subscribe to the clock. Time and subscriptions are explained in the next chapter of the guide. It might make sense to call that out in the exercise description :thinking:.

While the model is in the Bouncing state, I trigger a NewBounce message every 750 milliseconds. In the update I handle that message by popping the head off the list of bounces.

Key lesson: Elm allows you to subscribe to the clock, sending your update a message of your choice every n time interval.

subscriptions : Model -> Sub Msg
subscriptions model =
    case model of
        Initial ->
            Sub.none

        Bouncing _ _ ->
            Time.every 750 (\_ -> NewBounce)

        Final _ ->
            Sub.none

Random positions

Executable example

It’s cool that we’re showing a bunch of different die faces over time but they aren’t moving. So far, each roll has been represented by an integer. We could use a fancier data structure to capture both a value and a position for each die roll.

type alias Position =
    { x : Int
    , y : Int
    }

type alias Die =
    { position : Position
    , value : Int
    }

Then we create some new random generators to generate these more complex types:

dieValue : Random.Generator Int
dieValue =
    Random.int 1 6

randomPosition : Random.Generator Position
randomPosition =
    Random.map2 Position (Random.int 0 200) (Random.int 0 200)

dieRoll : Random.Generator Die
dieRoll =
    Random.map2 Die randomPosition dieValue

We use some SVG transforms in the view render the die at its position.

Key lesson: Try to model the problem without randomness first. Here, we need to render a die to an arbitrary position so we added that to our data modeling. Once we have that, we can figure out what generators are necessary to make those positions random.

Going further

This is OK but you’d like it to feel more alive. Have some rotations! Move smoothly between different points! Bounce realistically according to natural laws!

You’ve now gone beyond randomness and firmly entered the realms of computer graphics, animation, and physics simulation. The #gamedev channel on the elm-lang Slack would be a good place to ask questions on these topics. Good luck!

9 Likes

Thanks a lot to everyone, and to @joelq especially!
That is an AWESOME post! :smiley:
I’ll probably try to mess with your code, attempting to add the functionality to my current structure instead of just copy-paste your solution as a new/clean project.
I see already that it won’t be easy as you use a different Model and I’m sure that will drop me again in Type Mismatch hell. But at least this time I have a working example to start from :slight_smile:

1 Like

I think this is a major problem with this exercise. Earlier someone else asked how to solve this and once I suggested reading about time subscriptions in next chapter he was able to solve it.

After thinking more about this, my guess would be that the goal of the exercise was to introduce learners to using andThen to chain dependent random rolls. The previous exercise is:

Add a second die and have them both roll at the same time.

which introduces you to the problem of multiple independent rolls.

Perhaps a better exercise would be:

Roll a random number of dice together at the same time.

This is a logical next step after the 2-dice exercise and could be solved like my Technically counts solution above (or the SVG iteration on it). It introduces a new concept (andThen) without requiring knowledge of subscriptions, time, tasks, or any other Elm concepts.

Thoughts?

1 Like

Actually I read this as rolling 2 dice at the same time, with the same click, displaying the same result :stuck_out_tongue:
I attempted briefly to have the second die be independent from the first or to display a different number… but that seemed to require changing a LOT of stuff that broke everything in my initial code :confused:

I don’t know why exactly, but sometimes it feels obvious to go look into the packages for some helper function. But other times it is not.
The feeling is that my previous experiences with coding (failed as they might be) taught me to expect some things to be pre-made helpers… like a lot of the stuff in STRING for example… while more complex things are expected to be “done by hand”.
Like… you have something that generates ONE random value, and everything else SHOULD be just you employing that original building block to create whatever other complex structure you need… which LOGICALLY will require looping or some kind of recursions.

The thought pattern being: I need to do this thing… so I imagine this logical structure… so I need a, b, c, tools… so I go in the Modules looking for them… so once I find them, I am done with the Module.

I’m starting to see that this is not a good pattern in Elm.
You can’t loop.
Recursion seems to be kept to a bare minimum.
And having a lot of your functions converge into one common UPDATE function forces Type limits that are (for a n00b) hard to handle and understand.

So it seems to me that the “Elm answer” is to have ad-hoc helpers for a lot of stuff. They too need to be understood to be used. And they too introduce additional demands regarding the kind of Type they handle/accept. But they seem to be THE way to go, rather than try to stick to the basic primitives and build everything yourself.
I know in the future I’ll first go through a whole Module to see what’s available BEFORE even starting to think about the logic of my code. Like, if I need to generate a random value I will not just look for a way to generate a random value, but will instead think:
Oh there is a whole Module about randomness, then I HAVE TO read through it all; finding the one command I am looking for is NOT ENOUGH and could lead to problems later

This to me was not an obvious approach.

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.