如何解释函数签名

时间:2016-01-20 12:41:41

标签: f#

我仍然对如何阅读功能签名感到困惑。

Option.map签名如下:

/// map f inp evaluates to match inp with None -> None | Some x -> Some (f x).
/// mapping: A function to apply to the option value.
/// option: The input option.
val map : mapping:('T -> 'U) -> option:'T option -> 'U option

但是,我不知道签名意味着什么。

我将其读作以下内容:

有一个名为地图的功能,它将一个功能作为输入,我们称之为"映射" ,它将会产生一个结果也是我们称之为"选项" 的功能。

映射参数:

mapping:('T -> 'U)

我们作为输入传递的函数将 Titanium (即' T)作为输入并产生 Uranium (即' U)as输出

选项返回

option:'T option -> 'U option

我们会调用地图功能"选项" 的输出。 因此,执行地图功能返回的"选项" 也是一个函数,如上所述。它采用 Titanium选项并产生铀选项

示例:

type String20 = String20 of string

type Name = { First:String20
              Last:String20
              Suffix:String20 option }

let tryCreateName (first:string) (last:string) (suffix:string option) = 

    let isValid = [first; last] 
                  |> List.forall (fun x -> x.Length > 2 && x.Length <= 20)

    if isValid then 
        Some { First  = String20(first); 
               Last   = String20(last); 
               Suffix = Option.map String20 suffix }
    else None

以下表达式如何映射:

Option.map String20 suffix

基于上面的表达式,&#34;返回的函数&#34; Titanium选项 - &gt; 铀选项

4 个答案:

答案 0 :(得分:4)

首先看看Option.map<'T,'U> Function (F#)并注意

  

表达式映射f inp评估以匹配inp与None - &gt;没有|   一些x - &gt;一些(f x)。

因此,我们将此评论转换为工作代码。第一个map是Option类型的方法,但是为了使它更容易,我们将它变成一个类型之外的函数,并且为了避免与其他map函数冲突,我们将它命名为OptionMap。

let OptionMap = f inp =
    match inp with
    | None -> None
    | Some x -> Some (f x)

现在获取此功能的签名只需将其发送给F#Interactive

val OptionMap : f:('a -> 'b) -> inp:'a option -> 'b option

为了使类型明显,我们将输入函数的参数。

let optionMap (f : ('a -> 'b)) (inp : 'a option) : 'b option =
    match inp with
    | None -> None
    | Some x -> Some (f x)

现在测试一下,我们可以使用

let (test : String20 option) = optionMap String20 (Some("something"))
printfn "test: %A" test
// test: Some (String20 "something")

那么在OptionMap中发生了什么让它发挥作用

如果我们添加一些打印语句,请看看会发生什么

let optionMap (f : ('a -> 'b)) (inp : 'a option) : 'b option =
    printfn "f:   %A" f
    printfn "inp: %A" inp
    match inp with
    | None -> None
    | Some x -> 
            let result = Some (f x)
    printfn "result: %A" result
    result

我们得到了

f:   <fun:test@63>
inp: Some "something"
result: Some (String20 "something")

我们看到f是一个函数,它是String20

那么String20如何成为一个函数呢?

如果我们将String20发送到F#Interactive,它会给出。

> String20;;
val it : arg0:string -> String20 = <fun:clo@4>

因此String20是一个接受字符串并返回String20类型的函数。那种气味的构造函数。

让我们在F#中进行测试。

> let test1 = String20 "something";;

val test1 : String20 = String20 "something"

果然,String20是一个构造函数,但我们没有像在面向对象的世界中那样专门创建一个构造函数。

你必须考虑一种类型,即使是一个有区别的联盟也有构造函数。构造函数不是专门编写的,但确实存在。所以String20是一个构造函数,它接受一个值,一个字符串,这是一个具有Option.map函数的正确类型签名的函数。

我给出了更多细节的答案,以便人们可以学习如何解决问题的过程,并将内部工作视为解决这些问题的工具。

要了解有关函数式编程如何工作的低级详细信息,请了解lambda calculus。我知道学习它的最好方法是阅读Greg Michaelson的An Introduction To Functional Programming Through Lambda Calculus并查看lambda calculus tag中的信息。

另外,如果你喜欢这本书,你应该买一本而不是免费版。

答案 1 :(得分:4)

有两种观察方式。

let f x y = x + y
// val f: x:int -> y:int -> int

一种方法是,函数f采用x类型inty类型int两个参数,并返回{{ 1}}。所以我可以提供两个参数并获得结果:

int

另一种方式是该函数接受一个类型为let a = f 4 5 // val a: int = 9 的{​​{1}}参数,并返回另一个函数,该函数接受x类型int的一个参数,并返回y。所以我可以提供一个参数并得到一个函数作为结果:

int

数学上,它始终是第二种方式 - 所有函数都是单参数函数。这个概念对于使用高阶函数进行编程非常方便。 但第一种方式通常对人类来说更容易理解,因此您经常会看到所讨论的函数就像它们采用多个参数一样。

答案 2 :(得分:2)

通常,如果您有一个函数T1 -> T2 -> ...并将其应用于一个参数,则会得到一个函数T2 -> ...。在Option.map的情况下,T1本身就是一个函数,但这与参数的应用方式无关。

我发现调用函数&#34;选项&#34;,字符串&#34; Titanium&#34;还有一种名为String20&#34; Uranium&#34;的类型,所以我会坚持使用类型名称。

你问&#34;返回功能&#34;将字符串选项映射到String20选项的位置在表达式Option.map String20 suffix中。它只是

Option.map String20

由于String20从字符串构造String20,Option.map String20是一个函数,如果存在则将字符串映射到String20,否则映射到None。

如果您编写Option.map String20 suffix,此函数将应用于suffix,这将是一个字符串选项。此表达式的结果是String20选项。

答案 3 :(得分:2)

String20String20歧视联盟案例的案例构造函数。它是类型为string -> String20功能。因此,string代替'T,而String20取代'U mappingOption.map。{/ 1} >