Overloading function signatures haskell

断了今生、忘了曾经 提交于 2019-12-03 04:35:44

Ad-hoc polymorphism (and name overloading) are provided in Haskell by typeclasses:

class CanFindVal a where
          findVal :: [ValPair] -> a -> Double

instance CanFindVal Double where
     findVal xs d = ...

instance CanFindVal Int where
     findVal xs d = findVal xs (fromIntegral d :: Double)

Note that in this case, since findVal "really" needs a Double, I'd just always have it take a double, and when I needed to pass it an int, just use fromIntegral at the call site. You generally want typeclasses when there's actually different behavior or logic involved, rather than promiscuously.

Supporting both findVal :: [ValPair] -> Double -> Double and findVal :: [ValPair] -> Int -> Double requires ad-hoc polymorphism (see http://www.haskell.org/haskellwiki/Ad-hoc_polymorphism) which is generally dangerous. The reason is that ad-hoc polymorphism allows for changing semantics with the same syntax.

Haskell prefers what is called parametric polymorphism. You see this all the time with type signatures, where you have a type variable.

Haskell supports a safer version of ad-hoc polymorphism via type classes.

You have three options.

  1. Continue what you are doing with an explicit function name. This is reasonable, it is even used by some c libraries, for example opengl.
  2. Use a custom type class. This is probably the best way, but is heavy and requires a fair amount of code (by haskells very compact standards). Look at sclv's answer for code.
  3. Try using an existing type class and (if you use GHC) get the performance with specializations.

Like this:

findVal :: Num a => [ValPair] -> a -> Double
{-# SPECIALISE findVal :: [ValPair] -> Int -> Double #-}
{-# SPECIALISE findVal :: [ValPair] -> Double -> Double #-}
findVal = ...

Haskell does not support C++-style overloading (well it sortof does with typeclasses, but we don't use them in the same way). And yeah there are some dragons associated to adding it, mostly having to do with type inference (becomes exponential time or undecidable or something like that). However, seeing "convenience" code like this is pretty uncommon in Haskell. Which one is it, an Int or a Double? Since your Int method delegates to the Double method, my guess is that Double is the "correct" one. Just use that one. Because of literal overloading, you can still call it as:

findVal whatever 42

And the 42 will be treated as a Double. The only case where this breaks is if you got something that is fundamentally an Int somewhere and you need need to pass it as this argument. Then use fromIntegral. But if you strive to have your code use the "correct" type everywhere, this case will be uncommon (and when you do have to convert, it will be worth drawing attention to that).

In this case, I think it's easy to write a function that handles both Int and Double for the second argument. Just write findVal so that is calls realToFrac on the second argument. That will convert the Int to a Double and just leave a Double alone. Then let the compiler deduce the type for you, if you are lazy.

In many other programming languages you can declare (sort of) functions having the same name but different other things in their signatures, such as different parameter types. That is called overloading and certainly is the most popular way to achieve ad-hoc polymorphism.

Haskell deliberately does NOT support overloading because it's designers don't consider it as the best way to achieve ad-hoc polymorphism. The Haskell way rather is constrained polymorphism and it involves declaring type classes and class instances

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