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

2

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?

Follicle answered 27/11, 2012 at 18:12 Comment(0)
Y
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(...)

Yuri answered 27/11, 2012 at 18:16 Comment(1)
Thanks, this is an eye-opener, kvb! I'll definitely have to keep that last example in mind, in particular.Follicle
E
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 )
Echo answered 27/11, 2012 at 18:43 Comment(6)
Not quite everywhere... At least use bindings and the this identifier in member declarations are restricted to use proper identifiers rather than patterns, sadly.Yuri
@Yuri 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.Juetta
@Yuri That is an excellent point, ah well, the conflict between theory and practice strikes again :-)Echo
@RamonSnir Hmm, you could almost read as self as the standard <pat> as <var> 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 :-)Echo
In the case of <pat> as <var>, you can easily make it two patterns (instead of a pattern and a label) by using <pat> & <pat>. I wonder what would people think if some language supported type Foo(x : int, y : float) & Bar(z : string).Juetta
Looks like you can't use patterns in obj type-matching, either. ( #64747918 )Follicle

© 2022 - 2024 — McMap. All rights reserved.