In F#, How can I attach metadata to discriminated union values? - f#

I want to create something that's kind of like an enum with an F# record type for a value instead of an int. For example, if I've got the union:
type BologneseIngredients = | Spaghetti
| Tomatoes
| MincedBeef
| GrandmasSecretIngredient
I know that spaghetti is always 30cm long and tomatoes are always red. What I could do is have a 'get metadata' function:
let getMetadata = function
| Spaghetti -> { length: 30.0<cm> }
| Tomatoes -> { colour: Color.Red }
| _ -> { }
but I'd really like to keep the definition of the union and the data together. Is there a nice way to do this?

You could add properties to your discriminated union...
type BologneseIngredients =
| Spaghetti
| Tomatoes
| MincedBeef
| GrandmasSecretIngredient
member x.Color =
match x with
| Spaghetti -> Color.AntiqueWhite
| Tomatoes -> Color.Red
| MincedBeef -> Color.Firebrick
| GrandmasSecretIngredient -> Color.Transparent
let foo = Tomatoes
printfn "%A" foo.Color
> Color [Red]

my suggestion:
module Recipes =
type BologneseIngredients = | Spaghetti
| Tomatoes
| MincedBeef
| GrandmasSecretIngredient
let length (ind : BologneseIngredients) : float<cm> option =
match ind with
| Sphaghetti -> Some 30.0<cm>
| _ -> None
// .. or a bit more "metadata"ish
type Metadata =
| Length of float<cm>
| Color of System.Drawing.Color
let metadata =
function
| Sphaghetti -> [ Length 30.0<cm ]
| Tomatoes -> [ Color System.Drawing.Color.Red ]
| ...
let metaLength meta =
meta |> List.tryPick (function | Length l -> Some l | _ -> None)
let getLength = metadata >> metaLength

Related

Match on child discriminated union

If I have a discriminated union with multiple values sharing a child (Apple and MoreApples both have type Apple)...
type Apples =
| GrannySmith
| Gala
type Fruit =
| Apple of Apples
| MoreApples of Apples
| Banana
let speakFruit = function
| Apple GrannySmith
| MoreApples GrannySmith -> "granny smith"
| Apple Gala
| MoreApples Gala -> "gala"
| Banana -> "banana"
Is there a way to match on the sub-union to remove duplication? - something like:
let speakFruit2 = function
| _ GrannySmith -> "granny smith"
| _ Gala -> "gala"
| Banana -> "banana"
I don't think there is a nice way to do this with single pattern, but you can define an active pattern that will give you an alternative perspective of the data where the two kinds of apples are merged:
let (|AnyApple|Banana|) = function
| Apple a | MoreApples a -> AnyApple a
| Banana -> Banana
This hides the standard Banana definition - you should probably use another name to avoid confusion, but the rest stays the same. Now you can pattern match using AnyApple:
let speakFruit = function
| AnyApple GrannySmith -> "granny smith"
| AnyApple Gala -> "gala"
| Banana -> "banana"
How about this?
let speakFruit = function
| Apple x | MoreApples x ->
match x with
| GrannySmith -> "Granny Smith"
| Gala -> "gala"
| Banana -> "banana"
Partial Active Pattern could also be a solution
let (|IsKind|_|) kind z =
match z with
| Apple x | MoreApples x -> if (kind = x) then Some true else None
| _ -> None
let speakFruit x =
match x with
| IsKind GrannySmith z -> "Granny Smith"
| IsKind Gala z -> "Gala"
| Banana -> "banana"
| _ -> "something else"
But to be honest - I agree with Fyodor above. You probably should rethink your types.

Is there a way to encapsulate a pattern in F#?

Is there a way to encapsulate a pattern in F#?
For example, instead of writing this...
let stringToMatch = "example1"
match stringToMatch with
| "example1" | "example2" | "example3" -> ...
| "example4" | "example5" | "example6" -> ...
| _ -> ...
Is there some way to accomplish something along these lines...
let match1to3 = | "example1" | "example2" | "example3"
let match4to6 = | "example4" | "example5" | "example6"
match stringToMatch with
| match1to3 -> ...
| match4to6 -> ...
| _ -> ...
You can do this with Active Patterns:
let (|Match1to3|_|) text =
match text with
| "example1" | "example2" | "example3" -> Some text
| _ -> None
let (|Match4to6|_|) text =
match text with
| "example4" | "example5" | "example6" -> Some text
| _ -> None
match stringToMatch with
| Match1to3 text -> ....
| Match4to6 text -> ....
| _ -> ...

How to Return Specific Discriminated Union from Function

I have a hierarchy of discriminated unions.
type SpecificNoun =
| Noun
| NounPhrase
| Pronoun
| PosesivePronoun
type SpecificModifier =
| Adverb //slowly, quickly, verb + ly (90% of the time)
| Preposition //off, on, together, behind, before, between, above, with, below
type SpecificVerb =
| ActionVerb
| BeingVerb
| PossesiveVerb
| TransitiveVerb
type PartsOfSpeech =
| Noun of SpecificNoun
| Verb of SpecificVerb
| Adjective
| Punctuation
| Modifier of SpecificModifier
I need to translate a string into one of them, but it has to be a PartOfSpeech so I can use it in my match cases. The below code does not compile.
let StringToPartOfSpeech (part:string) =
match part with
| "Noun" -> SpecificNoun.Noun
| "NounPhrase" -> SpecificNoun.NounPhrase
| "Pronoun" -> SpecificNoun.Pronoun
| "PossessivePronoun" -> SpecificNoun.PosesivePronoun
| "Adverb" -> SpecificModifier.Adverb
This is a related question to this: F# - Can I return a discriminated union from a function however, in my case, everything is just straight discriminated unions
You need to return a consistent type from all branches. In your case the PartsOfSpeech type is ideal.
So that means you need to take a type like SpecificNoun.Noun and wrap it in the appropriate case from PartsOfSpeech.
Also, what if the input string doesn't match any of the cases?
In the code below I decided to return a PartsOfSpeech option, but you could raise an exception,
or return a more detailed Success/Failure type, etc.
let StringToPartOfSpeech (part:string) =
match part with
| "Noun" ->
SpecificNoun.Noun |> PartsOfSpeech.Noun |> Some
| "NounPhrase" ->
SpecificNoun.NounPhrase |> PartsOfSpeech.Noun |> Some
| "Pronoun" ->
SpecificNoun.Pronoun |> PartsOfSpeech.Noun |> Some
| "PossessivePronoun" ->
SpecificNoun.PosesivePronoun |> PartsOfSpeech.Noun |> Some
| "Adverb" ->
SpecificModifier.Adverb |> PartsOfSpeech.Modifier |> Some
| _ -> None
Your code doesn't compile because you are returning two values with different types :
let StringToPartOfSpeech (part:string) =
match part with
| "Noun" -> Noun // type of SpecificNoun
| "NounPhrase" ->NounPhrase // type of SpecificNoun
| "Pronoun" -> Pronoun // type of SpecificNoun
| "PossessivePronoun" ->PosesivePronoun // type of SpecificNoun
| "Adverb" -> Adverb // type of SpecificModifier
Why you didn't use your type PartsOfSpeech ?
Try the following code :
type PartsOfSpeech =
| PNoun of SpecificNoun
| PVerb of SpecificVerb
| PAdjective
| PPunctuation
| PModifier of SpecificModifier
| PUnknown
let StringToPartOfSpeech (part:string) =
match part with
| "Noun" -> PNoun (Noun)
| "Adverb" -> PModifier (Adverb)
| _ -> PUnknown
Plus, to avoid compiler warnings, I add a case for a Unknown String.

Wildcard for type when matching discriminated unions

In the following real world example I do a match:
type Style = Nice | Cool | Ugly
type Color = Blue | Yellow | Orange | Grey | Cyan
type ClothingProperties = Style * Color
type Clothes =
| Jeans of ClothingProperties
| Pullover of ClothingProperties
| Shirt of ClothingProperties
type Person =
| Person of string * Clothes
let team = [Person("Jan", Jeans (Cool, Blue)); Person("Pete", Shirt (Nice, Cyan)); Person("Harry", Pullover (Ugly, Grey))]
let matchPerson person=
match person with
| Person(name, Jeans(Ugly,_) ) -> printfn "%s wears ugly stuff." name
| Person(name, Pullover(Ugly,_) ) -> printfn "%s wears ugly stuff." name
| Person(name, Shirt(Ugly,_) ) -> printfn "%s wears ugly stuff." name
| _ -> ()
List.iter(fun x->matchPerson x) team
Is there a way to create a more efficient match, so I don't need to check each clothing case? Something like this:
let matchPerson person=
match person with
| Person(name, _ (Ugly,_) ) -> printfn "%s wears ugly stuff." name
| _ -> ()
Of course, this is not correct syntax. But how can I achieve such an effect?
That's not straightforward, you can use reflection, but the problem is that your discriminated union needs some redesign, because if you know there will always be a ClothingProperties then you can change it to this:
type Style = Nice | Cool | Ugly
type Color = Blue | Yellow | Orange | Grey | Cyan
type ClothingProperties = Style * Color // or just use a tuple
type Clothe =
| Jeans
| Pullover
| Shirt
type Clothes = Clothe *ClothingProperties
type Person =
| Person of string * Clothes
let matchPerson person=
match person with
| Person(name, (_,(Ugly,_)) ) -> printfn "%s wears ugly stuff." name
| _ -> ()
A related issue is described here Is it possible to pass discriminated union tags as arguments?

How can I make this match expression more concise?

Learning F# by writing blackjack. I have these types:
type Suit =
| Heart = 0
| Spade = 1
| Diamond = 2
| Club = 3
type Card =
| Ace of Suit
| King of Suit
| Queen of Suit
| Jack of Suit
| ValueCard of int * Suit
I have this function (ignoring for now that aces can have 2 different values):
let NumericValue =
function | Ace(Suit.Heart) | Ace(Suit.Spade) | Ace(Suit.Diamond) | Ace(Suit.Club) -> 11
| King(Suit.Heart) | King(Suit.Spade)| King(Suit.Diamond) | King(Suit.Club) | Queen(Suit.Heart) | Queen(Suit.Spade)| Queen(Suit.Diamond) | Queen(Suit.Club) | Jack(Suit.Heart) | Jack(Suit.Spade)| Jack(Suit.Diamond) | Jack(Suit.Club) -> 10
| ValueCard(num, x) -> num
Is there a way I can include a range or something? Like [Ace(Suit.Heart) .. Ace(Suit.Club)]. Or even better Ace(*)
You want a wildcard pattern. The spec (ยง7.4) says:
The pattern _ is a wildcard pattern and matches any input.
let numericValue = function
| Ace _-> 11
| King _
| Queen _
| Jack _ -> 10
| ValueCard(num, _) -> num

Resources