Haskell有返回类型重载吗?

时间:2012-07-10 19:03:53

标签: haskell polymorphism

基于我读过的关于Haskell的内容,以及我用GHC做过的实验,似乎Haskell有返回类型重载(又称ad hoc多态)。其中一个示例是fromInteger函数,根据结果的使用位置,它可以为您提供DoubleInteger。例如:

fd :: Double -> String
fd x = "Double"

fi :: Integer -> String
fi x = "Integer"

fd (fromInteger 5)  -- returns "Double"
fi (fromInteger 5)  -- returns "Integer"

A Gentle Introduction to Haskell似乎同意这一点:

  

到目前为止,我们所讨论的多态性通常称为参数多态。另一种称为ad hoc多态,更好地称为重载。以下是ad hoc多态的一些示例:

     
      
  • 文字1,2等通常用于表示固定和任意精度整数。
  •   

如果数字文字被认为是ad hoc多态(也就是重载)的一个例子,那么对fromInteger等函数的结果似乎也是如此。

事实上,我发现other questions on Stack Overflow的一些答案表明Haskell已经按返回类型进行了重载。

但是,至少有一个Haskell程序员告诉我这不是返回类型重载,而是“参数多态,其中参数受通用量词限制”的例子。

认为他得到的是fromIntegerNum的每个实例返回一个值(一种非确定性类型)。

这似乎是一个合理的解释,但据我所知,Haskell从不让我们看到这些实例值中的多个(部分归功于Monomorphism restriction)。我们看来的实际情况似乎也可以静态确定。由于所有这一切,似乎可以合理地说,在表达式fd (fromInteger 5)中,子表达式fromInteger 5的类型为Double,而在表达式fi (fromInteger 5)中的子表达式{{1}类型为fromInteger 5

那么,Haskell是否有返回类型重载?

如果没有,请提供以下其中一项的示例:

  • 有效的Haskell代码,如果Haskell返回类型重载,则会有不同的行为
  • 有效的Haskell代码,如果Haskell返回类型重载,则无效
  • 无效的Haskell代码,如果Haskell返回类型重载,则该代码有效

5 个答案:

答案 0 :(得分:16)

好吧,一种看待它的方法是,Haskell将您正在考虑的返回类型多态转换为参数多态,使用称为字典传递翻译的类型类。 (虽然这不是实现类型类或推理它们的唯一方法;但它只是最受欢迎的。)

基本上,fromInteger在Haskell中有这种类型:

fromInteger :: Num a => Integer -> a

可能会在内部翻译成这样的内容:

fromInteger# :: NumDictionary# a -> Integer -> a
fromInteger# NumDictionary# { fromInteger = method } x = method x

data NumDictionary# a = NumDictionary# { ...
                                       , fromInteger :: Integer -> a
                                       , ... }

因此,对于具有T实例的每个具体类型Num,都有一个包含函数NumDictionary# T的{​​{1}}值,以及使用fromInteger :: Integer -> T的所有代码将type类翻译成以字典作为参数的代码。

答案 1 :(得分:12)

关于Haskell风格类型类的开创性论文称为"How to make ad-hoc polymorphism less ad hoc"。所以,你的问题的答案是合格的“是” - 取决于你需要返回类型重载的临时性......

换句话说:毫无疑问,ad hoc多态与<相关与类型类相关,因为这是发明它们的一个激励性例子。但是,您是否认为结果仍然符合“返回类型重载”的要求取决于您喜欢的定义的细节。

答案 2 :(得分:12)

我想谈谈你问题的一小部分:

  

我们看来的实际情况似乎也可以静态确定。

这不是很准确。考虑以下古怪的数据类型:

data PerfectlyBalancedTree a
    = Leaf a
    | Branch (PerfectlyBalancedTree (a,a))
    deriving (Eq, Ord, Show, Read)

在我们转向好位之前,先让我们先看一下这种类型。以下是PerfectlyBalancedTree Integer类型的一些典型值:

Leaf 0
Branch (Leaf (0, 0))
Branch (Branch (Leaf ((0,0),(0,0))))
Branch (Branch (Branch (Leaf (((0,0),(0,0)),((0,0),(0,0))))))

实际上,您可以将此类型的任何值可视化为n Branch标记的初始序列,然后是“我们终于完成了,谢天谢地”Leaf标记后跟2 ^包含类型的n-tuple。凉。

现在,我们将编写一个函数来解析这些更方便的表示。这是一些示例调用:

*Main> :t fromString
fromString :: String -> PerfectlyBalancedTree Integer
*Main> fromString "0"
Leaf 0
*Main> fromString "b(42,69)"
Branch (Leaf (42,69))
*Main> fromString "bbb(((0,0),(0,0)),((0,0),(0,0)))"
Branch (Branch (Branch (Leaf (((0,0),(0,0)),((0,0),(0,0))))))

一路上,编写稍微更多态的函数会很方便。这是:

fromString' :: Read a => String -> PerfectlyBalancedTree a
fromString' ('b':rest) = Branch (fromString' rest)
fromString' leaf = Leaf (read leaf)

现在我们的真实功能与具有不同类型签名的功能相同:

fromString :: String -> PerfectlyBalancedTree Integer
fromString = fromString'

但等一下......发生在这里的是什么?我很快就滑倒了!我们为什么不直接写这个?

fromStringNoGood :: String -> PerfectlyBalancedTree Integer
fromStringNoGood ('b':rest) = Branch (fromStringNoGood rest)
fromStringNoGood leaf = Leaf (read leaf)

原因是在递归调用中, fromStringNoGood具有不同的类型。它没有被要求返回PerfectlyBalancedTree Integer,而是要求它返回PerfectlyBalancedTree (Integer, Integer)。我们可以写自己这样的功能......

fromStringStillNoGood :: String -> PerfectlyBalancedTree Integer
fromStringStillNoGood ('b':rest) = Branch (helper rest)
fromStringStillNoGood leaf = Leaf (read leaf)

helper :: String -> PerfectlyBalancedTree (Integer, Integer)
helper ('b':rest) = Branch ({- ... what goes here, now? -})
helper leaf = Leaf (read leaf)

...但是这种方式是对深度和深度嵌套类型的写作无限回归。

问题在于,即使我们对单态顶级函数感兴趣,我们仍然无法静态地确定read是什么类型在它使用的多态函数中调用!我们传递的数据决定了应该返回的元组read的类型:b中的String更多意味着更深层次的嵌套元组。

答案 3 :(得分:4)

你是对的:Haskell确实有重载,它通过它的类型机制提供它。

考虑以下签名:

f :: [a] -> a
g :: Num a => [a] -> a

第一个签名告诉您,如果给定任何类型a的元素列表,f将生成类型a的值。这意味着f的实现不能对类型a及其承认的操作做出任何假设。这是参数多态的示例。片刻的反思揭示了实现f的实际选择很少:你唯一能做的就是从提供的列表中选择一个元素。从概念上讲,f的单一通用实现适用于所有类型a

第二个签名告诉您,给定属于类型类a的某种类型Num的元素列表,g将生成该类型{{1}的值}。这意味着a的实现可以使用类型类g附带的所有操作来使用,生成和操作类型a的值。例如,Num可以添加或乘以列表的元素,选择列表的最小值,返回提升的常量,......这是重载的示例,通常是被认为是ad-hoc多态的一种形式(另一种主要形式是强制)。从概念上讲,g中所有类型g的{​​{1}}实现了不同的实现。

答案 4 :(得分:1)

它有返回类型重载。有关示例,请参阅读取功能。它的类型为Read a =&gt;字符串 - &gt;一个。它可以读取和返回任何实现读类型类的内容。