问题
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?
回答1:
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.
回答2:
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.
- Continue what you are doing with an explicit function name. This is reasonable, it is even used by some c libraries, for example opengl.
- 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.
- 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 = ...
回答3:
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).
回答4:
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.
回答5:
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
来源:https://stackoverflow.com/questions/6119225/overloading-function-signatures-haskell