How do I match union cases dynamically in F# when there are value declarations?
Non working code:
let myShape = Shape.Square
expect myShape Shape.Circle
type Shape =
| Circle of int
| Square of int
| Rectangle of ( int * int )
let expect someShape someUnionCase =
if not ( someShape = someUnionCase )
then failwith ( sprintf "Expected shape %A. Found shape %A" someShape someUnionCase )
let myShape = Shape.Square
expect myShape Shape.Circle // Here I want to compare the value types, not the values
If my union cases did not declare values, this works using instantiation samples (which is not what I want):
let myShape = Shape.Square
expect myShape Shape.Circle
type Shape =
| Circle
| Square
| Rectangle
let expect someShape someUnionCase =
if not ( someShape = someUnionCase )
then failwith ( sprintf "Expected shape %A. Found shape %A" someShape someUnionCase )
let myShape = Shape.Square
expect myShape Shape.Circle // Comparing values instead of types
When you call the
expectfunction in your example with e.g.Shape.Squareas an argument, you’re actually passing it a function that takes the arguments of the union case and builds a value.Analyzing functions dynamically is quite difficult, but you could instead pass it concrete values (like
Shape.Square(0)) and check that their shape is the same (ignore the numeric arguments). This can be done using F# reflection. TheFSharpValue.GetUnionFieldsfunction returns the name of the case of an object, together withobj[]of all the arguments (which you can ignore):If you now compare
SquarewithCircle, the function throws, but if you compare twoSquares, it works (even if the values are different):If you wanted to avoid creating concrete values, you could also use F# quotations and write something like
expect <@ Shape.Square @> myValue. That’s a bit more complex, but maybe nicer. Some examples of quotation processing can be found here.