using a function inside a discriminated union - f#

I would like to do some unit tests on a function that accepts a DU and returns another :
type Commands =
| Schedule of string
| Abandon of string
type Events =
| Scheduled of string
| Abandonned of string
the function is the following :
let exec state = function
| Schedule (cmd) -> Choice1Of2( Scheduled("yes"))
| Abandon(cmd) -> Choice1Of2( Abandonned ("no"))
My tests are as follows :
let result:Choice<Events,string> = exec "initial state" <| Schedule("myCommand");;
result has the following type Choice<Events,string>, I would have loved to get some quick function in order to use them like this :
assertEvent Scheduled (fun e -> Assert.Equal("should produce GameScheduled Event",gameScheduled, e)) result
But to do that I would have the following home made assert function :
let assertEvent<'TEvent> f g result =
match result with
| Choice1Of2(e) ->
match e with
| f(evt) -> g(evt)
| _ -> Assert.None("event not recognised",Some(e))
| Choice2Of2(reason) -> Assert.None("reason",Some(reason))
I was expecting the function f to allow pattern matching on the fly but it does not. Instead I have the following error :
The pattern disciminator 'f' is not defined
Am I doing somthing wrong somewhere ? my fsharp skills are not that high...

A normal function like f can't be used as a pattern discriminator, but you can pass Active Patterns around as arguments:
let assertEvent<'TEvent> (|F|_|) g result =
match result with
| Choice1Of2(e) ->
match e with
| F(evt) -> g(evt)
| _ -> Assert.None("event not recognised",Some(e))
| Choice2Of2(reason) -> Assert.None("reason",Some(reason))
This does, however, require you to also pass an Active Pattern as an argument, which is a bit cumbersome:
assertEvent
(function Scheduled(x) -> Some x | _ -> None)
(fun e -> Assert.Equal("should produce GameScheduled Event",gameScheduled, e))
result
This isn't the way I'd approach the problem, though. What I'd prefer is to write a boolean expression that attempts to pull out and compare the values that I want to verify.
For starters, you could create a little generic helper function to pull out one of the choices from Choice<'a, 'b>:
let toOption1 = function Choice1Of2 x -> Some x | _ -> None
This function has the type Choice<'a,'b> -> 'a option. (I'll leave it as an exercise to define an equivalent toOption2 function.)
Now you can define a boolean expression that pulls out the data if it's there, and compares it with an expected value:
result
|> toOption1
|> Option.map (function Scheduled x -> x | _ -> "")
|> Option.exists ((=) expected)
This is a boolean expression, so you can use Unquote to turn it into an assertion. This is similar to this approach that I've previously described.

Related

How can I get values with discriminited union type from recursive function

I am trying to obtain a specific value from one list with a multiple recursion so I have :
type PowerSystem =
| System of string * int
| Junction of string * List<PowerSystem>
let Starship =
Junction("Core",
[
Junction("Users",
[
System("Main Computer",-10);
System("Library Computer",-10);
Junction("Defence",)]
let rec JunctionPath (pSystem:PowerSystem) =
match pSystem with
| Junction(name,aList) -> SplitList2 aList
| System(name,aNumber) -> [name]
and SplitList2 list =
match list with
| [] -> printfn "%A" []
| head::tail -> printfn "%A" List.filter (fun e->e="Port Phasers" (JunctionPath head)#(SplitList2 tail))
JunctionPath Starship
I get error FS0001: The type ''a -> string list' does not match the type 'unit'
I want to get the name of junctions when the system is called Main Computer but I can't call other functions. I tryed in different ways to get those values but I can't found the way. Thanks in advance
SplitList2 only has the side effect of printfn hence returns unit ().
In JunctionPath the first branch returns a list of string - [name] - and the second a unit due to calling SplitList2. This is not allowed as both (in general all) pattern match branches should return the same type. This is why you get your error "error FS0001: The type ''a -> string list' does not match the type 'unit'"
(It would be clearer if you, in future, note the line references in the error as that points to the problematic line).
Now in answer to your last point:
I want to get the name of junctions when the system is called Main
Computer but I can't call other functions. I tryed in different ways
to get those values but I can't found the way.
this does not make sense since what is "Port Phasers"? Anyway, without testing, I have modified SplitList2 to return a list on either branch. Then we pipe the result of JunctionPath into a printfn. However the logic of the line List.filter (fun e->e="Port Phasers" (JunctionPath head)#(SplitList2 tail)) still does not make sense, so I updated it as I think you meant it (although you should have had another error for this?). (This is not tail recursive and can be improved but one step at a time)
type PowerSystem =
| System of string * int
| Junction of string * List<PowerSystem>
let Starship =
Junction("Core",
[
Junction("Users",
[
System("Main Computer",-10);
System("Library Computer",-10);
Junction("Defence",)]
let rec JunctionPath (pSystem:PowerSystem) =
match pSystem with
| Junction(name,aList) -> SplitList2 aList
| System(name,aNumber) -> [name]
and SplitList2 list =
match list with
| [] -> []
| head::tail -> List.filter (fun e->e="Port Phasers") (JunctionPath head)#(SplitList2 tail))
JunctionPath Starship |> printfn "%A%

final output of Result, in F#

This seems like a question that has an ultra simple answer, but I can't think of it:
Is there a built in method, within Result, for:
let (a: Result<'a, 'a>) = ...
match a with
| Ok x -> x
| Error e -> e
No, because this function requires the Ok type and the Error type to be the same, which makes Result less general.
No, there isn't any function which will allow you to do so. But you can easily define it:
[<RequireQualifiedAccess>]
module Result =
let join (value: Result<'a, 'a>) =
match value with
| Ok v -> v
| Error e -> e
let getResult s =
if System.String.IsNullOrEmpty s then
Error s
else
Ok s
let a =
getResult "asd"
|> Result.join
|> printfn "%s"
It doesn't make Result less general (as said by #brianberns), because it's not an instance member. Existence of Unwrap doesn't make Task less general
Update
After more scrupulous searching inside FSharpPlus and FSharpx.Extras I've found necessary function. It's signature ('a -> 'c) -> ('b -> 'c) -> Result<'a,'b> -> c instead of Result<'a, 'a> -> 'a and it's called Result.either in both libraries (source 1 and source 2). So in order to get value we may pass id as both parameters:
#r "nuget:FSharpPlus"
open FSharpPlus
// OR
#r "nuget:FSharpx.Extras"
open FSharpx
getResult "asd"
|> Result.either id id
|> printfn "%s"
Also it's may be useful to define shortcut and call it Result.join or Result.fromEither as it's called in Haskell

How to do pattern matching in Rx. Where and Select in a single operator?

Suppose I have this type:
type T = int option
and an observable of that type:
let o : IObservable<T> = // create the observable
I'm looking for a better way to express this:
o.Where(function | None -> false | Some t -> true)
.Select(function | Some t -> t)
An observable that only propagates the Some case.
There are several things that I don't like.
I'm using 2 operators
I'm pattern matching twice
The second pattern matching isn't exhaustive (makes visual studio show a warning and feels odd)
Too much code. The pattern repeats every time I need pattern matching.
Can't you use Observable.choose ? something like this :
let o1 : IObservable<int option> = // ...
let o2 = Observable.choose id o1
If you have a type that is not an option, say:
type TwoSubcases<'a,'b> = | Case1 of 'a | Case2 of 'b
and a partial active pattern:
let (|SecondCase|_|) = function
| Case1 _ -> None
| Case2 b -> Some b
then you can do:
let o1 : IObservable<TwoSubcases<int, float>> = // ...
let o2 : IObservable<float> = Observable.choose (|SecondCase|_|) o1
Thanks to #Lee I came up with a nice solution.
o.SelectMany(function | None -> Observable.Empty() | Some t -> Observable.Return t)
This works for any union type, not only Option.

Pattern matching based on the function signature

In F# can you pattern match on a function signature. I want to decorate a number of functions with a function that measures the execution of the function and calls out to statsd. The current function I have is:
let WrapFunctionWithPrefix(metrics:Metric.Client.IRecorder, functionToWrap, prefix) =
let metricsIdentifier = (sprintf "%s.%s" prefix Environment.MachineName)
using (metrics.StartTimer(metricsIdentifier)) ( fun metrics -> functionToWrap)
As you can see above, the prefix will vary, and in our application this will vary per function definition. So rather than having to pass in the measure prefix every time I want to do something like the following:
let WrapFunction metrics afunc =
match afunc with
| :? (int -> int) -> WrapFunctionWithPrefix(metrics, afunc, "My function 1")
| :? (string -> string) -> WrapFunctionWithPrefix(metrics, afunc, "My function 2")
| _ -> failwith "Unknown function def"
Is there any way of pattern matching based on the function signature in F#?
Any help appreciated.
Billy
Would it be possible to declare the cases as a DU?
type MyFunctions =
| Intish of int -> int
| Stringish of string -> string
let WrapFunction metrics afunc =
match box afunc with
| :? (int -> int) -> WrapFunctionWithPrefix(metrics, afunc, "My function 1")
| :? (string -> string) -> WrapFunctionWithPrefix(metrics, afunc, "My function 2")
| _ -> failwith "Unknown function def"
will work for your pattern match. You normally end up having to box unknown types before trying to cast them, as :? doesn't like being used on value types.
I'm not totally sure how your using statement will interact with the function you return though. I think it will dispose metrics and return the function immediately, which is probably not what you want.

F# pattern matching when mixing DU's and other values

What would be the most effective way to express the following code?
match cond.EvalBool() with
| true ->
match body.Eval() with
| :? ControlFlowModifier as e ->
match e with
| Break(scope) -> e :> obj //Break is a DU element of ControlFlowModifier
| _ -> next() //other members of CFM should call next()
| _ -> next() //all other values should call next()
| false -> null
cond.EvalBool returns a boolean result where false should return null
and true should either run the entire block again (its wrapped in a func called next)
or if the special value of break is found, then the loop should exit and return the break value.
Is there any way to compress that block of code to something smaller?
I think that the code that you have written is fine. Here's an alternative which I marginally prefer:
let isBreak = function | Break(_) -> true | _ -> false
if cond.EvalBool() then
match body.Eval() with
| :? ControlFlowModifier as e when isBreak e -> e :> obj
| _ -> next()
else
null
I want to point out that it appears there's a subtype hierarchy for the result type of Eval, and if instead that were also a DU, then you could do something like
match body.Eval() with
| ControlFlowModifier(Break e) -> box e
| _ -> next()
Hurray for nested patterns.
I'm not too fond of matching booleans instead of using if-else. What about
let isBreak = function Break _ -> true | _ -> false
...
if cond.EvalBool() then
match body.Eval() with
| :? ControlFlowModifier as e when isBreak e -> box e
| _ -> next()
else null
Or, if you think that special isBreak function shouldn't be necessary (I'd understand that), lets try creating a more general function: C#'s as operator
let tryCast<'T> (o : obj) =
match o with
| :? 'T as x -> Some x
| _ -> None
...
if cond.EvalBool() then
match body.Eval() |> tryCast with
| Some (Break _ as e) -> box e //Break is a DU element of ControlFlowModifier
| _ -> next() //all other values should call next()
else null
I ended up creating an active pattern for this.
Similar logic exist elsewhere so I could make it reusable
let rec next() : obj =
if cond.EvalBool() then
match body.Eval() with
| IsBreak(res) -> res
| _ -> step.Eval() |> ignore ; next()
else null
Looks decent?
To flatten the nested match constructs, you'll need to use nested patterns. This works best for discriminated unions (as pointed out by Brian - and I agree that designing F# code to use primarily discriminated unions is the best thing you can do).
Otherwise, you'll need some active patterns if you want to write the code succinctly using match (ssp posted one example, which shows active patterns specifically for your problem). However, you can do this using the following two reusable active patterns:
let (|TryCast|_|) a : 'res option =
match (box a) with
| :? 'res as r -> Some(r)
| _ -> None
let (|Value|) (l:Lazy<_>) = l.Value
The first one is like :?, but it allows you to nest other patterns to match the value (which isn't possible with as). The second one forces evaluation of lazy value (I suppose that both of them could be declared in F# libraries as they are quite useful). Now you can write:
match lazy cond.EvalBool(), lazy body.Eval() with
| Value(true), Value(TryCast((Break(scope) : ControlFlowModifier)) as e) ->
e :> obj //Break is a DU element of ControlFlowModifier
| Value(true), _ ->
next() //all other values should call next()
| _, _ -> null
EDIT: As Roger pointed out in a comment, this version of the code may not be very readable. I think a better option would be to use only TryCast and format your original code slightly differently (although this isn't completely standard indentation, it is correct and F# compiler handles it fine):
match cond.EvalBool() with
| false -> null
| true ->
match body.Eval() with
| TryCast(Break(scope) as e) -> e :> obj
| _ -> next()
This is probably the most readable option based on pattern matching, but you could also use if instad of the first match as in the version by kvb and combine it with TryCast (this really depends on personal preferences):
if cond.EvalBool() then
match body.Eval() with
| TryCast(Break(scope) as e) -> e :> obj
| _ -> next()
else null
In any case, I believe that TryCast makes the code more readable as you avoid one nesting (which is othervise required because of :? .. as ..).
In case you mean "most effective way" as shortest code, i vote to AP too:
let (|CondEval|_|) (c,_) = if c.EvalBool() then Some true else None
let (|BodyEval|_|) (_,b) =
match b.Eval() with
| ControlFlowModifier as e -> Some e
| _ -> None
match cond,body with
| CondEval _ & BodyEval e -> e :> obj
| true -> next()
| false -> null

Resources