How can I access a nested discriminated union value? - f#

I created a nested discriminated union:
type hsAlgorithm = HS256 | HS384 | HS512
type rsAlgorithm = RS256 | RS384 | RS512
type esAlgorithm = ES256 | ES384 | ES512
type Algorithm =
| HMAC of hsAlgorithm
| RSA of rsAlgorithm
| ECDsa of esAlgorithm
I create an Algorithm variable:
> let a = HMAC HS256;;
val a: Algorithm = HMAC HS256
Is there a property to directly access HS256?
I wrote a function which matches the algorithm and returns the value but I wonder whether I can access the value directly.
Below is my function to match the value:
let extractAlgorithm (algorithm: Algorithm) =
match algorithm with
| HMAC x -> x.ToString()
| RSA x -> x.ToString()
| ECDsa x -> x.ToString()

The simple answer to your question is "no". Each DU case can contain different types so there isn't generally a way to access the value inside each one in a common way. If you find yourself needing this then you could probably find a better type structure. Here's a possible alternative:
type SHA =
| SHA256
| SHA384
| SHA512
member this.IdSuffix =
match this with
| SHA256 -> "256"
| SHA384 -> "384"
| SHA512 -> "512"
type AlgoType =
| HMAC
| RSA
| ECDsa
member this.IdPrefix =
match this with
| HMAC -> "HS"
| RSA -> "RS"
| ECDsa -> "ES"
// Use a record to define an algorithm, because every algorithm has a type AND a "HS"
type Algorithm =
{ AlgoType : AlgoType
SHA : SHA }
member this.Id = this.AlgoType.IdPrefix + this.SHA.IdSuffix
let algo = { AlgoType = HMAC; SHA = SHA256 }
algo.Id // returns "HS256"
Side note: Avoid using .ToString() or string on DUs. It's actually very slow and has caused significant performance issues for me in the past, even when working on relatively simple applications. See here for more details. I've included members on the types above to build up the algorithm's ID string.

but I wonder whether I can access the value directly.
You can make nested match patterns, like this:
let extractAlgorithm (algorithm: Algorithm) =
match algorithm with
| HMAC(HS384) -> "HS"
| RSA x -> x.ToString()
| ECDsa x -> x.ToString()
If you mean: how to access all these x as a single x, then the answer is: it depends. Only if all x have the same type, you can write something like the following (this won't work with your code):
let extractAlgorithm (algorithm: Algorithm) =
match algorithm with
| HMAC x
| RSA x
| ECDsa x -> x.ToString()
But since F# is a type-safe language, and you have defined three different types for each of the HMAC|RSA|ECDSa types, it won't let you do that.
If all you want is access to ToString(), just use the string function. You can just do: string algorithm. And more in general, you shouldn't use .ToString() as it is unsafe and may raise an NRE, where string never fails.

Related

What is the best way to enhance Map in F# such that it reports the key when raising KeyNotFoundException?

I find it impractical that F# Maps do not report the key in KeyNotFoundExceptions:
let m = Map.empty<int,string>
m.[2]
>> KeyNotFoundException: The given key was not present in the dictionary. // ok, and which key?
The problem certainly does not arise in this example where the key is present but when the exception comes out from a running program. I tried to extend Item, but extensions do not override (https://stackoverflow.com/a/46251858/857848)
module Ext =
type Map<'k, 'v when 'k : comparison> with
member o.Item k = // with better exception
match o.TryGetValue k with
| true, value -> value
| false,_ -> failwithf "key not found in map: %A" k
open Ext
I will wrap Map, to get it done, but wondered if there is a simpler alternative.
I don't think there's any practical way to do this, and I suspect that wrapping Map is more trouble than it's worth. Instead, I would consider a custom operator:
let (#) map key =
match map |> Map.tryFind key with
| Some value -> value
| None -> failwithf "key not found in map: %A" key
let m = Map.empty<int,string>
m # 2

Implementing a function for building a very simple tree (AST) using tokens/symbols in F#

I looked at how to make a tree from a given data with F# and https://citizen428.net/blog/learning-fsharp-binary-search-tree/
Basically what I am attempting to do is to implementing a function for building an extremely simple AST using discriminated unions (DU) to represent the tree.
I want to use tokens/symbols to build the tree. I think these could also be represented by DU. I am struggling to implement the insert function.
Let's just say we use the following to represent the tree. The basic idea is that for addition and subtraction of integers I'll only need binary tree. The Expression could either be an operator or a constant. This might be the wrong way of implementing the tree, but I'm not sure.
type Tree =
| Node of Tree * Expression * Tree
| Empty
and Expression =
| Operator //could be a token or another type
| Constant of int
And let's use the following for representing tokens. There's probably a smarter way of doing this. This is just an example.
type Token =
| Integer
| Add
| Subtract
How should I implement the insert function? I've written the function below and tried different ways of inserting elements.
let rec insert tree element =
match element, tree with
//use Empty to initalize
| x, Empty -> Node(Empty, x, Empty)
| x, Node(Empty,y,Empty) when (*x is something here*) -> Node((*something*))
| _, _ -> failwith "Missing case"
If you got any advice or maybe a link then I would appreciate it.
I think that thinking about the problem in terms of tree insertion is not very helpful, because what you really want to do is to parse a sequence of tokens. So, a plain tree insertion is not very useful. You instead need to construct the tree (expression) in a more specific way.
For example, say I have:
let input = [Integer 1; Add; Integer 2; Subtract; Integer 1;]
Say I want to parse this sequence of tokens to get a representation of 1 + (2 - 1) (which has parentheses in the wrong way, but it makes it easier to explain the idea).
My approach would be to define a recursive Expression type rather than using a general tree:
type Token =
| Integer of int
| Add
| Subtract
type Operator =
| AddOp | SubtractOp
type Expression =
| Binary of Operator * Expression * Expression
| Constant of int
To parse a sequence of tokens, you can write something like:
let rec parse input =
match input with
| Integer i::Add::rest ->
Binary(AddOp, Constant i, parse rest)
| Integer i::Subtract::rest ->
Binary(SubtractOp, Constant i, parse rest)
| Integer i::[] ->
Constant i
| _ -> failwith "Unexpected token"
This looks for lists starting with Integer i; Add; ... or similar with subtract and constructs a tree recursively. Using the above input, you get:
> parse input;;
val it : Expression =
Binary (AddOp, Constant 1,
Binary (SubtractOp, Constant 2, Constant 1))

F# is it possible to "upcast" discriminated union value to a "superset" union?

Let's say there are two unions where one is a strict subset of another.
type Superset =
| A of int
| B of string
| C of decimal
type Subset =
| A of int
| B of string
Is it possible to automatically upcast a Subset value to Superset value without resorting to explicit pattern matching? Like this:
let x : Subset = A 1
let y : Superset = x // this won't compile :(
Also it's ideal if Subset type was altered so it's no longer a subset then compiler should complain:
type Subset =
| A of int
| B of string
| D of bool // - no longer a subset of Superset!
I believe it's not possible to do but still worth asking (at least to understand why it's impossible)
WHY I NEED IT
I use this style of set/subset typing extensively in my domain to restrict valid parameters in different states of entities / make invalid states non-representable and find the approach very beneficial, the only downside is very tedious upcasting between subsets.
Sorry, no
Sorry, but this is not possible. Take a look at https://fsharpforfunandprofit.com/posts/fsharp-decompiled/#unions — you'll see that F# compiles discriminated unions to .NET classes, each one separate from each other with no common ancestors (apart from Object, of course). The compiler makes no effort to try to identify subsets or supersets between different DUs. If it did work the way you suggested, it would be a breaking change, because the only way to do this would be to make the subset DU a base class, and the superset class its derived class with an extra property. And that would make the following code change behavior:
type PhoneNumber =
| Valid of string
| Invalid
type EmailAddress =
| Valid of string
| ValidButOutdated of string
| Invalid
let identifyContactInfo (info : obj) =
// This came from external code we don't control, but it should be contact info
match (unbox obj) with
| :? PhoneNumber as phone -> // Do something
| :? EmailAddress as email -> // Do something
Yes, this is bad code and should be written differently, but it illustrates the point. Under current compiler behavior, if identifyContactInfo gets passed a EmailAddress object, the :? PhoneNumber test will fail and so it will enter the second branch of the match, and treat that object (correctly) as an email address. If the compiler were to guess supersets/subsets based on DU names as you're suggesting here, then PhoneNumber would be considered a subset of EmailAddress and so would become its base class. And then when this function received an EmailAddress object, the :? PhoneNumber test would succeed (because an instance of a derived class can always be cast to the type of its base class). And then the code would enter the first branch of the match expression, and your code might then try to send a text message to an email address.
But wait...
What you're trying to do might be achievable by pulling out the subsets into their own DU category:
type AorB =
| A of int
| B of string
type ABC =
| AorB of AorB
| C of decimal
type ABD =
| AorB of AorB
| D of bool
Then your match expressions for an ABC might look like:
match foo with
| AorB (A num) -> printfn "%d" num
| AorB (B s) -> printfn "%s" s
| C num -> printfn "%M" num
And if you need to pass data between an ABC and an ABD:
let (bar : ABD option) =
match foo with
| AorB data -> Some (AorB data)
| C _ -> None
That's not a huge savings if your subset has only two common cases. But if your subset is a dozen cases or so, being able to pass those dozen around as a unit makes this design attractive.

Check several option types and then convert to type

I am a new programmer in general, and as well to F#. I've ran into this particular problem several times, and have yet to solve it efficiently in my opinion. Here is the problem:
I have these example types:
type Retail1 = | Fashion | Auto | Sports
type Wholesale1 = | Fashion | Auto | Sports
type Events1 = | Wedding | Birthday
type Product =
| Retail of Retail1 | Wholesale of Wholesale1 | Events of Events1
| NoProduct
I want to convert the possibility of the first three types to the Product type via a function:
let convertToProduct (retail: Retail1 option)
(wholesale: Wholesale1 option) (events: Events1 option) =
// convert to Product here
if retail.IsSome then Retail retail
elif wholesale.IsSome then Wholsale wholseale
elif events.IsSome then Events events
else NoProduct
The way that I have handled it in the pass is just to chain a long if elif statement together to check for each condition and return the final type of Product, but this does not feel correct, or at the very least idiomatic to F#. What would be the recommended approach to this problem?
How about something like this:
let convertToProduct (retail: Retail1 option) (wholesale: Wholesale1 option) (events: Events1 option) =
match (retail, wholesale, events) with
|Some rt, None, None -> Retail rt
|None, Some wh, None -> Wholesale wh
|None, None, Some ev -> Events ev
|_ -> NoProduct
This exploits the fact that if you convert all the arguments into a tuple, you can do pretty concise pattern matching on the result.
Pattern matching is actually extremely powerful, you can find more details about the types of pattern matching you can perform in the MSDN documentation.

F# check if variable is of a subtype

To check if a variable a has type T, I can use
if (a :? T)
But what if T is a universally-defined and has several subtypes T1,T2,T3 in which I'm only interested in checking if a has type T1? For example:
type Uni = Iu of int
| Flu of float
| Su of string
| Bu of bool
| Lu of Uni List
| Fu of (Uni -> Uni)
How can I check if a variable aString has type Su?
Help is appreciated.
You can use pattern matching with the underscore (wildcard) pattern to ignore the float value:
let isSu = function Su _ -> true | _ -> false
Although you can think of discriminated unions like Uni as class hierarchies with some base class and a number of subtypes, I do not think this is very helpful in this case. It is much better to think of them as types which can have one of several possible representations. So instead of "checking for a subtype" you are really just checking if a value is represented using the Su case.

Resources