I get the following error message when I compile:

Duplicate type signature:
weightedMedian.hs:71:0-39: findVal :: [ValPair] -> Double -> Double
weightedMedian.hs:68:0-36: findVal :: [ValPair] -> Int -> Double

My solution is to have findValI and findValD. However, findValI just converts the Int type to a Double and calls findValD.

Also I can't pattern match on types of Num (Int, Double) so I can't just change the type signature to

findVal :: [ValPair] -> Num -> Double   

In many languages I wouldn't need different names. Why do I need different names in Haskell? Would this be hard to add to the language? Or are there dragons there?

有帮助吗?

解决方案

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top