Unboxing values of F# discriminated unions - reflection

Some functions of my F# code receive values boxed as object even though the underlying values are typed. If the value is a discriminated union, it's not possible to unbox it back to its F# type. Here is a simple example:
type Result<'TOk,'TError> =
| Ok of 'TOk
| Error of 'TError
type ResultA = Result<string, int>
let a = Ok "A"
let o = box a
match o with
| :? ResultA -> printfn "match ResultA"
// | :? ResultA.Ok -> printfn "match" // doesn't compile
| _ when o.GetType().DeclaringType = typedefof<ResultA> -> printfn "match via reflection"
| _ -> printfn "no match"
The output from this example is "match via reflection", ResultA is never matched because the boxed value is of a different CLR type - Result.Ok. Since F# discriminated union cases are represented as its own types, the boxed value doesn't match the type ResultA. Moreover, it's not possible to match it to ResultA.OK because inside F# code it's not a legal type. The only option seems to be manual instantiation of a value using reflection, which is inefficient and silly because the value is already instantiated, it's here, it just can not be accessed in F# code once it's boxed.
Am I overlooking something? Is there a more straightforward way of unboxing an F# discriminated union value?

You're just matching a different type. Your variable a is not of type ResultA, but of generic type Result<string, 'a>, which when boxed gets coerced to Result<string, obj>.
Either make the variable have the right type explicitly:
let a : ResultA = Ok "A"
Or match with the right type:
match o with
| :? Result<string, obj> -> printfn "match ResultA"
Both options will work.
A note on your assumption:
ResultA is never matched because the boxed value is of a different CLR type - Result.Ok
That is not the reason. Matching with a type works just the same as the is/as operators in C# - i.e. it matches subtypes as well as the exact type. And DU members get compiled as subtypes of the DU type itself. That is how F# can get .NET to handle different cases as one type.
A note on runtime typing in general:
Handling types at runtime shouldn't be necessary. Try to avoid it if at all possible. The rule of thumb should be, if you find yourself handling types at runtime, you've probably modeled something wrong.
This is especially true if you don't know exactly how everything works.

Related

SProxy in purescript?

What's the use of Sproxy in purescript?
In Pursuit, it's written as
data SProxy (sym :: Symbol)
--| A value-level proxy for a type-level symbol.
and what is meant by Symbol in purescipt?
First, please note that PureScript now has polykinds since version 0.14 and most functions now use Proxy instead of SProxy. Proxy is basically a generalisation of SProxy.
About Symbols and Strings
PureScript knows value level strings (known as String) and type level strings (known as Symbol).
A String can have any string value at runtime. The compiler does not track the value of the string.
A Symbol is different, it can only have one value (but remember, it is on the type level). The compiler keeps track of this string. This allows the compiler to type check certain expressions.
Symbols in Practice
The most prominent use of Symbols is in records. The difference between a Record and a String-Map is that the compiler knows about the keys at compile time and can typecheck lookups.
Now, sometimes we need to bridge the gap between these two worlds: The type level and the value level world. Maybe you know that PureScript records are implemented as JavaScript objects in the official compiler. This means we need to somehow receive a string value from our symbol. The magical function reflectSymbol allows us to turn a symbol into a string. But a symbol is on the type level. This means we can only write a symbol where we can write types (so for example in type definition after ::). This is where the Proxy hack comes in. The SProxy is a simple value that "stores" the type by applying it.
For example the get function from purescript-records allows us to get a value at a property from a record.
get :: forall proxy r r' l a. IsSymbol l => Cons l a r' r => proxy l -> Record r -> a
If we apply the first paramerter we get:
get (Proxy :: Proxy "x") :: forall r a. { x :: a | r } -> a
Now you could argue that you can get the same function by simply writing:
_.x :: forall r a. { x :: a | r } -> a
It has exactly the same type. This leads to one last question:
But why?
Well, there are certain meta programming szenarios, where you don't programm for a specific symbol, but rather for any symbol. Imagine you want to write a JSON serialiser for any record. You might want to "iterate" over every property of the record, get the value, turn the value itself into JSON and then concatinate the key value pair with all the other keys and values.
An example for such an implementation can be found here
This is maybe not the most technical explanation of it all, but this is how I understand it.

Ocaml - Runtime compilation of code as a string

I want to parse and compile a function that I have written at runtime, for example I have the following string I generated at runtime:
let str = "fun x y z -> [x; y; z;]"
I am looking for something that will allow me to do something similar to:
let myfun = eval str
(* eval returns the value returned by the code in the string so myfun will
have the type: 'a -> 'a -> 'a -> 'a list*)
Is there a way to do that in OCaml? I came across Dynlink but I am looking for a simpler way to do it.
There is no easier solution than compiling the code and Dynlinking the resulting library.
Or equivalently, one can use the REPL, write the string to the file system and them load it with #use.
Depending on your precise use case, MetaOCaml might be an alternative.
Another important point is that types cannot depend on values in a non-dependently typed language. Thus the type of eval needs to be restricted. For instance, in the Dynlinking path, the type of dynamically linked functions will be determined by the type of the hooks used to register them.

F#: How to get the type of an instance of an empty discriminated union case using Reflection?

I read the answers of those questions:
Generic F# function: How to get the Type of an F# Discriminated Union?
C# types for empty F# discriminated union cases
(mine) How to get the type of each union case for a given union type in F#
But I found out something surprising about the underlying type of discriminated unions:
type Union1 =
| A
| B
type Union2 =
| A
| B of int
[<EntryPoint>]
let main argv =
printfn "%A" (Union1.A.GetType())
printfn "%A" (Union1.B.GetType())
printfn "%A" (Union2.A.GetType())
printfn "%A" (Union2.B(32).GetType())
0
Program+Union1
Program+Union1
Program+Union2+_A
Program+Union2+B
Hence my question how I can discriminate a case based on the type when a case is empty?
There is no way to distinguish between two union cases with no parameters based on type, because their type in compiled .NET code is the same. You can see that this is the case by slightly tweaking your example:
type Union1 = A | B
Union1.A.GetType() = Union1.B.GetType() // Returns 'true'
The F# compiler compiles Union1 as a class with a numerical Tag field. For cases with no additional parameters like A and B here, it will just create an instance of Union1 and set the Tag field to 0 or 1.
When you have a union case with additional parameters, then the compiler generates a new inherited class that then stores values of these parameters (which is why you get a different type for Union2.B).
From the F# perspective, values of a discriminated union have the same type (even if the way DUs are compiled means that the type can sometimes be an inherited class), so there is no reason to expect that you would be able to distinguish cases based on a type - if you have a case where you need this, you should probably reconsider your approach.

Is the same Empty returned when it matched in a function?

we have mapOptional from the NICTA course:
mapOptional :: (a -> b) -> Optional a -> Optional b
mapOptional _ Empty = Empty
mapOptional f (Full a) = Full (f a)
When matching f we obviously use that function that was passed, what about the Empty? and what about Full?
There is nothing in Haskell that lets you observe whether the two Emptys are the same Empty or not, and no guarantees about what an implementation must do with that code in that regard.
That said, in GHC, nullary constructors for a given parameterized type are shared across all parameterizations; so there is just one Empty in the whole program, and just one [], and so forth.
They can't be the same Empty, the argument has the type Optional a and the output has the type Optional b. When I try to force some sort of reuse, I will typically use something of the type
mapOptional _ a#Empty = a
This won't compile, and I don't think that's implementation dependent.

Type error when using recursion accumulating lists

A dictionary is a list of pairs. The task is to take a dictionary and return a pair of lists: the keys and values.
I tried to do it iterating (using recursion) over the dictionary and accumulating keys and values in two lists, but I get a type error that baffles me.
Note: I am NOT looking for an alternative solution to the task. I want to understand the error and how to correct it.
Here is the code:
let lists_of_dict dict =
let rec separate dict keys values =
match dict with
[] -> (keys, values)
| (k, v)::t -> separate t k::keys v::values
(* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *)
in
separate dict [] []
(the underlined part is characters 19-47 of line 5)
Here is the error message:
File "lod-problem.ml", line 5, characters 19-47:
Error: This expression has type 'a list
but an expression was expected of type 'b * 'c
I can't understand why Ocaml deduces that type, and how to make it understand that the expression has type ('a list * 'b list) (if I'm not mistaken).
separate t k::keys v::values parses as (separate t k) :: (keys v) :: values, not separate t (k::keys) (v::values) as you intended.
So OCaml sees that you build the result using :: and thus the result is going to be a list (never mind the fact that the arguments for :: also have the wrong type - the type checker never gets that far), but the expected result is a tuple (because that's what the first case produced). So at that point the type checker errors out.

Resources