9

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?

MiloDC
  • 2,373
  • 1
  • 16
  • 25

2 Answers2

16

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(...)

kvb
  • 54,864
  • 2
  • 91
  • 133
  • Thanks, this is an eye-opener, kvb! I'll definitely have to keep that last example in mind, in particular. – MiloDC Nov 27 '12 at 18:36
6

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 )
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • 4
    Not quite everywhere... At least `use` bindings and the `this` identifier in member declarations are restricted to use proper identifiers rather than patterns, sadly. – kvb Nov 27 '12 at 18:56
  • 4
    @kvb True and true. Also not for `as self` in implicit-constructor classes. I believe these are the only three cases, where there is actually a meaning to the name. – Ramon Snir Nov 27 '12 at 19:32
  • 1
    @kvb That is an excellent point, ah well, the conflict between theory and practice strikes again :-) – Tomas Petricek Nov 28 '12 at 01:28
  • @RamonSnir Hmm, you could almost read `as self` as the standard ` as ` pattern, but it does not completely make sense because `Foo()` in `type Foo()` is not really a pattern. Not sure what that would mean, but it is a curious thought :-) – Tomas Petricek Nov 28 '12 at 01:31
  • In the case of ` as `, you can easily make it two patterns (instead of a pattern and a label) by using ` & `. I wonder what would people think if some language supported `type Foo(x : int, y : float) & Bar(z : string)`. – Ramon Snir Nov 28 '12 at 07:05
  • Looks like you can't use patterns in obj type-matching, either. ( https://stackoverflow.com/questions/64747418/f-type-test-pattern-matching-decomposing-tuple-objects ) – MiloDC Dec 24 '20 at 10:50