elm/compiler

Runtime error when comparing functions

Opened this issue ยท 5 comments

The following:

f x = x
g y = y
b = f == g

gives me a runtime error "Equality error: general function equality is undecidable, and therefore, unsupported" when run on http://elm-lang.org/try. I guess this should give a type inference error instead.

This has often been discussed. In GitHub issues like here and elsewhere, also on the mailing list, here and again in other threads.

The most complete discussion about why this is not a thing that can easily be made a type error caught by the compiler is probably this one: elm-lang/elm-plans#9.

A pragmatic alternative is this one: https://github.com/elm-lang/core/pull/213. But it's also not favored by everyone.

So, don't expect anything soon. ๐Ÿ˜ž

I'll stop ranting on elm-discuss (I think I've mostly kept this off of elm-dev) and simply post to the discussion here. If it were just function equality that were a problem, the simple answer would be to disallow it. But it isn't because functions can be nested further down and fixing that would require threading equatable all the way through the compiler. One could just say "live with it". After all, some people get lucky and never get bitten. But if you come from most other languages, you are likely to be highly surprised if equality can throw a runtime exception โ€” particularly in a language that says "No Runtime Exceptions" on its home page. Write the "right" sort of code and you're fine, but that's true in every language. And the problem is that error cases are non-obvious. For example, the following code throws in 0.17:

import Html exposing (..)

import Http

task1 = Http.getString "http://somewhere"

task2 = Http.getString "http://somewhere"

main = text <| if task1 == task2 then "Match" else "No match"

Given that example, if you use code you didn't write โ€” including the core libraries โ€” you arguably can't trust that you can use == on any of the values it produces. That's a pretty harsh restriction when it comes to the "right" sort of code and might actually argue for simply removing the == operator.

Rather than going that far, it would make programs more robust if the runtime simply returned False when it could not readily prove two functions equal. Yes, this isn't necessarily correct. Yes, it means that (\a -> a) == identitywill probably evaluate to False when it is actually True. On the other hand, it only programs that would otherwise have been terminated with a runtime exception that will experience these erroneous values so one could argue that there are programmers who want to write mathematically correct programs and who hence will not write code that compares functions and there are programmers who want to get stuff done without worrying that any time they type == they are inviting a runtime exception.

Changing the definition of (==) would arguably be the simplest solution but has issues with referential transparency. Introducing an equatable type class turns the runtime error into a compile time error but is one of the most difficult solutions to implement. So, how about adding the following functions somewhere:

{- definitelyEqual works like (==) but rather than throwing an exception
when it cannot compare values, it simply returns false. -}
definitelyEqual : a -> a -> Bool

{- definitelyUnequal works like (/=) but rather than throwing an exception
when it cannot compare values, it simply returns false. -}
definitelyUnequal : a -> a -> Bool

Where two values would generally be expected to return true for one of (==) and (/=) โ€” though NaN breaks that assumption! โ€” definitelyEqual and definitelyUnequal could both return false.

zoren commented

I agree making functions non-equatable is probably the most difficult solution to implement. But on the other hand I also think it's the best solution. I think it's what you would expect from a language like Elm.

I agree. It's the tact that haskell takes, and I think it makes sense to follow suit. A general solution for function equality is impossible.