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".
Related
For scalar (i.e. non array-like) optional arguments, I would use this pattern :
[<ExcelFunction(Category= "Test", Description= "Test optional arguments.")>]
let test_test1 ([<ExcelArgument(Description= "Optional. This is a double. Default is 42.0.")>] arg1 : obj) : double =
match arg1 with
| :? ExcelMissing -> 42.0 // the argument was missing
| :? double as d -> d // the argument was a double
| _ -> -1.0 // otherwise
I am not sure if this code is "idiomatic" within Excel-Dna / F# but it seems to "work".
However I am not sure how to proceed for optional array-like arguments. Eg :
[<ExcelFunction(Category= "Test", Description= "Test optional arguments.")>]
let test_test2 ([<ExcelArgument(Description= "Optional. This is a double. Default is [42, 42].")>] arg1 : obj[]) : double[] =
match arg1.[0] with
| :? ExcelMissing -> [42.0; 42.0] |> List.toArray // the argument was missing OR it was an empty array
| :? double as d -> arg1 |> castToDouble // the argument was an array and its first argument was a double
| _ -> Array.empty // otherwise
The above seems to work for most cases but does not allow to handle the edge-cases properly : eg if arg1 is an empty array. (castToDouble being a custom obj[] -> double[] conversion function)
What would be the right / idiomatic way to handle optional double arrays in F# / Excel-Dna and how could I then rewrite test_test2?
=========== EDIT ===
Following Govert's advice, I tried the following :
[<ExcelFunction(Category= "Test", Description= "Test optional arguments.")>]
let test_test3 ([<ExcelArgument(Description= "Optional. This is a double. Default is [42, 42].")>] arg1 : obj) : double[] =
match arg1 with
| :? (double[]) as ds -> [1.0; 2.0] |> List.toArray // the argument was a array of double elements
| :? ExcelMissing -> [42.0; 42.0] |> List.toArray // the argument was missing OR it was an empty array
| _ -> Array.empty // otherwise
... but unfortunately I get a #NUM! output when I pass an array of doubles (or of anything else). It's only when I pass nothing that I correctly get the [42.0, 42.0] array.
This covers all the possibilities:
[<ExcelFunction("describes the input argument")>]
let describe(arg1 : obj) : string =
match arg1 with
| :? double as d -> sprintf "Double: %f" d
| :? string as s -> "String: " + s
| :? bool as b -> sprintf "Boolean: %b" b
| :? ExcelError as err -> sprintf "ExcelError: %A" err
| :? (obj[,]) as arr -> sprintf "Array[%i, %i]" (Array2D.length1 arr) (Array2D.length2 arr)
| :? ExcelEmpty -> "<<Empty>>"
| :? ExcelMissing -> "<<Missing>>"
| _ -> "!? Unheard of ?!"
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
I need a function that could take an arbitrary number of arguments, each could be either of type 'T or seq<'T>. Inside the function I need to process it as a single seq<'T> with all inputs combined in the same order as they sere supplied.
The obvious way was to have something like:
module Test =
let flatten ([<ParamArray>] args) =
let flat = seq {
for a in args do
match box a with
| :? int as x -> yield x
| :? seq<int> as sq ->
for s in sq do
yield s
| _ -> failwith "wrong input type"
}
flat // this should be seq<int>
but I cannot make it work in FSI even with the simplest case
let fl = Test.flatten 1;;
----------------------^
...: error FS0001: The type 'int' is not compatible with the type 'seq<'a>'
What is wrong here and how to get it work as needed? Probably this could be done in some completely different way?
From msdn :
In F#, parameter arrays can only be defined in methods. They cannot be
used in standalone functions or functions that are defined in
modules.
So instead of a module, declare a type with a static method.
open System
type Test() =
static member flatten ([<ParamArray>] args: obj[]) =
let flat = seq {
for a in args do
match box a with
| :? int as x -> yield x
| :? seq<int> as sq ->
for s in sq do
yield s
| _ -> failwith "wrong input type"
}
flat
If you have other let bindings you can still declare a module with the same name.
Also note that in the second guard of the match you can avoid the for loop by doing:
| :? seq<int> as sq -> yield! sq
And box is not required.
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.
I've spent a few hours trying to get to grips with F# Quotations, but I've come across a bit of a road block. My requirement is to take simple functions (just integers,+,-,/,*) out of a discriminated union type and generate an expression tree that will eventually be used to generate C code. I know this is possible using Quotations with 'direct' functions.
My problem is that the expression tree seems to terminate with a "Value", and I can't figure out how to traverse into that value.
My questions is
whether this is actually possible in this situation? or are there any other approaches that are worth considering.
type FuncType =
| A of (int -> int -> int)
| B
| C
[<ReflectedDefinition>]
let add x y = x + y
let myFunc1 = A (fun x y -> x + y )
let myFunc2 = A add
let thefunc expr =
match expr with
| A(x) ->
<# x #>
| _ ->
failwith "fail"
printfn "%A" (thefunc myFunc1) // prints "Value (<fun:myFunc1#14>)"
printfn "%A" (thefunc myFunc2) // prints "Value (<fun:myFunc2#15>)"
printfn "%A" <# fun x y -> x + y #> // generates usable expression tree
Quotations represent the F# code that was quoted syntactically. This means that if you write something like <# x #>, the quotation will contain just Value case specifying that you quoted something which has the specified value. (Variables are automatically replaced with values if the variable is defined outside of the quotation).
You can only get quotation of code that was explicitly quoted using <# .. #> or of a function that was marked as ReflectedDefinition and is referred to by name in a quotation (e.g. <# add #> but not for example let f = add in <# f #>).
To be able to do what your snippet suggests, you'll need to store quotations in your FuncType too (so that the lambda function that you write is also quoted and you can get its body). Something like:
type FuncType =
| A of Expr<int -> int -> int>
| B | C
[<ReflectedDefinition>]
let add x y = x + y
let myFunc1 = A <# fun x y -> x + y #>
let myFunc2 = A <# add #>
let thefunc expr =
match expr with
| A(x) -> x
| _ -> failwith "fail"
This should work for functions marked as ReflectedDefinition too. To extract the body of the function you need to add something like (you'll need to substitute arguments of the function for parameters, but this should give you some idea):
match expr with
| Lambdas(_, body) ->
match body with
| Call(_, mi, _) when Expr.TryGetReflectedDefinition(mi) <> None ->
let func = Expr.TryGetReflectedDefinition(mi)
match func with
| Some(Lambdas(_, body)) ->
// 'body' is the quotation of the body
| _ -> failwith "Not supported function"
| _ -> failwith "Not supported function"
| _ -> failwith "Not supported expression"