Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to understand F# active patterns, why can I do this:

I have a Dictionary over which I initially iterated thusly:

myDictionary |> Seq.iter (fun kvp -> doSomething kvp.Key kvp.Value)

Later, I discovered that I could make use of the KeyValue active pattern, and do this:

myDictionary |> Seq.iter (fun (KeyValue (k, v)) -> doSomething k v)

Knowing that active patterns aren't some form of preprocessor directive, how am I able to substitute the kvp argument in the lambda for a function that decomposes it?

like image 546
MiloDC Avatar asked Nov 27 '12 18:11

MiloDC


2 Answers

Functions arguments call always be destructured using pattern matching. For instance:

let getSingleton = fun [x] -> x
let getFirst = fun (a,b) -> a
let failIfNotOne = fun 1 -> ()
let failIfNeitherOne = fun (x,1 | 1,x) -> ()

Semantically, fun<pat>-><body> is roughly equivalent to

fun x ->
match x with
|<pat>-><body>
| _ -> raise MatchFailureException(...)

like image 124
kvb Avatar answered Nov 02 '22 23:11

kvb


I think the answer from @kvb covers in enough details why you can use patterns in the arguments of fun. This is not an ad-hoc feature - in F#, you can use patterns anywhere where you can bind a variable. To show some of the examples by @kvb in another contexts:

// When declaring normal functions     
let foo [it] = it    // Return the value from a singleton list
let fst (a, b) = a   // Return first element of a pair

// When assigning value to a pattern using let
let [it] = list
let (a, b) = pair

Similarly, you can use patterns when writing fun. The match construct is a bit more powerful, because you can specify multiple clauses.

Now, active patterns are not really that magical. They are just normal functions with special names. The compiler searches for active patterns in scope when it finds a named pattern. For example, the pattern you're using is just a function:

val (|KeyValue|) : KeyValuePair<'a,'b> -> 'a * 'b

The pattern turns a KevValuePair object into a normal F# tuple that is then matched by a nested pattern (k, v) (which assigns the first element to k and the second to v). The compiler essentially translates your code to:

myDictionary |> Seq.iter (fun _arg0 ->
  let _arg1 = (|KeyValue|) _arg0
  let (k, v) = _arg1 
  doSomething k v )
like image 41
Tomas Petricek Avatar answered Nov 03 '22 00:11

Tomas Petricek