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
Interestingly, this can be done very easily in C#, but the F# compiler will not allow you to call the functions - which seems odd.
The spec says that a discriminated union will have (section 8.5.3):
One CLI instance property u.Tag for each case C that fetches or
computes an integer tag corresponding to the case.
So we can write your expect function in C# trivially
public bool expect (Shape expected, Shape actual)
{
expected.Tag == actual.Tag;
}
It is an interesting question as to why this can't be done in F# code, the spec doesn't appear to give a good reason why.
When you call the expect function in your example with e.g. Shape.Square as 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. The FSharpValue.GetUnionFields function returns the name of the case of an object, together with obj[] of all the arguments (which you can ignore):
open Microsoft.FSharp.Reflection
let expect (someShape:'T) (someUnionCase:'T) =
if not (FSharpType.IsUnion(typeof<'T>)) then
failwith "Not a union!"
else
let info1, _ = FSharpValue.GetUnionFields(someShape, typeof<'T>)
let info2, _ = FSharpValue.GetUnionFields(someUnionCase, typeof<'T>)
if not (info1.Name = info2.Name) then
failwithf "Expected shape %A. Found shape %A" info1.Name info2.Name
If you now compare Square with Circle, the function throws, but if you compare two Squares, it works (even if the values are different):
let myShape = Shape.Square(10)
expect myShape (Shape.Circle(0)) // Throws
expect myShape (Shape.Square(0)) // Fine
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.
I use the same pattern to implement type checking in HLVM. For example, when indexing into an array I check that the type of the expression is an array ignoring the element type. But I don't use reflection as the other answers have suggested. I just do something like this:
let eqCase = function
| Circle _, Circle _
| Square _, Square _
| Rectangle _, Rectangle _ -> true
| _ -> false
Usually in a more specific form like this:
let isCircle = function
| Circle _ -> true
| _ -> false
You could also do:
let (|ACircle|ASquare|ARectangle|) = function
| Circle _ -> ACircle
| Square _ -> ASquare
| Rectangle _ -> ARectangle
If you do decide to go the reflection route and performance is an issue (reflection is unbelievably slow) then use the precomputed forms:
let tagOfShape =
Reflection.FSharpValue.PreComputeUnionTagReader typeof<Shape>
This is over 60× faster than direct reflection.
NOTE this has a caveat. See UPDATE below.
It appears that union cases are implemented as nested classes of the union type (type name: FSI_0006+Shape+Square). So given a union type instance, checking the type of the instance by obj.GetType() is sufficient.
let expect (someShape:'T) (someUnionCase:'T) =
if (someShape.GetType() <> someUnionCase.GetType()) then failwith "type not compatible"
type Shape =
| Circle of int
| Square of int
| Rectangle of ( int * int )
let myShape = Shape.Square 12
printfn "myShape.GetType(): %A" (myShape.GetType())
expect myShape (Shape.Circle 5)
This outputs:
myShape.GetType(): FSI_0006+Shape+Square
System.Exception: type not compatible
at Microsoft.FSharp.Core.Operators.FailWith[T](String message)
> at FSI_0006.expect[T](T someShape, T someUnionCase)
at <StartupCode$FSI_0006>.$FSI_0006.main#()
Stopped due to error
I just don't know if this approach is considered implementation dependent, i.e., some platform/runtime implements this differently such that the types of two different union case objects are the same.
UPDATE
OK I found the above doesn't work for union type with cases that don't take parameters. In that case, the implementation of the cases are different and .GetType() always gives the union type's declaring type. The below code demonstrates this:
type Foo = A|B|C
type Bar = X|Y|Z of int
let getType (x:obj) = x.GetType()
let p (x:obj) = printfn "%A" x
A |> getType |> p
B |> getType |> p
C |> getType |> p
X |> getType |> p
Y |> getType |> p
Z 7 |> getType |> p
This gives:
FSI_0004+Foo
FSI_0004+Foo
FSI_0004+Foo
FSI_0004+Bar+_X
FSI_0004+Bar+_Y
FSI_0004+Bar+Z
The more general alternative, as mentioned in another answer, would be to convert the case instances into tags:
open Microsoft.FSharp.Reflection
// more general solution but slower due to reflection
let obj2Tag<'t> (x:obj) =
FSharpValue.GetUnionFields(x, typeof<'t>) |> fst |> (fun (i: UnionCaseInfo) -> i.Tag)
[A;B;C;A] |> List.map obj2Tag<Foo> |> p
[X;Y;Z 2; Z 3; X] |> List.map obj2Tag<Bar> |> p
This gives:
[0; 1; 2; 0]
[0; 1; 2; 2; 0]
This should be considerably slower if operated on large amount of objects, as it's heavily depend on reflection.
Related
I want to check if there is an element of a specific discriminated union in a sequence, and if it exists, I want to return that specific element.
type Shape =
| Rectangle of float * float
| Circle
| Prism
let rectangle1 = Rectangle(5.0,1.2)
let rectangle2 = Rectangle(2.0,1.4)
let test = [Circle;Prism;rectangle2;rectangle1]
let getShape shape =
match shape with
| Rectangle(a,b) -> Some(a)
| _ -> None
let x =
if test |> List.exists (fun shape -> (getShape shape) = Some(1.0)) then
???
elif test|> List.exists (fun shapre -> shape = Circle) then
Circle
else
Prism
For the code above, I want to be able to check if there is a Shape of Rectangle with first element of tuple is 1.0
This checking works : test |> List.exists (fun shape -> (getShape shape) = Some(1.0))
However, I don't know how to return the "shape" found from that expression.
I want it for sequence, but the example above uses List. I guess it will also apply for sequence.
List.tryFind sounds like what you need. Also for Seq.
In your case I believe it would look something like this:
let x = test |> List.tryFind (fun shape -> (getShape shape) = Some(1.0))
After that x should be of type Shape, you'll still need to pattern match to operate on it as a Rectangle.
Rectangle here is not a type (though it might be represented as one once compiled), but rather a value.
I m trying to filter a mixed data type for a specific type, say float (ideally this would be dynamic though)
here my example:
let testobj = [8.0 , 1.0, "bla" ; 8.0 , 1.0, "bla"]
let testfun data = data |> List.filter (fun a ->
match a.GetType() with
| float -> a
| _ -> 0.0)
now this should return [8.0 , 1.0, 0.0 ; 8.0 , 1.0, 0.0] for testobj but I m gettting an error that the function is of type bool
This isn't what you want to do.
Seriously.
F# wants lists to be homogeneous and your list is not homogeneous. float and string don't share a common base class so you're not going to get a list from it.
What F# wants you to do is to use a discriminated union for this. So if you have this type:
type Composite =
| Num of float
| Str of string
you can define your list like this:
let data = [ Num(8.0); Num(1.0); Str("bla"); Num(8.0); Num(1.0); Str("bla") ]
and from there you can pattern match on the types and your function looks like this:
let testfun d = d |> List.map (fun a ->
match a with
| Num x -> a
| _ -> Num(0.0) )
data|> testfun |> printfn "%A"
And the output will be:
[Num 8.0; Num 1.0; Num 0.0; Num 8.0 ; Num 1.0 ; Num 0.0;]
If you want floats in the end and not Composites, do this:
let testfun1 d = d |> List.map (fun a ->
match a with
| Num x -> x
| _ -> 0.0 )
which sheds the composite type. And everything (and I mean everything) in that code is type strong and type-safe.
From a real-world maintenance point of view, I would eschew the _ case in the matches and instead use all my types, reasoning that if I extend Composite to include another type I would want the compiler to scream at me and look at each function that uses it rather than silently assuming that 0.0 or Num(0.0) is really what I wanted.
For example, if I added integers to that type, this would do exactly the wrong thing if I wanted to sum the contents of a list of composites.
Given that you're stuck/hell-bent on a weakly-typed data set, then you want something like this:
let testfun2 d = d |> Array.map (fun (a:Object) ->
match a with
| :? float as x -> x
| _ -> 0.0
)
let data:Object[] = [|8.0; 1.0; "bla"; 8.0; 1.0; "bla"|]
data |> testfun2 |> printfn "%A"
which will print what you expect. Note that I'm using proper Array syntax and not list syntax.
However this is feeling really wonky for F#. See how I have to adorn a and d with types? In my previous code, the language can figure it all out. If I don't adorn either, I get compiler errors because we're really going against the grain of the type system.
If I were you, I would be inclined to do something like this first:
let recast d = d |> Array.map (fun (a:Object) ->
match a with
| :? float as x -> Num x
| :? string as x -> Str x
| _ -> raise (ArgumentException("that was unexpected: " + a.GetType().Name))
)
which turns this into an Array of Composite which is now type strong. If you tack on |> Array.toList after the Array.map, you get a list (if you want that).
I keep learning F# pattern matching with my simple function which should return square root if argument is number, argument otherwise. I've modified it a bit and it looks like as follows.
let my_sqrt (o: obj) =
match o with
| :? float as d -> (sqrt d).ToString()
| _ as x -> x.ToString()
It is working fine for my purpose, but what if I don't want to cast return value to string? How can I return "some object" and then use it in printfn "%A" (my_sqrt [| 1; 2; 3 |]) construction?
Even though your example is just a demonstration of what you're trying to do, it is worth pointing out that this is probably not a good design. In F#, you would not normally use functions that operate on objects using casts - a better way to represent this would be a discriminated union:
type MyInput =
| Numeric of float
| Other of obj
let my_sqrt = function
| Numeric d -> Numeric (sqrt d)
| Other o -> Other o
This function works on a type that is either Numeric or Other, but thanks to the DU, you do not need any casting. I think something along these lines would be a better approach to your actual problem too.
I think you want
let my_sqrt (o: obj) =
match o with
| :? float as d -> (sqrt d) :> obj
| _ as x -> x
just upcast to object
I think your function is ok. When you want to compute each square root, you have to map your function over array like this:
Array.map my_sqrt [| 1.0; 2.0; 3.0 |] |> printfn "%A"
As mentioned in TP answer, the general idea should be, wherever possible, to surface information to your type system.
It is then easier for you to read and reason your program, as you have named things.
That means F# can actually work for you and tell you when you made something wrong
That makes it always worth the investment.
I agree with Tomas that using a Discriminated Union would be better. There is no Either monad built into F# but you could use the Choice union to standardize the interface:
let my_sqrt (o : obj) =
match o with
| :? float as d -> Choice1Of2 (sqrt d)
| o -> Choice2Of2 o;;
I suspect that I am missing something very obvious here but this doesn't work:
let t = Array2D.create 1 1 1.0
for x in t do printfn "%f" x;;
It fails with
error FS0001: The type 'obj' is not compatible with any of the types float,float32,decimal, arising from the use of a printf-style format string
Interestingly using printf "%A" or "%O" prints the expected values which suggests to me that the problem is with the type inference
The corresponding code for a 1D array works fine
let t = Array.create 1 1.0
for x in t do printfn "%f" x;;
For reference this is on version 2.0 (both interactive and compiler) running on the latest mono
In .NET, a 1D array implicitly implements IList, which means it also implements (by inheritance) IEnumerable<T>. So, when you run:
let t = Array.create 1 1.0
for x in t do printfn "%f" x;;
the F# compiler emits code which gets an implementation of IEnumerable<T> (seq<T> in F#) from t, then iterates over it. Since it's able to get an IEnumerable<T> from the array, x will have type T.
On the other hand, multi-dimensional arrays (2d, 3d, etc.) only implement IEnumerable (not IEnumerable<T>) so the F# compiler infers the type of x as System.Object (or obj, in F#).
There are two solutions for what you want:
Cast each individual value within the loop, before printing it:
for x in t do printfn "%f" (x :?> float);;
Or, use Seq.cast to create and iterate over a strongly-typed enumerator:
for x in (Seq.cast<float> t) do printfn "%f" x;;
As Jack pointed out, this is a problem. One easy solution is:
let t = Array2D.create 2 2 1.0
t |> Array2D.iter (printfn "%f");;
And if you really like the for .. in .. do syntax:
type Array2DForLoopBuilder() =
member __.Zero() = ()
member __.For(a, f) = Array2D.iter f a
member __.Run e = e
let a2dfor = Array2DForLoopBuilder()
let t = Array2D.init 2 2 (fun a b -> float a + float b)
a2dfor { for x in t do printfn "%f" x }
Say I have a list of shapes:
type shape =
| Circle of float
| Rectangle of float * float
let a = [ Circle 5.0; Rectangle (4.0, 6.0)]
How can I then test e.g. a Circle exists in a? I could create a function for each shape
let isCircle s =
match s with
| Circle -> true
| _ -> false
List.exists isCircle a
but I feel there must be a more elegant way in F#, other than having to define such a function for each shape type. Is there?
Related question is how to group a list of shapes, based on shape types:
a |> seq.groupBy( <shapetype? >)
If you're interested in the different categories of shapes, then it makes sense to define another type that exactly captures them:
type shapeCategory = Circular | Rectangular
let categorize = function
| Circle _ -> Circular
| Rectangle _ -> Rectangular
List.exists ((=) Circular) (List.map categorize a)
a |> Seq.groupBy(categorize)
Edit - as suggested by Brian, you can alternatively use active patterns instead of a new type. It works out pretty similarly for your examples, but would extend better to more complicated patterns, while the approach above may be better if you're code often works with the categories, and you want a nice union type for them instead of a Choice type.
let (|Circular|Rectangular|) = function
| Circle _ -> Circular
| Rectangle _ -> Rectangular
List.exists (function Circular -> true | _ -> false) a
let categorize : shape -> Choice<unit, unit> = (|Circular|Rectangular|)
a |> Seq.groupBy(categorize)
you can combine F# reflection with quotations to get generic solution
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
type Shape =
| Circle of float
| Rectangle of float * float
let isUnionCase (c : Expr<_ -> 'T>) =
match c with
| Lambda (_, NewUnionCase(uci, _)) ->
let tagReader = Microsoft.FSharp.Reflection.FSharpValue.PreComputeUnionTagReader(uci.DeclaringType)
fun (v : 'T) -> (tagReader v) = uci.Tag
| _ -> failwith "Invalid expression"
let a =
[ Circle 5.0; Rectangle (4.0, 6.0)]
|> List.filter (isUnionCase <# Rectangle #>)
printf "%A" a
You can use the F# reflection library to get a value's tag:
let getTag (a:'a) =
let (uc,_) = Microsoft.FSharp.Reflection.FSharpValue.GetUnionFields(a, typeof<'a>)
uc.Name
a |> Seq.groupBy getTag
I want to add another solution that works with quotations for every union case, based on the one desco provided. Here it goes:
open Microsoft.FSharp.Quotations.Patterns
open Microsoft.FSharp.Reflection
let rec isUnionCase = function
| Lambda (_, expr) | Let (_, _, expr) -> isUnionCase expr
| NewTuple exprs ->
let iucs = List.map isUnionCase exprs
fun value -> List.exists ((|>) value) iucs
| NewUnionCase (uci, _) ->
let utr = FSharpValue.PreComputeUnionTagReader uci.DeclaringType
box >> utr >> (=) uci.Tag
| _ -> failwith "Expression is no union case."
Defined this way, isUnionCase works like desco has shown, but even on union cases that are empty or have more than one value. You can also enter a tuple of comma-separated union cases. Consider this:
type SomeType =
| SomeCase1
| SomeCase2 of int
| SomeCase3 of int * int
| SomeCase4 of int * int * int
| SomeCase5 of int * int * int * int
let list =
[
SomeCase1
SomeCase2 1
SomeCase3 (2, 3)
SomeCase4 (4, 5, 6)
SomeCase5 (7, 8, 9, 10)
]
list
|> List.filter (isUnionCase <# SomeCase4 #>)
|> printfn "Matching SomeCase4: %A"
list
|> List.filter (isUnionCase <# SomeCase3, SomeCase4 #>)
|> printfn "Matching SomeCase3 & SomeCase4: %A"
The first isUnionCase I provided only worked for single case checks. I later added the expression check for NewTuple and thought you might like it. Just make sure that if you alter the code the precomputations still work, this is why iucs is defined outside of the returned anonymous function.
A more elegant solution could be the following:
let shapeExistsInList shapeType list =
List.exists (fun e -> e.GetType() = shapeType) list
let circleExists = shapeExistsInList ((Circle 2.0).GetType()) a
However, I'm not very satisfied with this myself since you have to create an instance of the discriminated union for it to work.
Grouping by shape type could work in a similar fashion.