Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why has Haskell troubles resolving "overloaded" operators?

This post poses the question for the case of !! . The accepted answer tell us that what you are actually doing is creating a new function !! and then you should avoid importing the standard one.

But, why to do so if the new function is to be applied to different types than the standard one? Is not the compiler able to choose the right one according to its parameters? Is there any compiler flag to allow this?

For instance, if * is not defined for [Float] * Float

Why the compiler cries

>  Ambiguous occurrence *
>  It could refer to either `Main.*', defined at Vec.hs:4:1
>                          or `Prelude.*',

for this code:

(*) :: [Float] -> Float -> [Float]
(*) as k = map (\a -> a*k) as  -- here: clearly Float*Float


r = [1.0, 2.0, 3.0] :: [Float]

s = r * 2.0 -- here: clearly [Float] * Float

main = do
     print r
     print s
like image 208
cibercitizen1 Avatar asked Jan 08 '23 08:01

cibercitizen1


2 Answers

Allowing the compiler to choose the correct implementation of a function based on its type is the purpose of typeclasses. It is not possible without them.

For a justification of this approach, you might read the paper that introduced them: How to make ad-hoc polymorphism less ad hoc [PDF].

like image 171
Rein Henrichs Avatar answered Jan 09 '23 22:01

Rein Henrichs


Really, the reason is this: in Haskell, there is not necessarily a clear association “variable x has type T.

Haskell is almost as flexible as dynamic languages, in the sense that any type can be a type variable, i.e. can have polymorphic type. But whereas in dynamic languages (and also e.g. OO polymorphism or C++ templates), the types of such type-variables are basically just extra information attached to the value-variables in your code (so an overloaded operator can see: argument is an Int->do this, is a String->do that), in Haskell the type variables live in a completely seperate scope in the type language. This gives you many advantages, for instance higher-kinded polymorphism is pretty much impossible without such a system. However, it also means it's harder to reason about how overloaded functions should be resolved. If Haskell allowed you to just write overloads and assume the compiler does its best guess at resolving the ambiguity, you'd often end up with strange error messages in unexpected places. (Actually, this can easily happen with overloads even if you have no Hindley-Milner type system. C++ is notorious for it.)

Instead, Haskell chooses to force overloads to be explicit. You must first define a type class before you can overload methods, and though this can't completely preclude confusing compilation errors it makes them much easier to avoid. Also, it lets you express polymorphic methods with type resolution that couldn't be expressed with traditional overloading, in particular polymorphic results (which is great for writing very easily reusable code).

like image 26
leftaroundabout Avatar answered Jan 09 '23 23:01

leftaroundabout