我是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
的任何。
有人能让我直截了当吗?
答案 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
和(*)
一起玩自己的游戏来实现这一目标。特别是,他选择了呼叫者所做的相同类型。但是现在Num
和Integer
只要求他证明这种类型是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_requiredstaticjumppoints
中staticjumppointfilter()
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
。由于Num
是constant
的子类,因此a
的类型签名无效,因为调用方可以为Num
选择Fractional
但Int
但Integer
的类型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
的成员保证提供整数转换(即整数值的默认类型,如1
或1000
) - fromInteger
函数。
但是,他们无法保证提供有理数字的转换(例如1.0
) - Fractional
类型类确实将此作为fromRational
函数提供,但它并不重要,因为您只使用Num
。