编译时收到以下错误消息:
重复类型签名:
weightedMedian.hs:71:0-39:findVal :: [ValPair] - >双 - >双
weightedMedian.hs:68:0-36:findVal :: [ValPair] - > Int - >双
我的解决方案是找到findValI和findValD。但是,findValI只是将Int类型转换为Double并调用findValD。
此外,我无法在Num(Int,Double)类型上进行模式匹配,因此我不能只将类型签名更改为
findVal :: [ValPair] -> Num -> Double
在许多语言中,我不需要不同的名字。为什么我在Haskell中需要不同的名字?这会很难添加到语言中吗?或者那里有龙吗?
答案 0 :(得分:32)
特殊多态(和名称重载)在Haskell中由类型类提供:
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)
请注意,在这种情况下,由于findVal
“确实”需要一个Double
,我只需要一个双倍,当我需要传递一个int时,只需使用{ {1}}在通话网站上。当涉及到实际上不同的行为或逻辑时,通常需要类型类,而不是混乱。
答案 1 :(得分:16)
支持findVal :: [ValPair] -> Double -> Double
和findVal :: [ValPair] -> Int -> Double
需要ad-hoc多态(请参阅http://www.haskell.org/haskellwiki/Ad-hoc_polymorphism),这通常很危险。原因是ad-hoc多态允许使用相同的语法更改语义。
Haskell更喜欢所谓的参数多态。你总是会看到类型签名,你有一个类型变量。
Haskell通过类型类支持更安全的ad-hoc多态性版本。
您有三种选择。
像这样:
findVal :: Num a => [ValPair] -> a -> Double
{-# SPECIALISE findVal :: [ValPair] -> Int -> Double #-}
{-# SPECIALISE findVal :: [ValPair] -> Double -> Double #-}
findVal = ...
答案 2 :(得分:6)
Haskell不支持C ++样式的重载(好吧它与类型类有关,但我们不以同样的方式使用它们)。是的,有一些龙与添加它有关,主要与类型推断有关(变成指数时间或不可判断的或类似的东西)。但是,在Haskell中看到像这样的“方便”代码是非常罕见的。哪一个是Int
还是Double
?由于您的Int
方法委托给Double
方法,我的猜测是Double
是“正确的”方法。只需使用那个。由于文字重载,您仍然可以将其称为:
findVal whatever 42
42
将被视为Double
。唯一可以解决的问题是如果你在某处从根本上一个Int
,你需要将它作为这个参数传递。然后使用fromIntegral
。但是如果你努力让你的代码在任何地方使用“正确”的类型,这种情况将是不常见的(当你必须转换时,值得引起注意)。
答案 3 :(得分:3)
在这种情况下,我认为编写一个处理第二个参数的Int和Double的函数很容易。只需编写findVal
,以便在第二个参数上调用realToFrac
。这会将Int
转换为Double
,只留下Double
。然后让编译器为你推断出类型,如果你是懒惰的。
答案 4 :(得分:0)
在许多其他编程语言中,您可以声明(种类)具有相同名称但签名中具有不同其他内容的函数,例如不同的参数类型。这称为重载,当然也是实现ad-hoc多态的最常用方法。
Haskell故意不支持重载,因为它的设计者并不认为它是实现ad-hoc多态的最佳方法。 Haskell方式是受约束的多态,它涉及声明类型类和类实例