Haskell是否有返回类型重载?

根据我读过的关于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"

Haskell的一个温柔的介绍似乎同意这一点,当它说:

迄今为止我们讨论的多态性通常称为参数多态性。 还有另一种叫做ad hoc多态的,更好的称为重载。 以下是特别多态性的一些示例:

  • 文字1,2等通常用于表示固定和任意精度整数。
  • 如果数字文字被认为是临时多态性(又名重载)的一个例子,那么看起来像fromInteger这样的函数的结果也是如此。

    实际上,我发现Stack Overflow上的其他问题的一些答案表明Haskell按返回类型重载。

    然而,至少有一位Haskell程序员告诉我,这不是返回类型重载,而是“参数多态,其中参数受全局量词约束”的例子。

    我认为他得到的是来自fromInteger从每个Num实例返回的值(某种非确定类型)。

    这似乎是一个合理的解释,但据我所知,Haskell从来没有让我们看到这些实例值中的一个以上(部分归因于单态限制)。 它也似乎是我们看待的实际情况,可以静态确定。 因为这一切,似乎有理由说,在表达fd (fromInteger 5)子表达式fromInteger 5的类型为Double ,而在表达fi (fromInteger 5)子表达式fromInteger 5的类型为Integer

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

    如果不是,请提供以下示例之一:

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

  • 那么,看看它的一种方法是,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
                                           , ... }
    

    因此,对于每个具有Num实例的具体类型T ,都有一个包含函数fromInteger :: Integer -> TNumDictionary# T值,并且所有使用Num类型类的代码都被转换为将字典作为参数的代码。


    我想解决你的问题的一小部分:

    它也似乎是我们看待的实际情况,可以静态确定。

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

    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元组。 凉。

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

    *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应该返回: String更多的b表示更深的嵌套元组。


    关于Haskell风格类型类的开创性论文被称为“如何使ad-hoc多态性不那么特别”。 所以,你的问题的答案是一个合格的“是” - 取决于你是如何特别要求你的返回类型超载是...

    换句话说:特殊的多态性与类型类相关是毫无疑问的,因为这是发明它们的动机。 但是,您是否认为结果仍然符合“返回类型超载”的标准取决于您最喜欢的定义的细节。

    链接地址: http://www.djcxy.com/p/43125.html

    上一篇: Does Haskell have return type overloading?

    下一篇: Effects of monomorphism restriction on type class constraints