I have some nested Discriminated Unions
type Job = Sniff | Guard
type Dog = Chihuahua | GermanShepherd of Job
Here's a function that takes a Dog and returns a string.
let dogPrinter d =
match d with
| Chihuahua -> ""
| GermanShepherd g ->
match g with
| Sniff -> ""
| Guard -> ""
I can convert the first match to the function syntax:
let dogPrinter = function
| Chihuahua -> ""
| GermanShepherd g ->
match g with
| Sniff -> ""
| Guard -> ""
How can I convert the second match to function?
The idiomatic way of avoiding nested matches in scenarios like this is to use nested patterns:
let dogPrinter = function
| Chihuahua -> ""
| GermanShepherd Sniff -> ""
| GermanShepherd Guard -> ""
You can nest patterns as deeply as you need, just like you can nest expressions when creating the values.
The only way I could think of, which I think might generally be the best approach, because it separates the concerns appropriately. However, I don't think this function keyword adds any value. I usually just stick with the match keyword.
let jobPrinter = function
| Sniff -> ""
| Guard -> ""
let dogPrinter = function
| Chihuahua -> ""
| GermanShepherd job -> job |> jobPrinter
Although I think #glennsl answer is what you should consider doing here's an aswer to what OP asked for:
type Job = Sniff | Guard
type Dog = Chihuahua | GermanShepherd of Job
let dogPrinter = function
| Chihuahua -> "Voff"
| GermanShepherd g ->
g |> ( function
| Sniff -> "Sniff"
| Guard -> "Guard"
)
Related
I have built a toy spreadsheet to help learn F#. When I process the text for a new cell I store it as a discriminated type. To parse it I feel I should be able to do something like:
let cv =
match t with
| _ when t.Length=0 -> Empty
| x when t.[0]='=' -> Expr(x)
| x when t.[0]='\"' -> Str(x)
| (true,i) when Int32.TryParse t -> IntValue(i) // nope!
| _ -> Str(t)
I have tried quite a few combinations but I cannot get TryParse in the guard. I have written a helper:
let isInt (s:string) =
let mutable m:Int64 = 0L
let (b,m) = Int64.TryParse s
b
I can now write:
| _ when Utils.isInt t -> IntValue((int)t)
This seems like a poor solution as it discards the converted result. What the correct syntax to get TryParse into the guard?
I think an active pattern will do what you want:
let (|Integer|_|) (str: string) =
let flag, i = Int32.TryParse(str)
if flag then Some i
else None
let cv =
match t with
| _ when t.Length=0 -> Empty
| x when t.[0]='=' -> Expr(x)
| x when t.[0]='\"' -> Str(x)
| Integer i -> IntValue(i)
| _ -> Str(t)
But if you really want TryParse in the guard condition (and you don't mind parsing twice), you could do this:
| x when fst (Int32.TryParse(t)) -> IntValue (Int32.Parse(x))
I have a function that pattern matches its argument, which is a string:
let processLexime lexime
match lexime with
| "abc" -> ...
| "bar" -> ...
| "cat" -> ...
| _ -> ...
This works as expected. However, I'm now trying to extend this by expressing "match a string containing only the following characters". In my specific example, I want anything containing only digits to be matched.
My question is, how can I express this in F#? I'd prefer to do this without any libraries such as FParsec, since I'm mainly doing this for learning purposes.
You can use active patterns: https://msdn.microsoft.com/en-us/library/dd233248.aspx
let (|Integer|_|) (str: string) =
let mutable intvalue = 0
if System.Int32.TryParse(str, &intvalue) then Some(intvalue)
else None
let parseNumeric str =
match str with
| Integer i -> printfn "%d : Integer" i
| _ -> printfn "%s : Not matched." str
One way would be an active pattern
let (|Digits|_|) (s:string) =
s.ToCharArray() |> Array.forall (fun c -> System.Char.IsDigit(c)) |> function |true -> Some(s) |false -> None
then you can do
match "1" with
|Digits(t) -> printf "matched"
I would use regular expressions combined with active patterns. With regular expressions you can easily match digits with \d and active patterns makes the syntax nice inside your match.
open System.Text.RegularExpressions
let (|ParseRegex|_|) regex str =
let m = Regex("^"+regex+"$").Match(str)
if (m.Success) then Some true else None
let Printmatch s =
match s with
| ParseRegex "w+" d -> printfn "only w"
| ParseRegex "(w+|s+)+" d -> printfn "only w and s"
| ParseRegex "\d+" d -> printfn "only digis"
|_ -> printfn "wrong"
[<EntryPoint>]
let main argv =
Printmatch "www"
Printmatch "ssswwswwws"
Printmatch "134554"
Printmatch "1dwd3ddwwd"
0
which prints
only w
only w and s
only digis
wrong
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.
I have 2 nested discriminated unions:
type ServiceTypes =
| Contexts
| Context of int
| Producers
type ServiceActions =
| Get of ServiceTypes
| Update of ServiceTypes
And a nested match statement:
let s_action = match action with
| Get(stype) -> sprintf "Get%s" (match stype with
| Contexts -> sprintf "Contexts"
| Context(id) -> (sprintf "Context/%d" id))
| _ -> raise (RequestException("get"))
| Update(stype) -> sprintf "Update%s" (match stype with
| Producers -> (sprintf "Producers")
| _ -> raise (RequestException("update")))
The goal is to build a request string with a call looking like that req.Send(Update Producers).
Anyway for a reason that I do not understand, the compiler gives me 2 warnings:
on the Update(stype) I get a This rule will never be matched
on the the first match stype I get a Incomplete pattern matches on this expression. For example, the value 'Producers' may indicate a case not covered by the pattern(s).
So the question is why do I get these 2 warnings? Did I miss something on the way matching works?
While nested match expressions are sometimes warranted, in this particular case I would write a more readable single-level match, if I were you:
let s_action =
match action with
| Get Contexts -> "GetContexts"
| Get (Context id) -> sprintf "GetContext/%d" id
| Update Producers -> "UpdateProducers"
| Get _ -> raise (RequestException "get")
| Update _ -> raise (RequestException "update")
which achieves exactly the same effect as your code.
Your closing parenthesis is in the wrong place.
| Context(id) -> (sprintf "Context/%d" id))
| _ -> raise (RequestException("get"))
should be
| Context(id) -> (sprintf "Context/%d" id)
| _ -> raise (RequestException("get")))
Indeed, for the sake of clarity I would get rid of all extraneous parentheses (which in this case is actually every parenthesis):
let s_action =
match action with
| Get stype -> match stype with
| Contexts -> "Contexts"
| Context id -> sprintf "Context/%d" id
| _ -> RequestException "get" |> raise
|> sprintf "Get%s"
| Update stype -> match stype with
| Producers -> "Producers"
| _ -> RequestException "update" |> raise
|> sprintf "Update%s"
Personally I find this more readable, but of course that's subjective so YMMV.
Since you closed the paratheses in the wrong point, your code actually becomes:
let s_action =
match action with
| Get(stype) -> sprintf "Get%s" (match stype with
| Contexts -> sprintf "Contexts"
| Context(id) -> (sprintf "Context/%d" id))
| _ -> raise (RequestException("get")) (* Closing parenthesis should be here *)
| Update(stype) -> sprintf "Update%s" (match stype with
| Producers -> (sprintf "Producers")
| _ -> raise (RequestException("update")))
Obviously you can see the first match stype with doesn't cover Producers and the last pattern Update(stype) never matches due to the previous pattern of _. Therefore, all compiler warnings are justified.
You seem to overuse paratheses; here is a cleaned up version:
let s_action =
match action with
| Get stype -> sprintf "Get%s" <| match stype with
| Contexts -> sprintf "Contexts"
| Context id -> sprintf "Context/%d" id
| _ -> raise <| RequestException "get"
| Update stype -> sprintf "Update%s" <| match stype with
| Producers -> sprintf "Producers"
| _ -> raise <| RequestException "update"
Say I've got some code like this
match exp with
| Addition(lhs,rhs,_) -> Addition(fix lhs,fix rhs)
| Subtraction(lhs,rhs,_) -> Subtraction(fix lhs,fix rhs)
is there any way that would allow me to do something like
match exp with
| Addition(lhs,rhs,_)
| Subtraction(lhs,rhs,_) -> X(fix lhs,fix rhs)
where X be based on the actual pattern being matched
I like #kvb's answer.
This does suggest that you may want to redefine the DU, though:
type Op = | Add | Sub
type Expr = | Binary of Op * Expr * Expr
You can use an active pattern:
let (|Binary|_|) = function
| Addition(e1,e2) -> Some(Addition, e1, e2)
| Subtraction(e1,e2) -> Some(Subtraction, e1, e2)
| _ -> None
let rec fix = function
| Binary(con,lhs,rhs) -> con(fix lhs, fix rhs)
| _ -> ...