Haskell提供了很多其他语言不具备的灵活性。这样做的代价是有很多情况下,其他语言会给你一个语法或类型错误,但Haskell不会。让我们把你的例子:
3 4 :: (Num (a -> t), Num a) => t
在Haskell,如果您有任何两个语法有效的表达,使他们彼此相邻,其间还为您提供了一个有效的表达空白。因此,例如,如果f
和x
两种表达,这也是一个表达式:
f x
这种类型的表达的被称为功能应用,或简称只是应用。
除此语法规则外,还有一个输入规则。我会写上前面的表达式类型注释:
((f :: a -> b) (x :: a)) :: b
也就是说,对于应用程序得到很好的类型的,那么对于类型a
和b
,f :: a -> b
,x :: a
和f x :: b
一些选择。
它使用什么类型推断你的程序的语法结构和任何显式类型的注释,您和您使用已经提供给弄清楚,可以分配给你的表达最普遍的类型库。
所以现在,回到你的例子:
3 4 :: (Num (a -> t), Num a) => t
这种表达是语法上有效的应用,因为它具有f x
形式我上面所解释的。此外,整数常量3
和3
都有一段a
(为一个字面的每次出现独立地选自)类型Num a => a
。把所有这些东西放在一起,好吧,我们有一个句法结构良好的表达式,如果单独采用,也会进行类型检查。
你可能认为这是疯狂的,在某些情况下,一个整数文字可能是一个功能......但好,没有这么多。一个例子是:我曾经在那里我使用的Haskell到原型数据转换和查询,其中在DSL表达式静置功能从输入帧(查询条件)到值的域专用语言的项目。举例来说,一个框架可能是一组对年/月值及营业员的,和值可能是由销售人员在该月的售电量,或单位出售数量:
sales :: (YearMonth, Salesperson) -> Money
units :: (YearMonth, Salesperson) -> Integer
有的straightfoward方式(其中icktoofay的回答证明)的扩展Num
类,让我们写这样的表述:
avgSales :: (YearMonth, Salesperson) -> Money
avgSales = sales/fromIntegral units
,并延长Num
类这样也可以让我们像对待3
整数文字作为文字DSL。对应于3
功能为常数函数返回3
无论(YearMonth, Salesperson)
组合是什么:
salesTimesThree :: (YearMonth, Salesperson) -> Money
salesTimesThree = sales * 3
在实践中你不能延伸到Num
功能类型,然而,您可以定义为这个新的类型(以及增加对查询做IO来从文件或数据库中的数据的能力):
newtype Query tag point value =
Query { runQuery :: Set (Tagged tag point) -> IO (Map (Tagged tag point) value) }
instance Functor (Query tag point) where ....
instance Applicative (Query tag point) where ....
instance Num value => Num (Query tag point value) where ....
但仍然是利落有作任何类型的成数的能力在那里你可以明智地提供Num
操作。隐含的缺点是你所说的:错误信息将不那么有用。
事实上,'3 4'不编译!如果没有,你会看到一个类型错误,而不是一个类型。实际上并没有关系,因为'3 4'编译的原因是类型类是开放的,所以有人可能会出现并定义一个实例'Num(a - > a)'在这个点上'3 4 '有价值。 – user2407038 2014-11-02 04:43:10
我不知道这是否是重复本身,而是看到[这个问题和它的答案(http://stackoverflow.com/q/26515102/1186208)。谢谢你(含蓄地)同意我说'(3 4)'是一种可憎的东西 - 但是,如果你写这个实例,它就会遵循并且毫无怨言地运行。 (相当于3.) – 2014-11-02 04:46:07