I'm fairly new to F#, and I want to model things in the real world that have fairly complex "has-a" relationships. At the top of the hierarchy are four types, A - D, with these relationships:
A
|
+--A
|
+--B
| |
| +--B
| |
| +--D
| |
| +--D
|
+--C
| |
: +--D
| |
| +--D
:
So type B can have a "parent" of either A or B, and type D can have a parent of B, C or D.
I want to use discriminated unions to constrain the parent of each type, so they can't be assigned to invalid parents, e.g:
type B_Parent = A | B
type D_Parent = B | C | D
Then I want to use records to model each type, where one field is the parent, e.g:
type A = { parent:A_Parent; ... }
type B = { parent:B_Parent; ... }
type C = { parent:C_Parent; ... }
type D = { parent:D_Parent; ... }
C_Parent isn't a problem because its parent type A is declared beforehand. And I have used 'A option' for A_Parent. But I haven't been able to work out how to define B_Parent and D_Parent, because of their nested dependency on themselves, and on other types?
First, one very important thing: when you write
type B_Parent = A | B
you are not declaring that B_Parent is a DU joining the two previously-defined types A and B. There is no syntax for that.
What the line above is actually doing is defining two empty subtypes of B_Parent, which have nothing to do with the original A and B. It is equivalent to:
type B_Parent = B_Parent.A | B_Parent.B
In order to re-use existing types inside a DU, you need to give a name to the case - effectively wrapping them in another type. So the proper declaration can be something like:
type B_Parent = P_a of A | P_b of B
With that aside - as Anton said, the keyword for defining mutually referential types is and. That said, it is best to keep such mutual references as small and tight as possible. So we could do something like this:
type A = { parent:A_Parent; ... }
and A_Parent = P_a of A
| None
type B = { parent:B_Parent; ... }
and B_Parent = P_a of A
| P_b of B
type C = { parent:C_Parent; ... }
and C_Parent = P_a of A
type D = { parent:D_Parent; ... }
and D_Parent = P_b of B
| P_c of C
| P_d of D
We could have used just A option for A_Parent, and just A for C_Parent, but I think that keeping the case names consistent will probably make things more readable.
You can use the and keyword to define types that are mutually related, for example:
type B_Parent = A | B
and D_Parent = B | C | D
and A_Parent = A
and C_Parent = B
and A = { parent:A_Parent }
and B = { parent:B_Parent }
and C = { parent:C_Parent }
and D = { parent:D_Parent }
See also this related SO question. The and keyword is also helpful when defining mutually recursive functions.
Related
I have a DU and I'm overriding the Equals method. Based on the current DU value, I would like to call the base equality method or my custom one. However, it's not letting me access "base". Any idea on how to work around this?
type Test =
| A of string
| B of int64
override this.Equals(other) =
let other' = other :?> Test
match other' with
| A str -> str = "a"
| B i -> base.Equals this other //how do I do this?
First, any F# discriminated union will have obj as base class, so just use obj.Equals.
Second, Equals is a .NET method, not an F# function, so its arguments must be given in a tupled form - i.e. Equals(x,y) instead of Equals x y.
Finally, if you implement a custom Equals, you also need to add [<CustomEquality; NoComparison>]
So:
[<CustomEquality; NoComparison>]
type Test =
| A of string
| B of int64
override this.Equals(other) =
let other' = other :?> Test
match other' with
| A str -> str = "a"
| B i -> obj.Equals(this, other)
I am trying to find an elegant way of assigning keys to symbols without having to do something like the following.
let [<Literal>] North = ConsoleKey.UpArrow // etc.
I'd rather do something like this, using only one attribute. Is there any way I can do that?
[<Literal>]
type Direction =
| North of ConsoleKey.UpArrow
| East of ConsoleKey.RightArrow
| South of ConsoleKey.DownArrow
| West of ConsoleKey.LeftArrow
Assuming your aim is to use these in a pattern match, here is one way to do it:
// Use a type alias to shorten the name for ConsoleKey
type Key = ConsoleKey
// Create a general purpose active pattern that simply tests for equality
let (|Is|_|) a b = if a = b then Some () else None
// This is how you would use it
let describeMovement key =
match key with
| Is Key.UpArrow -> "up"
| Is Key.RightArrow -> "right"
| Is Key.DownArrow -> "down"
| Is Key.LeftArrow -> "left"
| _ -> "invalid"
Right, I have this data type in Rascal:
data Type = Any() | Void() | Int() | Not(Type l) | And(set[Type] es) | Or(set[Type] es);
What I want to do is define another type like this:
data Primitive = Any() | Void() | Int();
And then be able to do things like this:
Primitive p = Any();
Type d = p;
Or, for example, match against Primitive when simplifying Type. Something like this:
public Type reduce(Not(Primitive p)) = p;
Currently, the only solution I can see is to expand the above rule for each case like so:
public Type reduce(Not(Any)) = Any();
public Type reduce(Not(Void)) = Void();
public Type reduce(Not(Int)) = Int();
I'm guessing there is a way to do this, but I didn't figure it out yet ... thoughts?
The short answer: although Abstract Data Types can be extended (i.e., their definition can be extended across modules) there is no direct inheritance.
Work arounds:
Solution A
data Type = Any() | Void() | Int() | Not(Type l) | And(set[Type] es) | Or(set[Type] es);
bool isPrim(Any()) = true;
bool isPrim(Void()) = true;
bool isPrim(Int()) = true;
default bool isPrim(Type t) = false;
Type reduce(Not(Type t)) = t when isPrim(t);
default Type reduce(Type t ) = t;
Here all constructors for Type are in a single ADT and the predicate isPrim selects the primitives. For example, reduce(Not(Void())) will reduce to Void().
Solution B
data Primitive = Any() | Void() | Int();
data Type = prim(Primitive p) | Not(Type l) | And(set[Type] es) | Or(set[Type] es);
Type reduce(Not(prim(Primitive p))) = prim(p);
default Type reduce(Type t ) = t;
Here the primitives are collected in a separate ADT Primitive and they are included in Type via the constructor prim. Now reduce(Not(prim(Void()))) will reduce to prim(Void()).
Final Notes
We would also prefer to have inheritance (without the extra constructor prim as in Solution B) but for various technical reasons we did not include it. Although desirable, I am not sure that we will ever do.
Note the functions preceded by default, they are the catch all case when the other declarations of a function do not match.
All functions are public, unless preceded by the key word private.
Nice question. Rascal does not feature user-defined sub-typing and typing for data types is nominal. That answers your question in theory, so how does that work in practise?
The answer for data types is slightly different for syntax types, so here follows both stories;
There many are different idioms to model a hierarchy of data-structures, we'll show only three here for the sake of simplicity;
Here's a way to extend a data-type with new features which does not involve adding new types, this produces an over-approximate model of what you intended:
// first the primitive types are defined (I've added Not here to make a point later):
data Type = Any() | Void() | Int() | Not(Type l);
// then the extension is added (perhaps in a different module)
data Type = And(set[Type] es) | Or(set[Type] es);
// the second definition adds its alternatives also to the child of `Not`.
The second way is more close to an actual extension, because the original Type is not extended, and no "junk" is added accidentally:
// we give the original type a unique name:
data Primitive = Any() | Void() | Int();
// For the extension the Primitive type is not polluted with the new constructors, but
// it was wrapped inside a singleton constructor `prim`
data Type = prim(Primitive super) | And(set[Type] es) | Or(set[Type] es);
Of course, this second solution will make you add prim constructors in possible pattern matches you might do, but the / deep match operator will allow you to ignore it where possible. For example:
bool evalt(prim(p)) = evalp(p);
bool evalp(Any()) = true;
bool evalp(Not(p)) = !evalp(p);
bool containsVoid(Type t) = /Void() := t;
Now for syntax types the story is similar but since chain rules in syntax types are invisible, it gives some additional flavor:
syntax Primitive = "any" | "void" | "int";
// notice the first chain rule or "injection" of Primitive into Type:
syntax Type = Primitive | left Type "∧" Type > left Type "∨" Type;
bool evalt((Type) `any`) = true; // the chain rule is parsed but invisible
People have been discussing to add implicit chaining to the abstract data-types as well, for its attractive to simulate sub-typing like so. I guess that would be like Scala's implicits. The jury is still out on that one.
Is it possible to add constant field values to F# discriminated unions?
Can I do something like this?
type Suit
| Clubs("C")
| Diamonds("D")
| Hearts("H")
| Spades("S")
with
override this.ToString() =
// print out the letter associated with the specific item
end
If I were writing a Java enum, I would add a private value to the constructor like so:
public enum Suit {
CLUBS("C"),
DIAMONDS("D"),
HEARTS("H"),
SPADES("S");
private final String symbol;
Suit(final String symbol) {
this.symbol = symbol;
}
#Override
public String toString() {
return symbol;
}
}
Just for completeness this is what is meant:
type Suit =
| Clubs
| Diamonds
| Hearts
| Spades
with
override this.ToString() =
match this with
| Clubs -> "C"
| Diamonds -> "D"
| Hearts -> "H"
| Spades -> "S"
The closest thing to your requirement is F# enums:
type Suit =
| Diamonds = 'D'
| Clubs = 'C'
| Hearts = 'H'
| Spades = 'S'
let a = Suit.Spades.ToString("g");;
// val a : string = "Spades"
let b = Suit.Spades.ToString("d");;
// val b : string = "S"
The problem with F# enums is non-exhaustive pattern matching. You have to use wildcard (_) as the last pattern when manipulating enums. Therefore, people tend to prefer discriminated unions and write explicit ToString function.
Another solution is to make a mapping between constructors and corresponding string values. This is helpful in case we need to add more constructors:
type SuitFactory() =
static member Names = dict [ Clubs, "C";
Diamonds, "D";
Hearts, "H";
Spades, "S" ]
and Suit =
| Clubs
| Diamonds
| Hearts
| Spades
with override x.ToString() = SuitFactory.Names.[x]
Pretty sure you can't, but is trivial to write a function that pattern matches and then compose the two things
In F#, given
type MyType = A | B | C | D | E | F | G
How do I randomly define an instance of MyType?
This ought to work:
let randInst<'t>() =
let cases = Reflection.FSharpType.GetUnionCases(typeof<'t>)
let index = System.Random().Next(cases.Length)
let case = cases.[index]
Reflection.FSharpValue.MakeUnion(case, [||]) :?> 't
This code assumes that the union cases are all nullary and that the type you're using is actually a union type, but it would be easy to explicitly check those assumptions and throw meaningful exceptions if desired.
Select a random number, then pattern match that number with different branches returning a different instant?