多态性:常数与函数

时间:2016-09-01 21:53:30

标签: haskell types polymorphism

我是Haskell的新手,在 Haskell Programming from First Principles 一书中遇到了一个有点令人费解的例子。在第6章的最后,我突然想到以下内容不起作用:

constant :: (Num a) => a
constant = 1.0

但是,以下工作正常:

f :: (Num a) => a -> a
f x = 3*x

我可以将x的任何数值输入到函数f中,什么都不会中断。它不仅限于采用整数。这对我来说很直观。但是这个常数的例子让我感到困惑。

在本书的reddit thread上解释(释义),常量示例不起作用的原因是类型声明强制constant的值仅为事物这些并不比Num更具体。因此,尝试为Num的子类Fractional分配一个值不是犹太人。

如果这个解释是正确的,那么我认为这两个例子看起来完全相反是错误的吗?在一种情况下,类型声明强制值尽可能通用。在另一种情况下,函数的可接受值可以是实现Num的任何

有人能让我直截了当吗?

3 个答案:

答案 0 :(得分:5)

有时可以帮助读取类型作为两个演员之间玩的游戏,类型的实现者和类型的用户。为了更好地解释这个观点,我们必须引入Haskell默认隐藏的东西:我们将为所有类型变量添加绑定器。所以你的类型实际上会成为:

constant :: forall a. Num a => a
f :: forall a. Num a => a -> a

现在,我们将阅读类型形成规则:

  • forall a. t表示:来电者选择类型a,游戏继续为t
  • c => t表示:来电者显示约束c成立,游戏继续为t
  • t -> t'表示:来电者选择t类型的值,游戏继续为t'
  • t(其中t是单态类型,例如裸变量或Integer或类似)意味着:实现者生成类型a的值

我们需要一些其他细节来真正理解这里的内容,所以我会在这里快速说出来:

  • 当我们写一个没有小数点的数字时,编译器会隐式地将其转换为对fromInteger的调用,该Integer应用于解析该数字所产生的fromInteger :: forall a. Num a => Integer -> a。我们有fromRational
  • 当我们写一个带小数点的数字时,编译器会隐式地将其转换为对Rational的调用,该fromRational :: forall a. Fractional a => Rational -> a应用于解析该数字所产生的Num。我们有(*) :: forall a. Num a => a -> a -> a
  • constant :: forall a. Num a => a constant = 1.0 {- = fromRational (1 % 1) -} 类包含方法constant

现在让我们试着慢慢地仔细阅读你的两个例子。

Num

fromRational :: Fractional a => Rational -> a的类型表示:调用者选择一个类型,表明此类型实现Fractional,然后实现者必须生成该类型的值。现在,实现者试图通过调用a来玩自己的游戏。他选择调用者所做的相同类型,然后尝试显示此类型实现Num。哎呀!他无法证明这一点,因为调用者向他证明的唯一事情就是a实现Fractional - 这并不能保证constant也实现fromRational 1}}。荡。因此,f的实施者不得以此类型致电f :: forall a. Num a => a -> a f x = 3*x {- = fromInteger 3 * x -}

现在,让我们看看f

Num

(*)的类型说:调用者选择一个类型,显示该类型实现fromInteger,并选择该类型的值。然后,实现者必须生成该类型的另一个值。他将通过与fromInteger(*)一起玩自己的游戏来实现这一目标。特别是,他选择了呼叫者所做的相同类型。但是现在NumInteger只要求他证明这种类型是3的一个实例 - 所以他传递了调用者给他的证据并节省了一天!然后他选择fromInteger (*)作为Num的参数,并选择结果和调用者将其作为a的两个参数传递给他的值。每个人都很满意,并且实现者可以返回一个新值。

这整个阐述的重点是:两种情况下的Num约束都强制完全相同的事情,即我们选择实例化constant = 1.0的任何类型at必须是Num类的成员。只是f x = 3*x中的Num定义不足以执行我们已编写的操作,而final Set<DoubleMatrix1D> l_requiredstaticjumppoints = p_static.stream() .filter( i -> this.staticjumppointfilter( p_currentposition.getQuick( 0 ), p_targetposition.getQuick( 0 ), p_currentposition.getQuick( 1 ), p_targetposition.getQuick( 1 ), i.getQuick( 0 ), i.getQuick( 1 ), p_objects, l_checkdirection ) ) .collect( Collectors.toSet() ); 位于l_requiredstaticjumppointsstaticjumppointfilter() 1}} 足以完成我们编写的操作。而且,由于我们为这两件事选择的操作是如此不同,所以不应该令人惊讶的是一个人工作而另一个人没有!

答案 1 :(得分:4)

当您具有多态值时,调用者会选择要使用的具体类型。 Haskell report定义了数字文字的类型,即:

  

整数和浮动文字的类型(Num a)=&gt;一个和   (分数a)=&gt; a,分别

Num a => a是一个整数文字,因此类型(*)Num a => a -> a -> a的类型为f,因此Num a => a -> a的类型为3.0

相比之下,Fractional a => a的类型为Fractional。由于Numconstant的子类,因此a的类型签名无效,因为调用方可以为Num选择FractionalIntInteger的类型1}}例如data8 = data6.filter("income == 0") data9 = data8.sample(False, 10000/float(data8.count())) print data6.count(), data8.count(), data9.count() 48790 37109 10094 data10 = data6.subtract(data9) data10.count() Py4JJavaError: An error occurred while calling o3692.count. : java.lang.RuntimeException: no default for type org.apache.spark.ml.linalg.VectorUDT@3bfc3ba7

答案 2 :(得分:1)

它们并不意味着相反 - 它们的意思完全相同(“尽可能一般”)。类型类为您提供所有可以依赖的保证 - 如果类型类T提供函数f,您可以将它用于T的所有实例,但即使其中一些实例是G(提供g)也需要T类型类型,不足以调用g

在你的情况下,这意味着:

Num的成员保证提供整数转换(即整数值的默认类型,如11000) - fromInteger函数。

但是,他们无法保证提供有理数字的转换(例如1.0) - Fractional类型类确实将此作为fromRational函数提供,但它并不重要,因为您只使用Num