Something equivalent to assert or unsafe crash

In Haskell I frequently use “error” in development. first it’s a placeholder, able to take on any type, allowing me to see if my program type checks early on. Second, it’s kind of alike “asert” in that it will force a crash in an exceptional situation. I’d like to do something similar in elm. Is this possible? The two criteria are “something that takes on any type” and “something that crashes with an error message”

Mike

Hi Mike and welcome to the Elm community.

You can accomplish the same thing with Debug.todo

Here is the counter example with the decrement branch replaced by a Debug.todo:
https://ellie-app.com/5cSGhHc8zQWa1

There is the added benefit that the compiler will not let you ship Debug.todo in production. If you compile with --optimize it will error if it sees any Debug usage.

3 Likes

If you really need to, you can also write something like that for production usage, however I highly recommend against it.


crash : a -> b
crash arg =
  crash arg

2 Likes

This is not good, as it gets translated to a while loop (since it’s tail recursive).
If this code ever gets executed, the browser just freezes up.
If you really want a function that actually crashes in production, I’d use something like this:

crash : String -> a
crash msg =
    crash (crashHelp msg)

crashHelp : a -> a
crashHelp a =
    crashHelp (crashHelp a)

or this one, which only works because the compiler doesn’t properly optimize this yet:

crash : String -> a
crash x = x |> crash

But if you ever really want to actually use this in production, you should probably question your approach.

2 Likes

You can also incorporate one of the few edge cases where Elm still crashes. I know of two: Comparing functions that are not two references to the same function, for example (\x -> x) == (\x -> x), or dividing by zero in the modulus function, i.e. modBy 0 1. Put one of these into a let binding in the crash function. (You still need to write some kind of recursion to trick the type system. But this way, it crashes on the first call instead of producing a stack overflow.)

It’s not clear if all of your answers are proposing a crash with a message. That’s the critical part, getting the message out so I know what failed.

I think Debug.todo must give a custom message based on what I see, so that will be fine.

ˋDebug.crashˋ is the only way to get a message out with your crash, as far as I know. However, it can’t be used when compiling in production mode, so if you really want something like it in production, the message-less versions above are the only way.

I’m curious, could you explain your use case for the crash? I understand the “type prototyping”, but why do you want it to crash?

You don’t want to crash, of course. But occasionally, Elm’s type system is not strong enough to make every impossible state unrepresentable. In that case, you might end up in a situation where (you think) you know that a certain case cannot come up, but you can’t express it in the types and therefore you have to handle it. I think there are three types of solutions:

  • You might return a Maybe and let the case bubble up to a place where you can handle it. But this is quite a heavy solution if the case is actually impossible.
  • You can decide to return some default value. If the case is impossible it doesn’t matter what you return. But if there is an error in your reasoning and the case is possible after all, instead of just crashing you’re now corrupting your data with this incorrect default value.
  • You can crash. I would argue that this is the best solution for actually impossible cases (because I’d rather crash than corrupt the data), but Elm does not provide a standard solution for that in production code. I guess the main reason is that one tends to get lazy and use this function even in cases where a better solution (for example a remodeling of the types to make the impossible state unrepresentable) exists.

By the way, Elm takes different approaches in the standard library: A lot of functions return Maybes. Division by zero returns a default value (namely zero). Modulo by zero crashes. (I really don’t understand why these two are inconsistent, if division by zero returns zero, modulo by zero should return the number itself.)

It’s not clear if all of your answers are proposing a crash with a message . That’s the critical part, getting the message out so I know what failed.

I would combine then one of the solutions above with Debug.log.

But Debug.log is also not be allowed with --optimize :frowning:

In production, knowing what crashes shouldn’t be necessary, since your crash should never happen there (and if it does, it will happen to someone else, so you won’t know anyway). In development, use Debug.todo.

But Debug.log is also not be allowed with --optimize :frowning:

oh yes, sorry. My answer doesn’t make sense then.

I wasn’t clear - I don’t want this for production code, only for things in development.

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