Is F# aware of its discriminated unions' compiled forms? - f#

A discriminated union in F# is compiled to an abstract class and its options become nested concrete classes.
type DU = A | B
DU is abstract while DU.A and DU.B are concrete.
With ServiceStack, the serialization of types to JSON strings and back can be customized with functions. With respect to the DU type, here's how I could do it in C#.
using ServiceStack.Text;
JsConfig<DU.A>.SerializeFn = v => "A"; // Func<DU.A, String>
JsConfig<DU.B>.SerializeFn = v => "B"; // Func<DU.B, String>
JsConfig<DU>.DeserializeFn = s =>
if s == "A" then DU.NewA() else DU.NewB(); // Func<String, DU>
Is F# aware of its discriminated unions' compiled forms? How would I get the type of DU.A in F# at compile time?
typeof<DU> // compiles
typeof<DU.A> // error FS0039: The type 'A' is not defined
typeof<A> // error FS0039: The type 'A' is not defined
I can easily enough register a function for deserialization in F#.
open System
open ServiceStack.Text
JsConfig<DU>.RawDeserializeFn <-
Func<_, _>(fun s -> printfn "Hooked"; if s = "A" then A else B)
Is it possible to register serialize functions wholly in F# for the concrete types DU.A and DU.B?

Whilst all the behaviour (the abstract classes etc.) is not just an implemenation detail, it is actually defined by the spec, these things are not accesible from F# - this is a quote from the spec
A compiled union type U has:
· One CLI static getter property U.C for each null union case
C. This property gets a singleton object that represents each such
case.
· One CLI nested type U.C for each non-null union case C. This
type has instance properties Item1, Item2.... for each field of the
union case, or a single instance property Item if there is only one
field. However, a compiled union type that has only one case does not
have a nested type. Instead, the union type itself plays the role of
the case type.
· One CLI static method U.NewC for each non-null union case C.
This method constructs an object for that case.
· One CLI instance property U.IsC for each case C. This
property returns true or false for the case.
· One CLI instance property U.Tag for each case C. This
property fetches or computes an integer tag corresponding to the case.
· If U has more than one case, it has one CLI nested type
U.Tags. The U.Tags typecontains one integer literal for each case, in
increasing order starting from zero.
· A compiled union type has the methods that are required to
implement its auto-generated interfaces, in addition to any
user-defined properties or methods.
These methods and properties may not be used directly from F#.
However, these types have user-facing List.Empty, List.Cons,
Option.None, and Option.Some properties and/or methods.
Importantly, "these methods and properties may not be used from F#".

Daniel is correct, you can do this by registering serialization functions for the base type DU. Here is a fuller example
open System
open ServiceStack.Text
type DU = A | B
let serialize = function
| A -> "A"
| B -> "B"
let deserialize = function
| "A" -> A
| "B" -> B
| _ -> failwith "Can't deserialize"
JsConfig<DU>.SerializeFn <- Func<_,_>(serialize)
JsConfig<DU>.DeSerializeFn <- Func<_,_>(deserialize)
let value = [| A; B |]
let text = JsonSerializer.SerializeToString(value)
let newValue = JsonSerializer.DeserializeFromString<DU[]>(text)
Result:
val value : DU [] = [|A; B|]
val text : string = "["A","B"]"
val newValue : DU [] = [|A; B|]

The fact that a DU in F# is a single type is key to its usefulness. The F# approach would be to use pattern matching:
JsConfig<DU>.SerializeFn <- function
| A -> "A"
| B -> "B"
This should work because the union cases are not only nested types in C#, but subtypes as well. Of course if ServiceStack doesn't consider base type serializers then this won't work.

Related

F#: Why can't I cons an object of type B to a list of type A when B is a subtype of A?

According to the 3rd example on this site http://msdn.microsoft.com/en-us/library/dd233224.aspx, F# lists can contain objects of different types as long as both types derive from the same super-type. However, I can't get the cons (::) operator to add subtypes to a list of the supertype
module test
type A() =
member this.x = 2
type B() =
inherit A()
member this.y = 4
let mutable myList : A list = []
myList <- [B()] // Valid
myList <- B()::myList // Invalid: This expression was expected to have type A but here has type B
Is there a reason I can't use :: to append to the list?
F# does not always insert upcasts (conversion to a base type) automatically, so you have to insert an explicit cast that turns the B value into a value of type A.
Note that F# distinguishes between upcasts - casts to a base class (which are always correct) and downcasts - casts to a derived class (which may fail).
You can either use the upcast keyword or you can use the expr :> Type notation. In both cases, the compiler can fill in the required target type, so you can just write:
myList <- (upcast B())::myList
myList <- (B() :> _)::myList

F#: Is there a way to extend the monad keyword list?

Inside an F# monad, if you say let!, the compiler translates that to a Bind member that you've defined on the monad builder.
Now I see there are Query monads, as shown here on MSDN, where you can say:
query {
for student in db.Student do
select student
count
}
and the select and count, for example, will be translated to the QueryBuilder members Linq.QueryBuilder.Select and Linq.QueryBuilder.Count.
My question is, is this mapping of keywords to members hardwired into the F# compiler, or is it extensible? For example, can I say something like:
FooMonadBuilder() {
bar
}
and somehow tell the F# compiler that bar maps to a FooMonadBuilder.Bar() method?
In F# 2.0 (that is Visual Studio 2010), there is no way to extend the keyword list (other than Ramon's extension). However, the query mechanism in F# 3.0 (Visual Sutdio 11) is extensible and you can define your own keywords similar to select and count.
Here is a basic example that defines something like seq builder with reverse keyword:
type SeqBuilder() =
// Standard definition for 'for' and 'yield' in sequences
member x.For (source : seq<'T>, body : 'T -> seq<'R>) =
seq { for v in source do yield! body v }
member x.Yield item =
seq { yield item }
// Define an operation 'select' that performs projection
[<CustomOperation("select")>]
member x.Select (source : seq<'T>, [<ProjectionParameter>] f: 'T -> 'R) : seq<'R> =
Seq.map f source
// Defines an operation 'reverse' that reverses the sequence
[<CustomOperation("reverse", MaintainsVariableSpace = true)>]
member x.Expand (source : seq<'T>) =
List.ofSeq source |> List.rev
let mseq = SeqBuilder()
The details how this works are not yet documented, but the CustomOperation attribute says that the operation should be treated as a special syntax (you can set various properties to specify how it behaves - MaintainsVariableSpace means that it does not change the values inside sequence). The Projectionparameter attribute specifies that the expression following the keyword should be implicitly converted to a function.
Now, the mseq builder supports both select and reverse:
let q = mseq { for i in 1 .. 10 do
select (i + 100)
reverse }
Short answer: no.
I've extended the compiler to support that, you're welcome to read my blog article http://ramon.org.il/wp/2011/04/taking-computation-expressions-one-step-further/

Inconsistent behavior in pattern matching

type Bar = A | B
type Foo = C of Bar | D of Bar
let case = Unchecked.defaultof<Foo>;;
match case with
| C A -> ""
| C B -> ""
| _ -> "Matches";;
match case with
| C A -> ""
| D B -> ""
| _ -> "Throws"
Quickly skimming over the F# Language Spec, nothing about null-test (which I can't do anyway) seemed related and both types seems to be reference type (AFAIK).
I would assume the behavior in the first case to be correct one.
I think all bets are off once you use Unchecked.defaultof<_> (thus the "Unchecked" ;-)). Null isn't considered a valid value for type Foo from an F# perspective (although it is from the .NET perspective), so I don't think that the pattern matching semantics are defined.
What is it that you're trying to do?
So reading the spec, the first hint is here
b) Types with null as an abnormal value. These are types that do
not admit the null literal, but do have null as an abnormal value.
Types in this category are:
o All F# list, record, tuple, function, class and interface types.
o All F# union types apart from those with null as a normal value
(as discussed in the next paragraph).
For these types, the use of the null literal is not directly
permitted. However it is, strictly speaking, possible to generate a
null value for these types using certain functions such as
Unchecked.defaultof. For these types, null is considered an
abnormal value. The behavior of operations with respect to null values
is defined in §6.9.
This does seem to suggest that on passing in a null value for your union could be some sort of undefined behaviour as the value is "abnormal", Section 6.9 isn't particularly helpful
Looking at the definition for _, it seems like you are right that this is a bug - it states
7.1.7 Wildcard Patterns
The pattern _ is a wildcard pattern and matches any input. For
example:
let categorize x =
match x with
| 1 -> 0
| 0 -> 1
| _ -> 0
I think the most relevnat hints though are later on where the compiled methods for DU's are listed
8.5.3 Compiled Form of Union Types for Use from Other CLI Languages
A compiled union type U will have:
· One CLI static getter property U.C for each nullary union
case C. This will get a singleton object representing that case.
· One CLI nested type U.C for each non-nullary union case C.
This type will have instance properties Item1, Item2.... for each
field of the union case, or a single instance property Item if there
is only one field. A compiled union type with only one case does not
have a nested type. Instead, the union type itself plays the role of
the case type.
· One CLI static method U.NewC for each non-nullary union case
C. This will construct an object for that case.
· One CLI instance property u.IsC for each case C that returns
true or false for the case.
· One CLI instance property u.Tag for each case C that fetches
or computes an integer tag corresponding to the case.
From this, you can see that all of the methods for checking are instance methods, which would require non-nullness. Sine null is "abnormal", the generated code doesn't bother checking, so it throws.
I think you could argue that this is infact a bug, based on the definition of _. However, fixing it would require inserting null checks before every DU pattern matching check, which would slow the code down significantly, so I doubt whether this will be fixed
A pattern match against a DU with two cases compiles to an if/else with type tests. If you translate your examples, the behavior is apparent.
match case with
| C A -> ""
| C B -> ""
| _ -> "Matches"
translates to
if (case is C)
...
else
"Matches"
and
match case with
| C A -> ""
| D B -> ""
| _ -> "Throws"
translates to
if (case is D)
...
else //must be C
//check tag BANG! NRE
And kvb's example: match case with | C _ -> "C" | D _ -> "D"
if (case is D)
//...
else //must be C
"C"
I suppose you could view this as a reasonable optimization (vs. if (case is D) {...} else if (case is C) {...} else { MatchFailureException }) given that null behavior is undefined.
Add a third case to Foo and the problem goes away.
The description of the method Unchecked.defaultof ends with the sentence "This function is unsafe in the sense that some F# values don't have proper null values", which is exactly the case here.
Try this:
let isNull = (case = null) ;;
let isNull = (case = null) ;;
---------------------^^^^
stdin(3,22): error FS0043: The type 'Foo' does not have 'null' as a proper value
>
DUs are intended to be immutable and don't have proper null values.
I'm not able to confirm this at the moment, but wouldn't the AddNullLiteral attribute to the types allow the match to succeed?

F#: is mutual recursion between types and functions possible?

I can use the and keyword to set up mutually recursive function definitions. I can also use and for mutually recursive types, but what if there is a mutually recursive relationship between a type and a function? Is my only option to make the function a member of the type or can I use something similar to and here too?
Edit: Adding a simplified pseudo-example that I hope illustrates what I'm trying to do
// A machine instruction type
type Instruction = Add | CallMethod int (* method ID *) | ...
// A class representing a method definition
type MethodDef (fileName : string) =
member x.Params with get () = ...
member x.Body with get() =
let insts = readInstructions fileName
Array.map toAbstractInst insts
// a more abstract view of the instructions
and AbstractInstruction = AbstAdd | AbstCallMethod MethodDef | ...
// a function that can transform an instruction into its abstract form
let toAbstractInst = function
| Add -> AbstAdd
| CallMethod methodId -> AbstCallMethod (somehowResolveId methodId)
| ...
So you can see here that the recursive relationship is set up pretty indirectly: MethodDef <-> AbstractInst AND MethodDef -> toAbstractInst -> AbstractInstruction (where -> means "depends on")
This question is difficult to answer without an example
If you have mutually recursive types that do not have members, then the types don't need to know about the functions (so you can first define types and then functions).
If you have mutually recursive types that have the functions as members, then the members can see each other (across types) and you should be fine
The only tricky case is when you have mutually recursive types, mutually recursive functions and you also want to exposes some functions as members. Then you can use type extensions:
// Declare mutually recursive types 'A' and 'B'
type A(parent:option<B>) =
member x.Parent = parent
and B(parent:option<A>) =
member x.Parent = parent
// Declare mutually recursive functions 'countA' and 'countB'
let rec countA (a:A) =
match a.Parent with None -> 0 | Some b -> (countB b) + 1
and countB (b:B) =
match b.Parent with None -> 0 | Some a -> (countA a) + 1
// Add the two functions as members of the types
type A with
member x.Count = countA x
type B with
member x.Count = countB x
In this case, you could just make countA and countB members of the two types, because it would be easier, but if you have more complex code that you want to write as functions, then this is an option.
If everything is written in a single module (in a single file), then the F# compiler compiles type extensions as standard instance members (so it looks just like normal type from the C# point of view). If you declare extensions in a separate module, then they will be compiled as F#-specific extension methods.

How to implicitly convert to common super types in F# pattern matches?

Problem Summary
At the moment when using f# I must explicitly coerce a value to the parent type of its type in order to get pattern matching expressions to type check correctly. I would ideally like a neater way of doing.
Example
Suppose I have some class hierachy:
type Foo () =
abstract member Value : unit -> string
type A (i:int) =
inherit Foo ()
override this.Value () = i.ToString()
type B (s:string) =
inherit Foo ()
override this.Value () = s
Ideally, and in some programming languages in normally, I would write the equivalent of the following:
let bar (i:int) : Foo =
match i with
| 1 -> B "one"
| _ -> A i
However this fails to type check correctly, giving me the error, "This expression was expected to have type Foo but here has type B". I don't understand why the compiler doesn't have enough information to infer a common super type for the match expression and then check that the common super type is 'Foo'.
At present I am forced to provide an explicit coercion for every case in the pattern match:
let bar2 (i:int) : Foo =
match i with
| 1 -> (B "one") :> Foo
| _ -> (A i) :> Foo
I would like to avoid this.
Further Notes
Intuition suggests that this is a result of a more general issue. I would have thought though that something as common as pattern matching, or if statements which also exhibit the same property, would have a type checking rule to account for common super types.
Before anyone suggests - I appreciate that if A or B were Object Expressions this would work, but my real example is creating instances of C# classes where they are normal classes.
Is there a way for me to declare functions to implicitly convert types, as for example scala has, so I could apply automatic conversions for the module where I'm doing this generation?
Thanks for any help on this matter.
I would use upcast, a la
[<AbstractClass>]
type Foo () =
abstract member Value : unit -> string
type A (i:int) =
inherit Foo ()
override this.Value () = i.ToString()
type B (s) =
inherit Foo ()
override this.Value () = s
let bar2 i : Foo =
match i with
| 1 -> upcast B "one"
| _ -> upcast A i
You still have to add it to every branch, but this is often preferable to casting to the type, since often the typename is like 20 or 30 characters long (MyNamespace.ThisThingy), whereas upcast is just 6 characters.
But, briefly, the language rules don't allow for anything else, the types of all the branches have to be equal.
I've seen this question a couple of times before, but I just realized that there is quite an interesting way to workaround the issue (without any significant negative effects such as big runtime overhead).
You can use a very simple computation expression that has only Return member. The builder will have a type parameter and Return will expect values of this type. The trick is, that F# does insert automatic upcasts when calling a member. Here is the declaration:
type ExprBuilder<'T>() =
member x.Return(v:'T) = v
let expr<'T> = ExprBuilder<'T>()
To write a simple pattern matching that returns anything as obj, you can now write:
let foo a = expr<obj> {
match a with
| 0 -> return System.Random()
| _ -> return "Hello" }
You still have to be explicit about the return type (when creating the computation expression), but I find the syntax quite neat (but it is definitely a tricky use that could confuse people who'll see it for the first time).

Resources