1

我定义了一个函数来计算sqrt和转换Integral类的参数:

isqrt :: Integral i => i -> i
isqrt = floor . sqrt . fromIntegral

我不明白为什么会编译。如果我们写出各个函数的签名,我们会得到:

fromIntegral :: (Num b, Integral a) => a -> b
sqrt :: Floating a => a -> a
floor :: (RealFrac a, Integral b) => a -> b

所以 " sqrt" 需要一些Floating类型,但它提供了Num. 如果你看一下类层次结构,你可以看到Floating“继承”,Num但不是相反。我会理解是否Floating可以隐含地对待它,Num因为它是一种更“专业”的类型。为什么这对编译器来说是可以的?

4

2 回答 2

3

sqrt不提供任何.Num

它的供应商fromIntegral确实能够根据需要生产任何产品Num,而sqrt需要 aFloating作为其输入。并且Floating Num

所以很fromIntegral高兴地答应了。既然它可以产生Num任何类型,那么它肯定可以产生任何类型的任何子类Num。所以无论它最终是什么具体的具体类型,它都是 in Floating,因此它必然是 in Num

因此fromIntegral提供它没有问题。

编辑: Floating不是一种类型。它是一类类型。具体类型可能属于Floating类型类。由于Floating是 的子类Num,因此这种类型也保证在Num. 这不是由于您提到的“继承”而自动发生的事情。“继承”即子类关系是对特定类型的要求,该特定类型Floating也将是(in)Num即实现Num的方法以及来自 的方法Floating

所以是的,(特定的,具体的)Floating类型也可以被视为一种Num类型。

另一方面,sqrt产生与其输入类型相同的类型,因此它将是相同的具体Floating类型。但floor期望一个RealFrac类型。

观察

> :t floor            ----------
floor :: (Integral b, RealFrac a) => a -> b

> :t sqrt . fromIntegral            ----------
sqrt . fromIntegral :: (Integral a, Floating c) => a -> c

> :t floor . sqrt            ----------  ----------
floor . sqrt :: (Integral c, RealFrac b, Floating b) => b -> c

> :i Floating
class Fractional a => Floating a where
  ....
instance Floating Float
instance Floating Double

> :i RealFrac
class (Real a, Fractional a) => RealFrac a where
  ....
instance RealFrac Float
instance RealFrac Double

因此,该调用产生(并因此被接受)的具体类型sqrt必须是 (in)RealFracFloating.

由于从该应用程序链之外没有观察到它可能是任何兼容类型,因此是模棱两可的;但是键入 defaulting会启动并选择Double,除非您更改了默认值。

于 2021-11-05T17:50:08.093 回答
2

所以sqrt需要一些Floating类型,但它提供了Num.

没有fromIntegral保证它可以将具有类型类成员类型的任何数字转换Integral任何类型 Num,而不是类型 Num

由于sqrt需要一个类型类成员的数字,因此Floating对于该特定情况,fromIntegral将专门返回属于类型类成员的Floating类型。

于 2021-11-05T17:51:40.067 回答