I was wondering if there is a simple way to add pretty printing to a custom type used in a Deedle data frame.
In the following example:
open Deedle
type PrimaryContactInfo =
| Default of int
| NonDefault of int
| Missing
type Account = { PrimaryContact : PrimaryContactInfo }
[ { PrimaryContact = Default(1) }; { PrimaryContact = Default(2) }; { PrimaryContact = NonDefault(5) } ]
|> Frame.ofRecords
I get the following output in fsi:
PrimaryContact
0 -> FSI_0011+PrimaryContactInfo+Default
1 -> FSI_0011+PrimaryContactInfo+Default
2 -> FSI_0011+PrimaryContactInfo+NonDefault
but I would rather have output like this:
PrimaryContact
0 -> Default(1)
1 -> Default(2)
2 -> NonDefault(5)
Is this possible?
As implied by this language feature suggestion, F# discriminated unions don't automatically convert nicely to strings.
You can make it convert nicely by overriding ToString:
type PrimaryContactInfo =
| Default of int
| NonDefault of int
| Missing
override this.ToString () = sprintf "%A" this
Notice, however, that the use of "%A" may be slow, so measure, and write a faster, more explicit implementation if necessary; for example:
type PrimaryContactInfo =
| Default of int
| NonDefault of int
| Missing
override this.ToString () =
match this with
| Default i -> sprintf "Default %i" i
| NonDefault i -> sprintf "NonDefault %i" i
| Missing -> "Missing"
This produces exactly the same output as sprintf "%A, but doesn't use Reflection, so should, in theory, be faster (but don't take my word on it: measure).
Related
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))
I implemented a Discriminated Union type that would be used to select a function:
type BooleanCombinator =
| All
| Some
| None
| AtLeast of int
| MoreThan of int
| NotMoreThan of int
| LessThan of int
| ExactlyOne
| ExactlyTwo
| AllButOne
| AllButTwo
let boolToInt (b: bool) : int = if b then 1 else 0
let combineBooleans (combinator : BooleanCombinator)
(bools : bool list)
: bool =
let n = List.sumBy boolToInt bools
match combinator with
| BooleanCombinator.All -> List.forall id bools
| BooleanCombinator.Some -> bools |> List.exists id
| BooleanCombinator.None -> bools |> List.exists id |> not
| BooleanCombinator.AtLeast i -> n >= i
| BooleanCombinator.MoreThan i -> n > i
| BooleanCombinator.NotMoreThan i -> n <= i
| BooleanCombinator.LessThan i -> n < i
| BooleanCombinator.ExactlyOne -> n = 1
| BooleanCombinator.ExactlyTwo -> n = 2
| BooleanCombinator.AllButOne -> n = bools.Length - 1
| BooleanCombinator.AllButTwo -> n = bools.Length - 2
This looked Ok to me but the compiler started to look at all instances of Some and None as belonging to this DU, instead of the Option DU.
I do not want to go through all of my code replacing Some with Option.Some and None with Option.None.
Is there a way to tell the compiler that unqualified Some and None are actually Option.Some and Option.None?
Or should I just give different names to these DU cases, like AtLeastOne and ExactlyZero
The general rule for resolving name collisions in F# is "last declaration wins". Because your custom DU is declared after Option, its constructors Some and None win over those of Option.
But this rule offers a way to fix the problem: you just need to "reassert" the declarations after your custom DU:
type Bogus = Some of int | None
let g = function Some _ -> 42 | None -> 5
let x = Some 42
let inline Some a = Option.Some a
let inline None<'a> = Option.None : 'a option
let (|Some|None|) = function | Option.Some a -> Some a | Option.None -> None
let f = function Some _ -> 42 | None -> 5
let y = Some 42
If you inspect the types of g, x, f, and y in the above code:
> g
g : Bogus -> int
> f
f : 'a option -> int
> x
Bogus
> y
int option
The function g and value x were inferred to have type Bogus -> int and Bogus respectively, because Some and None in their bodies refer to Bogus.Some and Bogus.None.
The function f and value y were inferred to have Option-related types, because Some and None in their bodies refer to the Some function and the (|Some|None|) active pattern that I defined just above.
Of course, this is a rather hacky way to restore status quo. This will convince the compiler, but humans will still have a hard time reading your code. I suggest you rename the cases of your DU instead.
You can mark your DU with [<RequireQualifiedAccess>] attribute.
This means that you will be required to qualify the case name with the type whenever you use it in the code - which is something you do now anyway in your match expression.
That way an unqualified Some would still be resolved to mean Option.Some, despite the fact that you reuse the name.
It's a useful technique to know when you want to use a snappy name for a DU case - like None, Yes, Failure etc. - that by itself would be ambiguous or confusing to the reader (or the compiler, for that matter).
I've been hit by these errors before in a much more complex scenario. Since then I have simplified many things and had working code for a long time, until I needed to override Equals. It uses an inlined member which F# doesn't seem to be happy about.
Basically, the scenario can be summarized with the following code:
[<Flags>]
type MyType =
| Integer = 0b0001
| Float = 0b0010
module Test =
[<CustomEquality;NoComparison>]
type SomeType =
| Int of int64
| Float of float
override x.Equals other =
match other with
| :? SomeType as y ->
// following line throws on compiling this
match SomeType.getType x &&& SomeType.getType y with
| MyType.Integer -> int64 x = int64 y // highest type is integer (both are int)
| MyType.Float -> float x = float y // highest type is float (either is or both are float)
| _ -> false // impossible
| _ -> false
override x.GetHashCode() =
match x with Int i -> hash i | Float f -> hash f
static member inline op_Explicit(n: SomeType): float =
match n with
| Int i -> float i
| Float f -> f
static member inline op_Explicit(n: SomeType): int64 =
match n with
| Int i -> i
| Float f -> int64 f
static member inline getType x =
match x with
| Int _ -> MyType.Integer
| Float _ -> MyType.Float
The following errors are raised (this is similar to my previous question but that involved complex ducktyping).
error FS1114: The value 'Test.SomeType.getType' was marked inline but was not bound in the optimization environment
error FS1113: The value 'getType' was marked inline but its implementation makes use of an internal or private function which is not sufficiently accessible
warning FS1116: A value marked as 'inline' has an unexpected value
error FS1118: Failed to inline the value 'getType' marked 'inline', perhaps because a recursive value was marked 'inline'
Now, there is no recursive value, and the target type is known (as a result of the implicit cast to SomeType in the match pattern), so I don't think there's a reason for this inlining to be impossible.
Does anyone have some thoughts on this? Or a pattern, including the inlined op_Explicit (if you remove getType, you will get the error on those) and preferably also the getType as inlined?
I know I can resolve this with an OO hierarchy, interfaces and what not, but I'd rather use this approach, both for clarity (the type system is a lattice, not a hierarchy) and for performance (an earlier version with the inlining showed more than 4x speed increase in test scenarios, and speed is important).
As an afterthought, the following, simpler scenario also throws these errors:
module Test =
type SomeType =
| Int of int64
| Float of float
static member MyEquals (x, other: SomeType) =
// following line throws on compiling this
float x = float other
static member inline op_Explicit(n: SomeType): float =
match n with
| Int i -> float i
| Float f -> f
static member inline op_Explicit(n: SomeType): int64 =
match n with
| Int i -> i
| Float f -> int64 f
When I remove the type decoration other: SomeType, the error disappears. I don't know why that should matter, as a narrower type with the same statically inferred methods should not raise this error, I'd think.
And since the override x.Equals has a type annotation of obj, I don't see how I could use that knowledge (remove the type decoration) to help me here.
It seems to be that the F# compiler can't do inlining with unordered code. As you rightly point out in the comment below this appears to be a bug.
open System
[<Flags>]
type MyType =
| Integer = 0b0001
| Float = 0b0010
module Test =
[<CustomEquality;NoComparison>]
type SomeType =
| Int of int64
| Float of float
static member inline op_Explicit(n: SomeType): float =
match n with
| Int i -> float i
| Float f -> f
static member inline op_Explicit(n: SomeType): int64 =
match n with
| Int i -> i
| Float f -> int64 f
static member inline getType x =
match x with
| Int _ -> MyType.Integer
| Float _ -> MyType.Float
override x.Equals other =
match other with
| :? SomeType as y ->
// following line throws on compiling this
match SomeType.getType x &&& SomeType.getType y with
| MyType.Integer -> int64 x = int64 y // highest type is integer (both are int)
| MyType.Float -> float x = float y // highest type is float (either is or both are float)
| _ -> false // impossible
| _ -> false
override x.GetHashCode() =
match x with Int i -> hash i | Float f -> hash f
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".
I have a function, which can returns different types, and I use discriminated union for this. What I need, is to have conversion from one type in discriminated union to another type.
Also some of the types can be convertable to all other types (String), but some of the types can be converted only to String (MyCustomType)
For this I've added member method ConvertTo to the ResultType:
type MyTypes =
| Boolean = 1
| Integer = 2
| Decimal = 3
| Double = 4
| String = 5
| MyCustomType = 6
type ResultType =
| Boolean of bool
| Integer of int
| Decimal of decimal
| Double of double
| String of string
| MyCustomType of MyCustomType
with
member this.ConvertTo(newType: MyTypes) =
match this with
| ResultType.Boolean(value) ->
match newType with
| MyTypes.Boolean ->
this
| MyTypes.Integer ->
ResultType.Integer(if value then 1 else 0)
...
| ResultType.MyCustomType(value) ->
match newType with
| MyTypes.MyCustomType ->
this
| MyTypes.String ->
ResultType.String(value.ToString())
| _ ->
failwithf "Conversion from MyCustomType to %s is not supported" (newType.ToString())
I don't like such construction, because if I add more types, this requires me to do many changes: MyTypes, ResultType and also in several places in the ConvertTo member function.
Can anybody suggest better solution for such types conversion?
Thanks in advance
With a slightly different design, it is possible to exploit System.Convert.ChangeType and the fact that the constructors of discriminated unions are actually functions:
// statically typed wrapper for System.Convert.ChangeType
let conv a : 'T = System.Convert.ChangeType(a, typeof<'T>) :?> 'T
type MyCustomType() = class end
type ResultType =
| Boolean of bool
| Integer of int
| Decimal of decimal
| Double of double
| String of string
| MyCustomType of MyCustomType
with
member this.ConvertTo (newType:'T->ResultType) =
match this with
| Boolean b -> newType( conv b )
| Integer i -> newType( conv i )
| Decimal d -> newType( conv d )
| Double d -> newType( conv d )
| String s -> newType( conv s )
| MyCustomType m ->
if typeof<'T> <> typeof<string> then
raise (new System.InvalidCastException("MyCustomType can only be converted to String"))
else
String (m.ToString())
let i = Integer 42
let b = i.ConvertTo Boolean
printfn "%A" b
let d = i.ConvertTo Decimal
printfn "%A" d
let d2 = i.ConvertTo Double
printfn "%A" d2
let s = i.ConvertTo String
printfn "%A" s
//let mi = i.ConvertTo MyCustomType // throws InvalidCastException
let m = MyCustomType (new MyCustomType())
let sm = m.ConvertTo String
printfn "%A" sm
//let im = m.ConvertTo Integer // throws InvalidCastException
EDIT: Once you add more custom types, this will not help much.
Maybe you should make your custom types implement IConvertible. Then you can remove the special case code from ConvertTo and completely rely on System.Convert.ChangeType.
You would still have to extend every custom type's ToObject implementation whenever you add a new custom type. Whether that really is better than a central ConvertTofunction is debatable.
Why are you wanting to do type conversion to begin with? Discriminated Unions are a good way of hiding type information until you need it and abstract complexity away. Generally you have a match statement in a function that consumes this type and then you only cast if you need to.
If you're trying to make some type of parser or language engine then you have no choice but to define all the cast or at least their error states. If you wouldn't mind elaborating on why / what you would use this for, maybe I could suggest another approach.
An aside: F# and .NET in general doesn't support overloading of return types.