3
votes

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

2
I would suggest changing the names so that "Noun" is not used twice in different types. It can be confusing to read, and also messes up type inference. The SpecificNoun. qualifier would not be needed if the case names were unique. - Grundoon

2 Answers

4
votes

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
3
votes

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.