Alex_P
Alex_P

Reputation: 2952

How can I access a nested discriminated union value?

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()

Upvotes: 2

Views: 125

Answers (2)

TheQuickBrownFox
TheQuickBrownFox

Reputation: 10624

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.

Upvotes: 1

Abel
Abel

Reputation: 57149

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.

Upvotes: 1

Related Questions