2016-01-20 53 views
3

我仍然对如何读取函数签名感到困惑。如何解释函数签名

的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) 

,我们在通过作为输入该函数采用(即 'T)作为输入并产生(即' 作为输出U)。

的选项返回

option:'T option -> 'U option 

我们将调用地图功能“选项”的输出。因此,从执行地图函数返回的这个“选项”也是如上所述的函数。这需要钛选项并产生铀选项

实施例:

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 

- >铀选项

+2

我在想你什么时候会问这个问题。 –

+1

返回值没有名称,所以'map'函数有两个参数 - 'mapping:('T - >'U)'和'option:'T option'。 – Lee

+0

嗯......我以为“ - >”是指产量。因此,它在两个参数之间。 –

回答

4

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

表达映射f INP的计算结果为与无INP匹配 - >无| 一些x - >一些(f x)。

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

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

我们得到这个函数的签名只是其发送到F#互动

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:[email protected]> 
inp: Some "something" 
result: Some (String20 "something") 

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

那么,怎样才能String20是一个函数?

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

> String20;; 
val it : arg0:string -> String20 = <fun:[email protected]> 

所以String20是一个函数,它接受一个字符串并返回一个String20类型。这是一个构造函数的气味。我们在F#interactive中测试一下。

> 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中的信息。

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

+0

感谢Guy Coder。我挂断了“ - >”操作员。我认为这总是意味着收益。我不知道它也是函数签名中的参数分隔符。 –

+1

看[currying](https://en.wikipedia.org/wiki/Currying) –

+0

我也有类似的问题。只有当我花时间学习所有的基础知识时,它才会变得更加清晰。由于函数式编程就像数学一样,当数学老师在棋盘上写出变换序列并跳过几行时,就会非常混乱。当添加跳过的行时,它更有意义。花更多的时间在基础上,它会得到回报。我自己在基本功能上做过[数千次基本测试](https://github.com/jack-pappas/fsharp-logic-examples/blob/master/FSharpx.Books.AutomatedReasoning.Tests/lib.fs),它确实已付。 –

2

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

我觉得很困惑时调用的函数“选项”,一个字符串“钛”和一个叫String20“铀”的类型,所以我会用类型名称坚持。

你问在映射字符串选项到String20选项的“返回函数”在表达式Option.map String20 suffix中。这只不过是

Option.map String20 

由于String20从一个字符串构造一个String20,Option.map String20是映射一个字符串转换为String20如果它存在的功能,并没有其他。

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

2

String20案例构造函数String20判别联合案。这是一个功能与类型string -> String20。因此string代替'TString20代替'U在您提供给Option.mapmapping中。

4

有两种查看方法。

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

一种方式是说,功能f需要两个参数,intxyint类型,并返回一个int。所以,我可以提供两个参数并得到结果:

let a = f 4 5 
// val a: int = 9 

另一种方法是,该功能需要一个参数,intx,并返回另一个功能,这需要一个参数,inty,和返回int。所以,我可以提供一个参数,并得到一个功能结果:

let b = f 4 
// val b: int -> int 

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