A stepper helper for Elm beginners

It would be really great to have something like Dr.Racket’s stepper (ideally a core feature). It works very well for Racket’s teachpacks.

I’m not sure where this feature suggestion should live, or who to send a feature request to — any ideas please let me know!

It’s not something I’d be able to build myself, but think it could be very handy when trying to understand how a function works (or, indeed, a program) …

I seem to remember a Mario animation that logged it’s progress … but I’m not sure where that package is.


What you remember seeing is the time travel debugger that gets turned on via the --debug compile flag. But that’s very much UI specific.

It would be cool, but I don’t think such a feature exists.

To understand how a function works, I know 2 techniques:

Technique 1 is to pepper Debug.log calls, like so:

> List.foldl (\x acc -> Debug.log "WIP" (x :: acc)) [] [1,2,3]
WIP: [1]
WIP: [2,1]
WIP: [3,2,1]
[3,2,1] : List number
> List.foldl (\x acc -> Debug.log "Result" (Debug.log "\nx" x :: Debug.log "acc" acc)) [] [1,2,3]

x: 1
acc: []
Result: [1]

x: 2
acc: [1]
Result: [2,1]

x: 3
acc: [2,1]
Result: [3,2,1]
[3,2,1] : List number

Technique 2 is to implement tests: if I give this input, I should get this output, then learn from observing the results of your test run.


Elm’s current roadmap doesn’t include this (any?) kind of feature. I think Gren (another language that descends directly from Elm) recently implemented step by step debugging, so maybe your suggestion would be more appreciated there.

@miniBill has a project in that might be able to do this. Not sure if they’re still actively working on it or not.

@benjamin-thomas Oh yeah that example is helpful. Are there any good guides you can point me to for tutorials on how to properly use Debug.log? I’m not entirely sure where to place it in the program (I’ve tried to use it in case branch -> which doesn’t work)

@Maldus512 I’ve taken a brief look at both Gren and Roc lang — Roc looks promising, but Gren seems quite a small community. Worth a shot to mention the feature I suppose.

You can use it anywhere you can call a function. In a case branch, you can only use special pattern matching syntax (as I mentioned in the other thread). It’s not possible to call functions there. I’m not sure if it helps, but if you are familiar with the identity function: Debug.log is the identity function, with the side effect of logging its argument to the console.

1 Like

Here is another example where I have used lots of Debug.log. Open the tab LOGS in Ellie to see the created debug output in console.

The logs may be put almost anywhere - in front of expressions or preceeding any variable within the expression.

Only the variables used in main are also output in debug. The unused variables stay invisible.

There is a document about elm package Debug.

@polarit Nice. I quite like the way @lydell is handling it here using a let for the debugger.

I think code can get quite messy fast using that function, so might be better used sparingly, checking each function as you go. And I suppose unit tests will go a long way to keeping bugs at bay.

@wolfadex Do you have any idea what it’s called? Or a GitHub repo it lives?

Note that in Elm circles, “debugger” means the time travelling debugger UI that you get by compiling with the --debug flag. What I used in a let is the Debug.log function.

While the let _ = Debug.log "message" thing in “trick” is handy many times, it’s also really handy to use Debug.log in existing expressions. For example, if you have this:

|> List.reverse
|> List.map square

You can add some Debug.log to see the result after each operation:

|> List.reverse
|> Debug.log "reversed"
|> List.map square
|> Debug.log "squared"

Debug.logs are added temporarily where needed to debug a problem, and are then removed again. When compiling an Elm app with the --optimize flag, using the Debug module is not allowed. There is also an elm-review rule to help you remove Debug.log calls you left behind after debugging: NoDebug.


GitHub - miniBill/elm-interpreter though I’m not sure how good of a solution it would be for this. Just the first thing that came to mind

@badlydrawnrob you are definitely welcome to use elm-interpreter! It’s almost what you want!

You prepare it by running make, then run yarn elm-watch hot and open the address that’s reported as server: in the output.

You type in your expression, then press “Trace”, and then you’ll be able to navigate around the evaluation.

It’s not fast, it’s not complete, it’s not ergonomic, but it works, and I’d be super happy to get feedback!

1 Like

@miniBill Sounds very interesting; I think I’d need a bit more documentation to understand how it works though — I’m a beginner with Elm. Is it better for smaller expressions, or can it handle small programs too?

It can currently handle simple expression or single modules. If given a module it will try to evaluate the function called main, regardless of type.

It implements the whole of elm/core and no other library.

To use it you need to clone the repository, then run make to prepare all the codegenned files. Once you’ve done that you can run yarn elm-watch hot, and connect to the address it will print out.

The UI has an input but for you expression or module, and you want the Trace button to execute the code while keeping track of values.

1 Like

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