Why has Haskell troubles resolving “overloaded” operators?

前提是你 提交于 2019-12-12 13:37:53

问题


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

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




回答2:


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




回答3:


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



来源:https://stackoverflow.com/questions/33177019/why-has-haskell-troubles-resolving-overloaded-operators

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!