Haskell了解功能定义

时间:2018-08-20 14:44:12

标签: haskell syntax

我不能很好地找到以下方面的手册: 如果我想在Haskell中获得功能的定义,那么我正在使用class ProgramTemplate < ActiveRecord::Base #belongs_to :patient #belongs_to :program belongs_to :account #belongs_to :account_patient has_many :exercises, dependent: :destroy accepts_nested_attributes_for :exercises, allow_destroy: true #has_many :program_sessions, dependent: :destroy # accepts_nested_attributes_for :exercises, reject_if: lambda { |e| e[:published].blank? }, allow_destroy: true validates :name, presence: true validate :min_no_of_exercises 例如:

:t

我会得到:

:t sqrt
:t (+)
:t truncate

我只是不确定如何了解这里的语法,涉及sqrt :: Floating a => a -> a (+) :: Num a => a -> a -> a truncate :: (RealFrac a, Integral b) => a -> b =>。 我知道->与函数的定义有关,其余的与函数定义有关。这对于全面了解功能非常重要。

2 个答案:

答案 0 :(得分:5)

  

我知道::是关于定义函数的

er,不。 ::表示您要提供类型签名,这不一定与定义函数有关(尽管在实践中,您为每个编写的函数,即使不编写,编译器也会推断出一个签名。)

sqrt :: Double -> Double       -- } type signature

sqrt 0 = 0                     -- ⎫
sqrt 1 = 1                     -- ⎪
sqrt 2 = 1.4  -- should do     -- ⎬ definition
sqrt 4 = 2                     -- ⎪
sqrt _ = error "too hard"      -- ⎭

现在-正如您所看到的,这些签名实际上有些不同,而不仅仅是Double -> Double。具体来说,签名sqrt :: Floating a => a -> a解析为

sqrt :: forall a .
       (Floating a)
      => (a -> a)

这意味着,对于所有类型a(包括但不限于Double)(包括但不限于instance Floating),这实际上都适用。也就是说,类型必须支持Floating class的接口。对于这些类型中的每一种,签名都是“自身类型”,即

sqrt :: Double -> Double
sqrt :: Complex Double -> Complex Double
sqrt :: Float -> Float
sqrt :: ExactSymbolic -> ExactSymbolic

,但不是sqrt :: String -> String,因为String既不是Floating的实例,也不是Bool -> Char的实例,后者甚至都不匹配a -> a模式。

对于其他示例,它是相似的:

  • (+) :: Num a => a -> a -> a定义了可以用作例如Num class中所有类型的Int -> Int -> IntRational -> Rational -> Rational
  • truncate :: (RealFrac a, Integral b) => a -> b实际上可以在两种不同的类型之间进行转换,例如Double -> IntRational -> Integer。要求参数为RealFrac class,结果为Integral class

如何定义所有这些功能是另一回事–类型签名并不能告诉您(尽管对于特别的“基本”功能,实际上可能只有一种明智的实现方式)给定类型签名)。要查看函数的定义方式,您需要查看源代码。这通常是从Haddock文档中链接的,签名的右边是小Source链接。对于(+)上的Double之类的内置函数,您将找不到任何定义,因为它们实际上只能解析为primop,即硬件处理器指令。

答案 1 :(得分:0)

作者编辑 尽管对于我的基础水平和OP基础水平而言,这种解释足够接近,但此答复简直是错误的,请参阅下面的评论。请考虑其他答案。

一个接受Integer并返回Float的函数将被编写为Integer -> Float

但是,您的功能可能更通用。例如,它可以处理任何类型并返回String。诸如a之类的虚拟名称将用于输入类型。由于签名在a上是可变的,因此对可变参数类型的依赖性将用=>书写。因此,签名将为a => a -> String。如果您曾经做过类型理论,那就是Π型。否则,将=>看作是用来表示“类型函数”的箭头。在此,“类型的函数”采用类型a,并返回类型a -> String,这是常规函数类型。 (请注意,=>的优先级比->的低,否则就没有意义了。)

如果您依赖的类型不能为任何类型,但必须是具有某些属性的类型,则必须通过在类型之前给出类名称来指定其属性类。在您的问题中,一种行为像实值浮点数的类型为Floating

因此,Floating a => a -> a是一个依赖函数类型,取决于a,它必须是Floating类。同样,(RealFrac a, Integral b) => a -> b是双重依赖的函数类型,第一个依赖项必须是RealFrac类,而第二个必须是Integral类。

::只是值(或在类型理论中为“见证”)与其类型之间的分隔符。 truncate :: (RealFrac a, Integral b) => a -> b只是意味着truncate是{双依赖)类型(RealFrac a, Integral b) => a -> b