4

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
Community
  • 1
  • 1
cibercitizen1
  • 20,944
  • 16
  • 72
  • 95
  • 1
    you could hint to the compiler with `s = r * 2.0 where (*) = (Main.*)` and `(*) as k = map (\a -> a*k) as where (*) = (Prelude.*)` – rampion Oct 16 '15 at 18:35

3 Answers3

6

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].

Rein Henrichs
  • 15,437
  • 1
  • 45
  • 55
  • It's strange that what is not a problem elsewhere, it is at Haskell. Thanks for your answer. – cibercitizen1 Oct 16 '15 at 19:29
  • it seems that the statement is also true the other way around. Many problems that occur elsewhere, are none in haskell. – epsilonhalbe Oct 16 '15 at 22:56
  • Yes, but that is not a justification for not allowing ad-hoc polymorphism in Haskell. – cibercitizen1 Oct 17 '15 at 08:45
  • @cibercitizen1 Did you read the paper I linked? It discusses some limitations of ad-hoc polymorphism, especially in Hindley-Milner systems. – Rein Henrichs Oct 17 '15 at 20:51
  • @ReinHenrichs Yes I did. Thanks. Well, I understand the limitations, but it seems (to me) that they are limitations more for the compiler than actual limitations. Other languages do allow ad-hoc polymorphism, so I conclude that the reason not to have it in Haskell is a design decision (like I was for ruling out operator overloading in Java). – cibercitizen1 Oct 18 '15 at 07:35
  • 1
    @cibercitizen1 It is a design decision, and I think it's a reasonable one given the options. While your question and comments seem to suggest that Haskell simply chose not to implement ad hoc polymorphism per se, ad hoc polymorphism isn't free: it is in tension with other language design goals. Other languages have made different choices here (or, in some cases, don't have a choice to make, because they can't implement the feature they are giving up anyway). For example, I would prefer type inference to ad hoc polymorphism every time. I couldn't imagine writing Haskell without it. – Rein Henrichs Oct 18 '15 at 18:19
2

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).

leftaroundabout
  • 117,950
  • 5
  • 174
  • 319
1

It is a design decision, not a theoretical problem, not to include this in Haskell. As you say, many other languages use types to disambiguate between terms on an ad-hoc way. But type classes have similar functionality and additionally allow abstraction over things that are overloaded. Type-directed name resolution does not.

Nevertheless, forms of type-directed name resolution have been discussed for Haskell (for example in the context of resolving record field selectors) and are supported by some languages similar to Haskell such as Agda (for data constructors) or Idris (more generally).

kosmikus
  • 19,549
  • 3
  • 51
  • 66