2

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
Ringil
  • 6,277
  • 2
  • 23
  • 37
  • 1
    I'm not entirely sure what you mean by "curried arguments" when it comes to an active pattern. A curried argument means that you can partially apply the function. What does that mean for an active pattern? – N_A Aug 26 '16 at 15:16
  • @mydogisbox I say curried because an active pattern like `let (|Test|) a b c = a + b +c` has a signature of `val ( |Test| ) : a:int -> b:int -> c:int -> int` – Ringil Aug 26 '16 at 15:30
  • 2
    No, there is no way. F# is not Haskell. – Fyodor Soikin Aug 26 '16 at 15:36

3 Answers3

3

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.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
N_A
  • 19,799
  • 4
  • 52
  • 98
  • 1
    It's a good answer, but it's not true that it 'makes no sense' to do pattern matching on curried functions; you can do it in Haskell. – Mark Seemann Aug 26 '16 at 17:38
  • @MarkSeemann I was specifically calling out that Active Patterns (not pattern matching generally) return a piece of data which is not analogous to currying which has to do with passing data into a function. Maybe I'm incorrect on that point? – N_A Aug 26 '16 at 20:39
2

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

marklam
  • 5,346
  • 1
  • 24
  • 26
  • Well my question is how come I can't create the values `i` and `a` without the tuple syntax? Maybe there's a way involving changing the signature of the discriminated union? – Ringil Aug 26 '16 at 15:33
2

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)
kaefer
  • 5,491
  • 1
  • 15
  • 20