如何解释函数签名

Sco*_*rod 3 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
Run Code Online (Sandbox Code Playgroud)

但是,我不知道那个签名是什么意思。

我读它如下:

有一个名为map的函数,它将一个函数作为我们称之为“mapping”的输入,它会产生一个结果,它也是一个我们称之为“option”的函数。

映射参数:

mapping:('T -> 'U)
Run Code Online (Sandbox Code Playgroud)

我们作为输入传入的函数以(即'T)作为输入并产生(即'U)作为输出。

选项返回

option:'T option -> 'U option
Run Code Online (Sandbox Code Playgroud)

我们将调用映射函数的输出“选项”。因此,执行map函数返回的这个“选项”也是上面提到的一个函数。它需要一个钛选项并产生一个铀选项

例子:

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
Run Code Online (Sandbox Code Playgroud)

以下表达式如何映射:

Option.map String20 suffix
Run Code Online (Sandbox Code Playgroud)

根据上面的表达式,Titanium option -> Uranium option的“返回函数”在哪里?

Guy*_*der 5

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

表达式映射 f inp 计算结果匹配 inp 与 None -> None | 一些 x -> 一些 (fx)。

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

let OptionMap = f inp =
    match inp with
    | None -> None
    | Some x -> Some (f x)
Run Code Online (Sandbox Code Playgroud)

现在要获取此函数的签名,只需将其发送到 F# Interactive

val OptionMap : f:('a -> 'b) -> inp:'a option -> 'b option
Run Code Online (Sandbox Code Playgroud)

为了使类型显而易见,我们将键入函数的参数。

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

现在要测试这个,我们可以使用

let (test : String20 option) = optionMap String20 (Some("something"))
printfn "test: %A" test
// test: Some (String20 "something")
Run Code Online (Sandbox Code Playgroud)

那么在 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
Run Code Online (Sandbox Code Playgroud)

我们得到

f:   <fun:test@63>
inp: Some "something"
result: Some (String20 "something")
Run Code Online (Sandbox Code Playgroud)

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

那么 String20 怎么可能是一个函数呢?

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

> String20;;
val it : arg0:string -> String20 = <fun:clo@4>
Run Code Online (Sandbox Code Playgroud)

所以 String20 是一个函数,它接受一个字符串并返回一个 String20 的类型。那种构造函数的味道。

让我们在 F# 交互中测试一下。

> let test1 = String20 "something";;

val test1 : String20 = String20 "something"
Run Code Online (Sandbox Code Playgroud)

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

您必须将类型,甚至是可区分的联合视为具有构造函数。构造函数不是专门写的,但确实存在。所以 String20 是一个构造函数,它接受一个值,一个字符串,它是一个具有 Option.map 函数正确类型签名的函数。

我给出了更详细的答案,以便人们可以学习如何分解问题的过程,并将内部运作视为解决此类问题的工具。

要了解更多关于函数式编程如何工作的底层细节,需要了解lambda 演算。我所知道的最好的学习方法是阅读Greg Michaelson 的An Introduction To Functional Programming Through Lambda Calculus并查看lambda calculus 标签中的信息

另外,如果您喜欢这本书,则应该购买副本而不是使用免费版本。