I made Elm crash runtime...

233 views
Skip to first unread message

J. E. Marca

unread,
Sep 30, 2016, 3:43:35 PM9/30/16
to Elm Discuss
Slowly climbing up the Elm learning curve, I made it crash runtime yesterday, and I just wanted to report to the mailing list.  This may be a known issue, but as a novice I'm making all kinds of rookie mistakes.  As such, when those rookie mistakes cause a runtime crash without a compiler complaint, I'll try to report them here.

The short version:  You can do this in Elm:

> v = v+1
NaN : number

I would have expected that to crash, because v is being used before it is defined.

In the clojurescript repl, I get a similar case:

cljs.user=> v
WARNING: Use of undeclared Var cljs.user/v at line 1 <cljs repl>
nil
cljs.user=> (def v (+ v 1))
#'cljs.user/v
cljs.user=> v
NaN

But in the clojure repl (Java based, not JavaScript), I get the crash I was looking for.

boot.user=> (def v (+ v 1))

clojure.lang.Compiler$CompilerException: java.lang.ClassCastException: clojure.lang.Var$Unbound cannot be cast to java.lang.Number, compiling:(boot.user7277212806697690427.clj:1:8)
           java.lang.ClassCastException: clojure.lang.Var$Unbound cannot be cast to java.lang.Number


I suspect this will always be the case, but it led to a runtime crash, as explain in the longish writeup below


Background, I want a date picker element in my page, so I copied in the elm-datepicker ( https://github.com/Bogdanp/elm-datepicker).  In copying I omitted something I thought was small, which in turn caused my program to pass compiling but crash runtime.

The code snippet from the real example that broke relates to the "update" behavior.  The original says:

```
update : Msg -> Model -> ( Model, Cmd Msg )
update msg ({ datePicker } as model) =
    case msg of
        ToDatePicker msg ->
            let
                ( datePicker, datePickerFx, mDate ) =
                    DatePicker.update msg datePicker
    ...
```

Note the construct `({datePicker} as model)`  As a newcomer, that is something I've never seen before.  Not something that is documented on the "syntax" page (http://elm-lang.org/docs/syntax), and not something I can find in the get started guide.  https://guide.elm-lang.org/get_started.html  I *guessed* that it was some sort of way to desctructure the incoming model argument, but because I had many cases, I didn't want to bother with that for just this one case.

(Better docs would probably have helped my understanding, but this is  young language...if I just wanted better docs, I'd still be using JavaScript)

Anyway, I dropped the destructuring thing, whatever it is.  My version looked like:

```
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  case msg of
    ToDatePicker rec ->
            let
                ( datePicker, datePickerFx, mDate ) =
                    DatePicker.update rec datePicker     -- <== bug here!
   ...
```

I changed the incoming destructuring thing, and I swapped out the nested redefinition of "msg" for "rec", but I *forgot* to say "model.datePicker" inside the let statement.

Strangely, this compiles just fine.  I would have expected to see at least a warning, because the rhs of the assignment is using a variable that is first declared on the lhs of the assignment.  Actually, no.  That's too weak.  I expect the compiler to bomb out in such a case.

But it doesn't

```
=================================== WARNINGS ===================================

-- unused import - /home/james/repos/jem/calvad/calvad_ui/elm-calvadui/src/elm/Main.elm

Module `Date.Extra.Format` is unused.

15| import Date.Extra.Format as Format exposing (format, isoDateFormat,formatUtc, isoStringNoOffset)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Best to remove it. Don't save code quality for later!

Success! Compiled 1 module.
Successfully generated /tmp/116830-14831-1hiiwmv.2po3kdquxr.js
```

(The warning that *is* there relates to an unused module, not to the usage of an undeclared variable.)

Running that, I see the following runtime crash:

```
TypeError: _p23 is undefined
_Bogdanp$elm_datepicker$DatePicker$update<() c4836e16ec49f9b909df.js:35001
A2()                                                                           c4836e16ec49f9b909df.js:25776
_jmarca$elm_calvadui$Main$update<()                  c4836e16ec49f9b909df.js:37131
... blah blah blah ...
```

Of course it crashes, because datePicker is undefined when DatePicker.update is called on it.  Once I spotted the error the fix was easy, but it took probably 2 hours for me to spot it.

My guess is that using undefined variables that get auto-defined-and-hoisted will always be a problem because this is JavaScript under the hood. This also affects clojurescript apparently, as is shown by my tl;dr up above.  Only clojure/java crash properly when using undefined variables.


Regards,
James

Noah Hall

unread,
Sep 30, 2016, 3:46:25 PM9/30/16
to elm-d...@googlegroups.com
This is a known issue. See this ->
https://github.com/elm-lang/elm-compiler/issues/873

It will be fixed in 0.18.
> --
> You received this message because you are subscribed to the Google Groups
> "Elm Discuss" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to elm-discuss...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

J. E. Marca

unread,
Sep 30, 2016, 4:13:31 PM9/30/16
to Elm Discuss


On Friday, September 30, 2016 at 12:46:25 PM UTC-7, Noah Hall wrote:
This is a known issue. See this ->
https://github.com/elm-lang/elm-compiler/issues/873

Sorry for the noise then.
 
It will be fixed in 0.18.


Oh cool.  I'll read up on the fix and see how it was/will be done.
 
Regards,
James

OvermindDL1

unread,
Oct 3, 2016, 11:33:05 AM10/3/16
to Elm Discuss
As a comparison, this is how OCaml handles the same issue.  OCaml uses built namespaces, meaning you can only use what was defined before you first of all, and if you want something to be able to refer to itself or something later on then you need to mark it explicitly

```
let v = v + 1
```

In Ocaml generates the error:
```
Line 1, 8: Error: Unbound value v
```

And even if you mark it as hoisting it to be bound in its own namespace via:
```
let rec v = v + 1
```

That is still not allowed because a value cannot reference itself:
```
Line 1, 12: Error: This kind of expression is not allowed as right-hand side of `let rec'
```

However, if it were a function (`()` is just an empty tuple, a convenient no-arg argument):
```
let rec v () = (v ()) + 1
```

Then that compiles, and as expected it will recurse forever if called.  ^.^

That is what I want Elm to do.  :-)


For an explanation of how/why this issue is possible at all, do feel free to ignore this section, but all of the examples you gave stem from the same problem, which is caused by two design decisions hitting each other, the first of which is because Elm uses a unified namespace, meaning you can define anything anywhere in a file, value or function, where in OCaml it has to be in order, but even in OCaml by using `let rec` and `and` you can do the same namespacing style as Elm and it will still not allow recursive values, but again that is because of the way Elm handles function, which is the second reason; in OCaml a binding is either a value (never recursive) or a function (and a function is also always 1 input to 1 output, although the output can be another function), Elm acts more like that everything is a function hence why a 'value' like `v = v + 1` is possible, it acts like a function of zero arguments that can then call itself, so that is more equal to OCaml's `let rec v () = (v ()) + 1`, however Elm still compiles it to a value, specifically it compiles it to, in javascript, `var _user$testProject$TestProject$v = _user$testProject$TestProject$v + 1`, where javascript has an OCaml-like built namespace, thus things can only reference what was built prior to it, however javascript has the oddity where when a `val` is defined the 'name' is bound before it is given a value (like a `let rec` in OCaml without the `and`, to be able to be used for recursive function calls, however this means that when the name is defined it is set to 'undefined' as it has not yet been defined even though it has been bound (like doing `var v` by itself), thus `var v = v + 1` is equal to doing `var v = undefined + 1`, which becomes `var v = NaN`.  This is fixable in Elm in a few ways, such as by perhaps separating zero-arg function to n-arg functions in the compiler, or by changing how namespacing works, either would fix this particular issue (although I do wish it would change both).  I'm curious which direction will be taken as it will change some semantics about the language as a whole (though changing the 0-arg function/value handling to not bound a name before usage would be a less noticeable change, I do wish the namespace would change to a built namespace, although that means that 'main' would always be at the bottom of a file then so I do not see this change happening).

Janis Voigtländer

unread,
Oct 3, 2016, 11:59:08 AM10/3/16
to elm-d...@googlegroups.com

2016-10-03 17:33 GMT+02:00 OvermindDL1 <overm...@gmail.com>:
 I'm curious which direction will be taken

OvermindDL1

unread,
Oct 3, 2016, 1:18:17 PM10/3/16
to Elm Discuss
Ooo, awesome, I did not know it was finalized yet.  :-)

Hmm, so it looks like it went the way I thought by disallowing adding itself to its scope.  It looks like it will resolve mutually recursive values too like:
```elm
a = b + 1
b = a - 1
```
As it would fail to sort the top level values with a bad recursion error, nice, I was curious if that would be caught too.  :-)
Reply all
Reply to author
Forward
0 new messages