Curried Arguments in Discriminated Unions

299 views Asked by At

I have a discriminated union like this:

type A = |B | C of int*A

I have to pattern match like this (the parenthesis appear to be needed):

match x with
| B -> printfn "B"
| C (i,a) -> printfn "%A, %A" i a

Is there a way to instead match like this with something like an active pattern:

match x with
| B -> printfn "B"
| C i a -> printfn "%A, %A" i a

And if not how come F# is designed such that this matching with curried arguments doesn't work and it instead forces you to use a tuple?

Edit: This was inspired by the F# list in which you can use h::t without any tupling or anything like that. And the source code is like:

type List<'T> = 
   | ([])  :                  'T list
   | (::)  : Head: 'T * Tail: 'T list -> 'T list
3

There are 3 answers

0
kaefer On BEST ANSWER

You cannot have whitespace as delimiter between bound patterns, because neither union cases nor active patterns support this. Syntax as per the F# spec:

6.9.8 Evaluating Union Case

Case(e1,…,en)

7.2.3 Active Patterns

(|CaseName|) arg1 ... argn inp
(|CaseName|_|) arg1 ... argn inp

So it's necessarily one tupled argument for a union case; and n+1 arguments for the banana function, of which n arguments are parameters. Only the last argument binds to the pattern. Consider:

type X = B | C
let (|C|) a b = C (a, b)
let i = 42

match C with
| B -> printfn "B"
| C i a -> printfn "%A, %A" i a // prints 42, (42, C)
1
marklam On

The case C in your discriminated union has a value of a tuple type (int * A).

The (i,a) part of your pattern matching isn't a parameter, it's matching the i to the int part and the a to the A part.

You could equally match with C x and x would hold a tuple of (int * A).

2
N_A On

I think examining the definitions of a curried function and an active pattern will make this clear for you.

Curried function: A function which takes multiple parameters but which allows you to pass them in one at a time in order to return a function which does the same thing but takes one fewer parameters. Example:

let add a b = a + b
//val add : a:int -> b:int -> int
let add5 = add 5
//val add5 : (int -> int)

Active Pattern: A way of applying pattern matching where the matching can be done using parsing or other complex logic. Takes one parameter and returns the result of the parsing. So input -> single return parameter.

//Example taken from https://fsharpforfunandprofit.com/posts/convenience-active-patterns/
let (|Int|_|) str =
   match System.Int32.TryParse(str) with
   | (true,int) -> Some(int)
   | _ -> None
val ( |Int|_| ) : str:string -> int option

Since the whole point of currying a function is to be able to partially apply the function, the concept simply makes no sense when applied to the result of an active pattern.

Put another way, the result of an active pattern can't be "curried" because you can only curry functions and the result of an active pattern is data which is not a function. In your example, 'C (i,a)' is defining the return type of the Active Pattern case, not a function call.

Related Questions in F#