Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does comparable mean in Elm?

Tags:

elm

I'm having trouble understanding what exactly a comparable is in Elm. Elm seems as confused as I am.

On the REPL:

> f1 = (<)
<function> : comparable -> comparable -> Bool

So f1 accepts comparables.

> "a"
"a" : String
> f1 "a" "b"
True : Bool

So it seems String is comparable.

> f2 = (<) 1
<function> : comparable -> Bool

So f2 accepts a comparable.

> f2 "a"
As I infer the type of values flowing through your program, I see a conflict
between these two types:

    comparable

    String

So String is and is not comparable?
Why is the type of f2 not number -> Bool? What other comparables can f2 accept?

like image 211
Mark Bolusmjak Avatar asked Aug 07 '15 19:08

Mark Bolusmjak


3 Answers

Normally when you see a type variable in a type in Elm, this variable is unconstrained. When you then supply something of a specific type, the variable gets replaced by that specific type:

-- says you have a function:
foo : a -> a -> a -> Int
-- then once you give an value with an actual type to foo, all occurences of `a` are replaced by that type:
value : Float
foo value : Float -> Float -> Int

comparable is a type variable with a built-in special meaning. That meaning is that it will only match against "comparable" types, like Int, String and a few others. But otherwise it should behave the same. So I think there is a little bug in the type system, given that you get:

> f2 "a"
As I infer the type of values flowing through your program, I see a conflict
between these two types:

    comparable

    String

If the bug weren't there, you would get:

> f2 "a"
As I infer the type of values flowing through your program, I see a conflict
between these two types:

    Int

    String

EDIT: I opened an issue for this bug

like image 179
Apanatshka Avatar answered Dec 22 '22 22:12

Apanatshka


Compare any two comparable values. Comparable values include String, Char, Int, Float, Time, or a list or tuple containing comparable values. These are also the only values that work as Dict keys or Set members.

taken from the elm docs here.

In older Elm versions:

Comparable types includes numbers, characters, strings,~~ lists of comparable things, and tuples of comparable things. Note that tuples with 7 or more elements are not comparable; why are your tuples so big?

This means that:

[(1,"string"), (2, "another string")] : List (Int, String) -- is comparable

But having

(1, "string",  True)` : (Int, String, Bool) -- or...

[(1,True), (2, False)] : List (Int, Bool ) -- are ***not comparable yet***. 

This issue is discussed here

Note: Usually people encounter problems with the comparable type when they try to use a union type as a Key in a Dict.

Tags and Constructors of union types are not comparable. So the following doesn't even compile.

type SomeUnion = One | Two | Three
Dict.fromList [ (One, "one related"), (Two, "two related") ] : Dict SomeUnion String

Usually when you try to do this, there is a better approach to your data structure. But until this gets decided - an AllDict can be used.

like image 35
AIon Avatar answered Dec 22 '22 21:12

AIon


I think this question can be related to this one. Int and String are both comparable in the sense that strings can be compared to strings and ints can be compared to ints. A function that can take any two comparables would have a signature comparable -> comparable -> ... but within any one evaluation of the function both of the comparables must be of the same type.

I believe the reason f2 is confusing above is that 1 is a number instead of a concrete type (which seems to stop the compiler from recognizing that the comparable must be of a certain type, probably should be fixed). If you were to do:

i = 4 // 2
f1 = (<) i   -- type Int -> Bool
f2 = (<) "a" -- type String -> Bool

you would see it actually does collapse comparable to the correct type when it can.

like image 30
robertjlooby Avatar answered Dec 22 '22 20:12

robertjlooby