2014-11-02 56 views
1

在ghci中,所述:t命令不会看到什么问题在这样的表达式3 4(sin . (+)) 1 2 3 4sin . (+) 1 2 3 4(-) - (-)并愉快地提供他们的类型信息:浮动(一 - > a)中,NUM(一 - >一 - >一)等在Haskell

3 4 :: (Num (a -> t), Num a) => t 

sin . (+) 1 2 3 4 
    :: (Floating c, Num (a1 -> a2 -> a -> c), Num a1, Num a2) => a -> c 

(sin . (+)) 1 2 3 4 
    :: (Floating ((a -> a1 -> t) -> a -> a1 -> t), Num (a -> a1 -> t), 
     Num a, Num a1) => 
    t 

(-) - (-) :: (Num (a -> a -> a), Num a) => a -> a -> a 

我相信这背后有一个原因。但我几乎无法想象(3 4)有什么意义(我不是指实用的东西,而是至少可以编译的东西),并且如果有人给我一个线索,我会很感激。

+1

事实上,'3 4'不编译!如果没有,你会看到一个类型错误,而不是一个类型。实际上并没有关系,因为'3 4'编译的原因是类型类是开放的,所以有人可能会出现并定义一个实例'Num(a - > a)'在这个点上'3 4 '有价值。 – user2407038 2014-11-02 04:43:10

+1

我不知道这是否是重复本身,而是看到[这个问题和它的答案(http://stackoverflow.com/q/26515102/1186208)。谢谢你(含蓄地)同意我说'(3 4)'是一种可憎的东西 - 但是,如果你写这个实例,它就会遵循并且毫无怨言地运行。 (相当于3.) – 2014-11-02 04:46:07

回答

4

很多事情“typecheck”与疯狂或不可能推断约束。我们有map :: (a -> b) -> [a] -> [b]的原因之一,而不是仅仅严格更一般的fmap :: Functor f => (a -> b) -> f a -> f b是,如果你错误地使用后者,你很可能最终会出现错误No instance (Functor [something obviously not a functor])而不是更友好的Expecting [a], got ...

Num/Floating的情况尤其令人不安,因为所有数字文字都是使用类型类重载的。编译器会高兴地推断出任何整型文字的任何类型,并且如果可能的话,就把它作为一个约束条件。通常这会导致实例解析错误。

此外,它可能写一个不完全无意义的instance Num b => Num (a -> b)That's a different question, though.正如我在那里指出的那样,它引起了我的质疑,并且故意不在base中。 (或者,就此而言,我查看了替代数字类型类软件包。)

+1

我认为能够定义'Num(a - > b)'本身不是错误的,但这是GHC可能更有帮助的一些情况之一,它解释如果类型推断结束需要一个不存在的函数实例,真正的问题是*可能*将函数应用于错误的参数。 – 2014-11-02 05:38:09

+1

我已经看过一个Num实例,用于演示之前使用的函数,以便在数字上获得自然的单位,例如“3千米”,其设置为自动转换为基本单位。这很聪明,但我从来没有用它在真实的代码 – bheklilr 2014-11-02 16:26:05

+0

感谢您的原因和链接。 – zabolekar 2014-11-02 18:52:28

1

您几乎可以制作任何东西Num。如果FlexibleInstances开启时,你可能有一个主要-不确定,但是定义的,足以对演示实例:

{-# LANGUAGE FlexibleInstances #-} 
instance Num (Integer -> Integer) where 
    fromInteger = (+) 
    (+) = (.) 
    (-) = undefined 
    (*) = undefined 
    negate = undefined 
    abs = undefined 
    signum = undefined 

然后有一些类型的注释,你可以得到一个合理的结果:

ghci> 2 (3 :: Integer) :: Integer 
5 
+0

谢谢你的例子。不知道这是真棒还是可怕的。 – zabolekar 2014-11-02 18:54:43

1

Haskell提供了很多其他语言不具备的灵活性。这样做的代价是有很多情况下,其他语言会给你一个语法或类型错误,但Haskell不会。让我们把你的例子:

3 4 :: (Num (a -> t), Num a) => t 

在Haskell,如果您有任何两个语法有效的表达,使他们彼此相邻,其间还为您提供了一个有效的表达空白。因此,例如,如果fx两种表达,这也是一个表达式:

f x 

这种类型的表达的被称为功能应用,或简称只是应用

除此语法规则外,还有一个输入规则。我会写上前面的表达式类型注释:

((f :: a -> b) (x :: a)) :: b 

也就是说,对于应用程序得到很好的类型的,那么对于类型abf :: a -> bx :: af x :: b一些选择。

它使用什么类型推断你的程序的语法结构和任何显式类型的注释,您和您使用已经提供给弄清楚,可以分配给你的表达最普遍的类型库。

所以现在,回到你的例子:

3 4 :: (Num (a -> t), Num a) => t 

这种表达是语法上有效的应用,因为它具有f x形式我上面所解释的。此外,整数常量33都有一段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操作。隐含的缺点是你所说的:错误信息将不那么有用。

相关问题