我仍然对如何阅读函数签名感到困惑。
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的“返回函数”在哪里?
首先看看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 标签中的信息。
另外,如果您喜欢这本书,则应该购买副本而不是使用免费版本。