F# - Pattern Matching on boxed tuples - f#

How can I pattern match on a boxed tuple? Or is there a better way to do something like this (simplified example):
open System.Drawing
let coerceColor a =
match box a with
| :? Color as c -> c
| (:? int as r),(:? int as g),(:? int as b) -> Color.FromArgb(r,g,b)
| _ -> failwith "Cannot coerce color"

let coerceColor a =
match box a with
| :? Color as c -> c
| :? (int*int*int) as t -> t |> Color.FromArgb
| _ -> failwith "Cannot coerce color"
But if I could change the design, I would rather use a DU or alternatively a static member with overloads.

Related

How can I use TryParse in a guard expression in match?

I have built a toy spreadsheet to help learn F#. When I process the text for a new cell I store it as a discriminated type. To parse it I feel I should be able to do something like:
let cv =
match t with
| _ when t.Length=0 -> Empty
| x when t.[0]='=' -> Expr(x)
| x when t.[0]='\"' -> Str(x)
| (true,i) when Int32.TryParse t -> IntValue(i) // nope!
| _ -> Str(t)
I have tried quite a few combinations but I cannot get TryParse in the guard. I have written a helper:
let isInt (s:string) =
let mutable m:Int64 = 0L
let (b,m) = Int64.TryParse s
b
I can now write:
| _ when Utils.isInt t -> IntValue((int)t)
This seems like a poor solution as it discards the converted result. What the correct syntax to get TryParse into the guard?
I think an active pattern will do what you want:
let (|Integer|_|) (str: string) =
let flag, i = Int32.TryParse(str)
if flag then Some i
else None
let cv =
match t with
| _ when t.Length=0 -> Empty
| x when t.[0]='=' -> Expr(x)
| x when t.[0]='\"' -> Str(x)
| Integer i -> IntValue(i)
| _ -> Str(t)
But if you really want TryParse in the guard condition (and you don't mind parsing twice), you could do this:
| x when fst (Int32.TryParse(t)) -> IntValue (Int32.Parse(x))

How to convert generic Discriminated Union to obj

Given a DU like
type Result<'a, 'b> = Ok of 'a | Error of 'b
and some functions
let doA () = Ok true
let doB () = Error <| exn "Fail"
let doC = function | 1 -> Ok "one" | x -> Error x
How do you define a function to cast the value?
toObjResult : x:obj -> Result<obj, obj> //where x is guaranteed to be Result<'a,'b>
Usage
let data =
[ doA() |> box
doB() |> box
docC 1 |> box
docC 2 |> box ]
|> List.map toObjResult
All attempts so far restrict the types of 'a and 'b to be obj
let toObjResult (x:obj) =
match x with
| :? Result<'a, 'b> as r ->
match r with
| Ok a -> Ok (box a)
| Error b -> Error (box b)
| _ -> Error <| (exn "Invalid type" |> box)
resulting in errors like
System.InvalidCastException: Unable to cast object of type 'Ok[System.Boolean,System.Object]' to type 'Result`2[System.Object,System.Object]'.
There is no way to do this without using reflection, enumerating all types, or modifying the type.
Using reflection can be slow, but lets you do what you want (see [the GenericType active pattern from this answer) and the answer from #robkuz shows how you can do this by listing all the cases that you want to cover - the problem is that this does not scale well.
Finally, if you were happy to modify your Result<'a, 'b> type, you could add a non-generic interface that lets you get the value as a boxed value:
type IBoxedResult =
abstract Boxed : Result<obj, obj>
and Result<'a, 'b> =
| Ok of 'a
| Error of 'b
interface IBoxedResult with
member x.Boxed =
match x with
| Ok v -> Ok (box v)
| Error v -> Error (box v)
Now you can cast obj to IBoxedResult and use Boxed to get the value as Reslt<obj, obj>:
[ box (Ok true)
box (Ok 1) ]
|> List.map (fun o -> (o :?> IBoxedResult).Boxed)
You have to match on the exact generic type params of your Result type in your matching expression
let matchR r =
match r with
| Ok a -> Ok (box a)
| Error b -> Error (box b)
let toObjResult (x:obj) =
match x with
| :? Result<bool, _> as r -> matchR r
| :? Result<string, int> as r -> matchR r
| :? Result<_, Exception> as r -> matchR r
| _ -> Error (box "Invalid type" )
sadly you can't match on unrealised type params (which is really bad)

How do you downcast to byte[]? [duplicate]

I'm trying to lookup DbType enumeration values from .net types. I'm using a match statement. However I cannot figure out how to match on the type byte[].
let dbType x =
match x with
| :? Int64 -> DbType.Int64
| :? Byte[] -> DbType.Binary // this gives an error
| _ -> DbType.Object
If there is a better way to map these types, I would be open to suggestions.
byte[], byte array, and array<byte> are all synonymous, but in this context only the last will work without parentheses:
let dbType (x:obj) =
match x with
| :? (byte[]) -> DbType.Binary
| :? (byte array) -> DbType.Binary // equivalent to above
| :? array<byte> -> DbType.Binary // equivalent to above
| :? int64 -> DbType.Int64
| _ -> DbType.Object

Auto generation of predicates and accessors for discriminated unions in F#

Is it possible in F# to automatically generate predicates and accessors for an arbitrary algebraic data type in type-safe manner?
For example, if we have user defined type:
type A =
B of string
| C of int * sting
should be generated something like this:
type A =
B of string
| C of int * sting
with
member __.isB = match __ with B -> true | _ -> false
member __.isC = match __ with C -> true | _ -> false
member __._1 = match __ with B(x) -> Some(x) | _ -> None
member __._2 = match __ with C(x,_) -> Some(x) | _ -> None
member __._3 = match __ with C(_,x) -> Some(x) | _ -> None
It will be better if can specify names for accessors may be with annotation like this:
[<GenerateAccessors(["BName", "CName", "Value"])>]
May be it can not be done or I should use records instead discriminated unions (DU) if I want to siplify access to inner data. But it's more simply to use patten matching with DUs and I want both of this profits - simply pattern matching and simply "direct data access" - in the same time.
You can reflect over a discriminated union using FSharpType.GetUnionCases and generate code using the F# CodeDOM available in the F# PowerPack or simply by writing text.
open Microsoft.FSharp.Reflection
type A = B of string | C of int * string
let generate t =
let cases = FSharpType.GetUnionCases(t)
printfn "type %s with" t.Name
for case in cases do
printfn "\tmember value.is%s = " case.Name
let fields =
match [for field in case.GetFields() -> "_"] with
| [] -> ""
| fields -> " (" + (fields |> String.concat ",") + ")"
printfn "\t\tmatch value with %s%s -> true | _ -> false" case.Name fields
generate typeof<A>
Generates an F# type extension:
type A with
member value.isB =
match value with B (_) -> true | _ -> false
member value.isC =
match value with C (_,_) -> true | _ -> false

F# trying to create a better printf

I'm trying to create a print statement that can print any type. I want to use type pattern matching to achieve this.
This doesn't work:
let print x = match x with | :? int -> printf "INT"; | _ -> None;;
I get the message:
let print x = match x with | :? int -> printf "INT"; | _ -> None;;
-----------------------------^^^^^^
stdin(47,30): error FS0008: This runtime coercion or type test from
type
'a to
int involves an indeterminate type based on information prior to this program point. Runtime type tests are not allowed on some types.
Further type annotations are needed.
So, i can't do type matching on an int? What other types can i not do type matching on? What does it mean that further type annotations are needed?
Type test is performed on reference types only. Therefore:
let print x =
match box x with
| :? int -> printf "INT"
| _ -> ()
or
let print (x: obj) =
match x with
| :? int -> printf "INT"
| _ -> ()
would work.
Notice that your function doesn't type check since None is of option type which is different from unit type of printf "INT".

Resources