How to convert generic Discriminated Union to obj - f#

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)

Related

match by value in a discriminated union, in F#

with this union:
type T =
| A
| B
| C
and a T list
I would like to implement something like this pseudo code:
let countOfType (t: Type) (l: T list) =
l
|> List.filter (fun x -> x.GetType() = t)
|> List.length
when I would pass if I want to count the 'A', 'B', etc..
but A.GetType() and B.GetType() return the T type, so this doesn't work.
Is there a way where I could check the type by passing it as a parameter?
The practical case here is that I have a Map that gets updated every few seconds and its values are part of the same DU. I need to be able to see how many of each type, without having to update the code (like a match block) each time an entry gets added.
Addendum:
I simplified the original question too much and realized it after seeing Fyodor's answer.
So I would like to add the additional part:
how could this also be done for cases like these:
type T =
| A of int
| B of string
| C of SomeOtherType
For such enum type T as you specified, you can just use regular comparison:
let countOfType t (l: T list) =
l
|> List.filter (fun x -> x = t)
|> List.length
Usage:
> countOfType A [A; A; B; C; A]
3
> countOfType B [A; A; B; C; A]
1
Try List.choose: ('a -> 'b option) -> 'a list -> 'b list, it filters list based on 'a -> 'b option selector. If selectors evaluates to Some, then value will be included, if selector evaluates to None, then value will be skipped. If you worry about allocations caused by instantiation of Some, then you'll have to implement version that will use ValueOption
let onlyA lis =
lis |> List.choose (function
| (A _) as a -> Some a
| _ -> None)
let onlyB lis =
lis |> List.choose (function
| (B _) as b -> Some b
| _ -> None)
let lis = [
A 1
A 22
A 333
B ""
B "123"
]
lis |> onlyA |> List.length |> printfn "%d"
You can pattern match, and throw away the data, to create a function for the filter.
type T =
| A of int
| B of string
| C of float
[A 3;A 1;B "foo";B "bar";C 3.1; C 4.6]
|> List.filter (fun x ->
match x with
| A _ -> true
| B _ -> false
| C _ -> false
)
|> List.length
But in general i would asume, that you create a predicate function in your modul.
let isA x =
match x with
| A _ -> true
| _ -> false
if you have those functions you can just write
[A 3;A 1;B "foo";B "bar";C 3.1; C 4.6]
|> List.filter isA
|> List.length

How to properly pattern match JsonConversions

Hi I have the following code which works as I expect but the compiler warns me about incomplete pattern matching when I pattern match in the Option.defaultWith function. Is there a smarter way to achieve the same effect but without warnings?
I have been thinking about throwing an exception for the rest of the cases but that's pretty ugly.
namespace JsonParser
open System
open System.Globalization
open FSharp.Data
open FSharp.Data.Runtime
type public Key = string
type public Value =
| Int of int
| Double of double
| Decimal of decimal
| String of string
| DateTime of DateTime
| Boolean of Boolean
| Array of Value []
| Guid of Guid
| Null
| Object of Record []
and public Record =
{ Key: Key
Value: Value }
module public Json =
let private culture = CultureInfo.InvariantCulture
let private emptyArray = Array.empty<String>
let rec private map (value: JsonValue) =
JsonConversions.AsInteger culture value
|> Option.map Value.Int
|> Option.orElseWith (fun () -> JsonConversions.AsDecimal culture value |> Option.map Value.Decimal)
|> Option.orElseWith (fun () -> JsonConversions.AsFloat emptyArray true culture value |> Option.map Decimal |> Option.map Value.Decimal)
|> Option.orElseWith (fun () -> JsonConversions.AsGuid value |> Option.map Value.Guid)
|> Option.orElseWith (fun () -> JsonConversions.AsDateTime culture value |> Option.map Value.DateTime)
|> Option.orElseWith (fun () -> JsonConversions.AsBoolean value |> Option.map Value.Boolean)
|> Option.defaultWith (fun () ->
match value with
| JsonValue.String x -> Value.String x
| JsonValue.Null -> Value.Null
| JsonValue.Array x ->
x
|> Array.map map
|> Value.Array
| JsonValue.Record x ->
x
|> Array.map (fun (x, y) ->
{ Key = x
Value = map y })
|> Value.Object)
The answer really depends on how you want to handle various corner cases in your JSON data.
The operations in JsonConversions are implemented in a way where they attempt to convert the value to the target type whenever this can reasonably be done. This means that using those, a value true, 1 and "yes" will all be converted to boolan true. Is this what you want? If so, then I would probably just add a case to the pattern match that throws an exception, saying that the situation should not happen:
match value with
| JsonValue.String x -> Value.String x
| JsonValue.Null -> Value.Null
| JsonValue.Array x -> (...)
| JsonValue.Record x -> (...)
| JsonValue.Float _ | JsonValue.Number _ | JsonValue.Boolean _ ->
failwith "should never happen: Numbers and booleans handled earlier!"
If you want to turn JSON value "yes" to Value.String("yes") rather than to Value.Boolean(true), then it is a lot easier if you directly pattern match on JsonValue:
let rec private map (value: JsonValue) =
match value with
| JsonValue.Float f -> Value.Double f
| JsonValue.Number n -> Value.Decimal n
| JsonValue.Boolean b -> Value.Boolean b
| JsonValue.String x -> Value.String x
| JsonValue.Null -> Value.Null
| JsonValue.Array x ->
x |> Array.map map |> Value.Array
| JsonValue.Record x ->
x |> Array.map (fun (x, y) -> { Key = x; Value = map y }) |> Value.Object
You can find the details about how JsonConversions work by looking at the relevant file in the source code: JsonConversions and TextConversions.

Railway Oriented Programming and partial application

I like using ROP when I have to deal with IO/Parsing strings/...
However let's say that I have a function taking 2 parameters. How can you do clean/readable partial application when your 2 parameters are already a Result<'a,'b> (not necessary same 'a, 'b)?
For now, what I do is that I use tuple to pass parameters and use the function below to get a Result of a tuple so I can then bind my function with this "tuple-parameter".
/// Transform a tuple of Result in a Result of tuple
let tupleAllResult x =
match (fst x, snd x) with
| Result.Ok a, Result.Ok b -> (a,b) |> Result.Ok
| Result.Ok a, Result.Error b -> b |> Result.Error
| Result.Error a, _ -> a |> Result.Error
let f (a: 'T, b: 'U) = // something
(A, B) |> tupleAllResult
|> (Result.bind f)
Any good idea?
Here what I wrote, which works but might not be the most elegant
let resultFunc (f: Result<('a -> Result<'b, 'c>), 'd>) a =
match f with
| Result.Ok g -> (g a) |> Result.Ok |> Result.flatten
| Result.Error e -> e |> Result.Error |> Result.flatten
I am not seeing partial application in your example, a concept related to currying and argument passing -- that's why I am assuming that you are after the monadic apply, in that you want to transform a function wrapped as a Result value into a function that takes a Result and returns another Result.
let (.>>.) aR bR = // This is "tupleAllResult" under a different name
match aR, bR with
| Ok a, Ok b -> Ok(a, b)
| Error e, _ | _, Error e -> Error e
// val ( .>>. ) : aR:Result<'a,'b> -> bR:Result<'c,'b> -> Result<('a * 'c),'b>
let (<*>) fR xR = // This is another name for "apply"
(fR .>>. xR) |> Result.map (fun (f, x) -> f x)
// val ( <*> ) : fR:Result<('a -> 'b),'c> -> xR:Result<'a,'c> -> Result<'b,'c>
The difference to what you have in your question is map instead of bind in the last line.
Now you can start to lift functions into the Result world:
let lift2 f xR yR =
Ok f <*> xR <*> yR
// val lift2 :
// f:('a -> 'b -> 'c) -> xR:Result<'a,'d> -> yR:Result<'b,'d> -> Result<'c,'d>
let res : Result<_,unit> = lift2 (+) (Ok 1) (Ok 2)
// val res : Result<int,unit> = Ok 3

How do sequence expressions and polymorphic recursion play together?

This project really is a source of questions for me.
I already learned about polymorphic recursion and I understand why it is a special case and therefore F# needs full type annotations.
For regular functions I might need some fiddeling but usually get it right. Now I'm trying to adapt a (working) basic toSeq to a more specialized finger tree, but can't.
My feeling is that the use of the computation expression has something to do with it. This is the condensed working version:
module ThisWorks =
module Node =
type Node<'a> =
| Node2 of 'a * 'a
| Node3 of 'a * 'a * 'a
let toList = function
| Node2(a, b) -> [a; b]
| Node3(a, b, c) -> [a; b; c]
module Digit =
type Digit<'a> =
| One of 'a
| Two of 'a * 'a
| Three of 'a * 'a * 'a
| Four of 'a * 'a * 'a * 'a
let toList = function
| One a -> [a]
| Two(a, b) -> [a; b]
| Three(a, b, c) -> [a; b; c]
| Four(a, b, c, d) -> [a; b; c; d]
module FingerTree =
open Node
open Digit
type FingerTree<'a> =
| Empty
| Single of 'a
| Deep of Digit<'a> * Lazy<FingerTree<Node<'a>>> * Digit<'a>
let rec toSeq<'a> (tree:FingerTree<'a>) : seq<'a> = seq {
match tree with
| Single single ->
yield single
| Deep(prefix, Lazy deeper, suffix) ->
yield! prefix |> Digit.toList
yield! deeper |> toSeq |> Seq.collect Node.toList
yield! suffix |> Digit.toList
| Empty -> ()
}
The one I don't manage to get to compile is this:
module ThisDoesnt =
module Monoids =
type IMonoid<'m> =
abstract Zero:'m
abstract Plus:'m -> 'm
type IMeasured<'m when 'm :> IMonoid<'m>> =
abstract Measure:'m
type Size(value) =
new() = Size 0
member __.Value = value
interface IMonoid<Size> with
member __.Zero = Size()
member __.Plus rhs = Size(value + rhs.Value)
type Value<'a> =
| Value of 'a
interface IMeasured<Size> with
member __.Measure = Size 1
open Monoids
module Node =
type Node<'m, 'a when 'm :> IMonoid<'m>> =
| Node2 of 'm * 'a * 'a
| Node3 of 'm * 'a * 'a * 'a
let toList = function
| Node2(_, a, b) -> [a; b]
| Node3(_, a, b, c) -> [a; b; c]
module Digit =
type Digit<'m, 'a when 'm :> IMonoid<'m>> =
| One of 'a
| Two of 'a * 'a
| Three of 'a * 'a * 'a
| Four of 'a * 'a * 'a * 'a
let toList = function
| One a -> [a]
| Two(a, b) -> [a; b]
| Three(a, b, c) -> [a; b; c]
| Four(a, b, c, d) -> [a; b; c; d]
module FingerTree =
open Node
open Digit
type FingerTree<'m, 'a when 'm :> IMonoid<'m>> =
| Empty
| Single of 'a
| Deep of 'm * Digit<'m, 'a> * Lazy<FingerTree<'m, Node<'m, 'a>>> * Digit<'m, 'a>
let unpack (Value v) = v
let rec toSeq<'a> (tree:FingerTree<Size, Value<'a>>) : seq<'a> = seq {
match tree with
| Single(Value single) ->
yield single
| Deep(_, prefix, Lazy deeper, suffix) ->
yield! prefix |> Digit.toList |> List.map unpack
#if ITERATE
for (Value deep) in toSeq deeper do
^^^^^
yield deep
#else
yield! deeper |> toSeq |> Seq.collect (Node.toList >> List.map unpack)
^^^^^
#endif
yield! suffix |> Digit.toList |> List.map unpack
| Empty -> ()
}
The error message I get says
Error Type mismatch. Expecting a
FingerTree<Size,Node<Size,Value<'a>>> -> 'b
but given a
FingerTree<Size,Value<'c>> -> seq<'c>
The type 'Node<Size,Value<'a>>' does not match the type 'Value<'b>'
and the squiggles underline the recursive call of toSeq.
I know that the “deeper” type is encapsulated in a Node and in the working code I just unpack it afterwards. But here the compiler trips already before I get the chance to unpack. Trying a for (Value deep) in toSeq deeper do yield deep has the same problem.
I already have a way out, namely to use the toSeq of the “base” Tree and Seq.map unpack afterwards. Not true, trying that yields a very similar error message.
I'm curious what makes this code break and how it could be fixed.
The compiler's error message seems clear to me: toSeq is applicable only to values of type FingerTree<Size, Value<'a>> for some 'a, but you're trying to call it on a value of type FingerTree<Size,Node<Size,Value<'a>>> instead, which is not compatible. There's nothing specific to polymorphic recursion or sequence expressions, these types just don't match.
Instead, it seems like it would be much simpler to make toSeq more generic by taking an input of type FingerTree<Size, 'a> (without any reference to Value), which would enable the recursive call you want. Then you can easily derive the more specific function you actually want by composing the more general toSeq with Seq.map unpack.

Strange (?) type mismatch error in this function

Can someone explain why compiler is giving me this error
Type mismatch. Expecting a
'a [] -> string
but given a
'a [] -> 'a []
The type 'string' does not match the type ''a []'
on this code snippet:
let rotate s: string =
[|for c in s -> c|]
|> Array.permute (function | 0 -> (s.Length-1) | i -> i-1)
while the one below compiles just fine:
let s = "string"
[|for c in s -> c|]
|> Array.permute (function | 0 -> (s.Length-1) | i -> i-1)
Your first snippet defines function rotate with return type of string.
Try to change it to:
let rotate (s: string) =
[|for c in s -> c|]
|> Array.permute (function | 0 -> (s.Length-1) | i -> i-1)
In this form you define a function with one string argument (I suppose that's what you wanted) and inferred return type.

Resources