Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing partial active patterns as arguments?

I'm learning F# by writing a recursive descent parser using active patterns.

Since all my rules or partial active patterns I need to combine them in different manners, but I'm getting really frustrated with the syntax of passing active patterns as parameters.

The following example shows the trouble I'm having:

// Combines two patterns by chaining them.
let (|Chain|_|) (|Pattern1|_|) (* Should I use pipes here? *) (|Pattern2|_|) data =
    match data with
    |Pattern1 result ->
        match result with
        |Pattern2 result2 -> Some result2
        |_ -> None
    |_ -> None 

// Stupid test patterns
let (|IfBiggerThan10ThenDouble|_|) value = if value > 10 then Some (value*2) else None
let (|IfLessThan100ThenDouble|_ |) value = if value < 100 then Some (value*2) else None

match 20 with
// Do I need pipes here?
|Chain (IfBiggerThan10ThenDouble IfLessThan100ThenDouble) value -> printfn "%A" value // Should print 80
| _ -> printfn "Did not match"

My main confusion seems to be about the '|' operator. Sometimes it seems to be a part of the type of the pattern and sometimes part of the name.

like image 551
monoceres Avatar asked Mar 25 '14 13:03

monoceres


1 Answers

You do not really need to implement your own chaining of patterns, because you can directly nest the patterns which gives you the required result:

match 20 with
| IfBiggerThan10ThenDouble(IfLessThan100ThenDouble value) -> printfn "%A" value
| _ -> printfn "Did not match"

This will first call the IfBiggerThan10ThenDouble pattern which calculates 20*2 and passes the value to the nested pattern IfLessThan100ThenDouble. This again doubles the value and binds it to the value symbol (when it succeeds).

That said, your implementation of the Chain pattern actually works and can be called like this:

match 20 with
| Chain (|IfBiggerThan10ThenDouble|_|) (|IfLessThan100ThenDouble|_|) value -> 
    printfn "%A" value // Should print 80
| _ -> printfn "Did not match"

In general, active pattern (|P|_|) is really just a function with a special name. You can treat it as an ordinary function and call it by writing (|P|_|) argument or you can treat it as a value and pass it as an argument to other functions or parameterized active patterns. Your code would work if you implemented Chain as a pattern taking ordinary functions:

let (|Chain|_|) f g data =
    f data |> Option.bind (fun r -> g data)

Then Chain <arg1> <arg2> <pat> is just calling the parameterized active pattern with two functions as an argument. When called, it binds the result to the pattern <pat>. In the above example, the two arguments are function values representing the patterns (these could be ordinary functions, but not lambda functions because of syntactic restrictions).

like image 191
Tomas Petricek Avatar answered Oct 21 '22 20:10

Tomas Petricek