Scott Nimrod
Scott Nimrod

Reputation: 11595

How to interpret a function signature

I am still confused on how to read function signatures.

The Option.map signature is the following:

/// 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

However, I have no clue what that signature means.

I read it as the following:

There's a function called map that takes a function as an input that we'll call "mapping" and it will yield a result that is also a function that we'll call "option".

Mapping Parameter:

mapping:('T -> 'U)

The function that we pass in as input takes Titanium (i.e. 'T) as the input and yields Uranium (i.e. 'U) as output.

The Option returned

option:'T option -> 'U option

We'll call the output of the map function "option". Thus, this "option" that is returned from executing the map function is also a function as referenced above. It's takes a Titanium option and yields a Uranium option.

Example:

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

How does the following expression map:

Option.map String20 suffix

Based on the expression above, where is the "returned function" of Titanium option -> Uranium option?

Upvotes: 3

Views: 276

Answers (4)

Guy Coder
Guy Coder

Reputation: 24976

First off take a look at Option.map<'T,'U> Function (F#) and notice

The expression map f inp evaluates to match inp with None -> None | Some x -> Some (f x).

So lets convert this comment to working code. First map is a method of the type Option, but to make it easier we will make it a function outside of a type and to avoid conflicts with other map functions we will give it the name OptionMap.

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

Now to get the signature of this function just send it to F# Interactive

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

and to make the types obvious we will type the parameters of the function.

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

Now to test this we can use

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

So what happened in OptionMap that allowed this to work

If we add some print statements let sees what happens

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

we get

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

we see that f is a function which is String20

So how can String20 be a function?

If we send String20 to F# Interactive it gives.

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

So String20 is a function that takes a string and returns a type of String20. That smells of a constructor.

Let's test that out in F# interactive.

> let test1 = String20 "something";;

val test1 : String20 = String20 "something"

Sure enough, String20 is a constructor, but we didn't specifically create a constructor as is done in the Object-Oriented world.

You have to think of a type, even a discriminated union as having constructors. The constructors are not specifically written but do exist. So String20 is a constructor that takes one value, a string, which is a function with the correct type signature for the Option.map function.

I gave the answer a lot more detail so that one can learn a process on how to break down problems and look at the inner workings as a tool to solving these kinds of problems.

To learn more about the lower level details of how functional programming works one needs to understand lambda calculus. The best way I know to learn it is to read An Introduction To Functional Programming Through Lambda Calculus by Greg Michaelson and look at the info in the lambda calculus tag.

Also if you like the book, you should buy a copy instead of using the free version.

Upvotes: 5

Fyodor Soikin
Fyodor Soikin

Reputation: 80734

There are two ways of looking at it.

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

One way is to say that function f takes two parameters, x of type int and y of type int, and returns an int. So I can supply two arguments and get the result:

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

The other way is that the function takes one parameter, x of type int, and returns another function, which takes one parameter, y of type int, and returns an int. So I can supply one argument and get a function as result:

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

Mathematically, it's always the second way - all functions are one-parameter functions. This notion is very convenient for programming with higher-order functions. But the first way is usually more understandable to humans, so you will often see functions discussed as if they take multiple parameters.

Upvotes: 4

Vandroiy
Vandroiy

Reputation: 6223

Generally, if you have a function T1 -> T2 -> ... and you apply it to one parameter, you get a function T2 -> .... In the case of Option.map, T1 is itself a function, but that is of no consequence to the way the arguments are applied.

I find it confusing to call a function "option", a string "Titanium" and a type called String20 "Uranium", so I'll stick with type names.

You ask where the "returned function" that maps a string option to a String20 option is in the expression Option.map String20 suffix. It is simply

Option.map String20

Since String20 constructs a String20 from a string, Option.map String20 is a function that maps a string to a String20 if it is present, and to None otherwise.

If you write Option.map String20 suffix, this function is applied to suffix, which would be a string option. The result of this expression is a String20 option.

Upvotes: 2

Mark Seemann
Mark Seemann

Reputation: 233135

String20 is the case constructor for the String20 Discriminated Union case. It's a function with the type string -> String20. So string takes the place of 'T, and String20 takes the place of 'U in the mapping you supply to Option.map.

Upvotes: 2

Related Questions