F # is a generic type with static elements. Is it possible?

I have the following situation:

1. There are many (20 - 40) very similar structurally slowly changing data types.

2. Each data type at a low level is represented by a unique (for type) string label and a unique (again, for type) key (usually string or long). The key does not change, but the label (on average) can be updated less than once a year.

3. I “promoted” each such data type to F # DU, so for each label / key there is a DU case. This is necessary because some of the higher-level code must perform pattern matching. Thus, in the rare case when types are updated, I want to get a compilation error if the situation changes. So, for each of this type, I generate / write code like:

type CollegeSize = 
    | VerySmall
    | Small
    | Medium
    | Large
    | VeryLarge
with
    static member all = 
        [|
            (CollegeSize.VerySmall, "Very small (less than 2,000 students)", 1L)
            (CollegeSize.Small, "Small (from 2,001 to 5,000 students)", 2L)
            (CollegeSize.Medium, "Medium (from 5,001 to 10,000 students)", 3L)
            (CollegeSize.Large, "Large (from 10,000 to 20,000 students)", 4L)
            (CollegeSize.VeryLarge, "Very large (over 20,000 students)", 5L)
        |]

4. : F #, , -. F # DU. - , , . , - , #.

5. , :

type CaseFactory<'C, 'L, 'K when 'C : comparison and 'L : comparison and 'K : comparison> = 
    {
        caseValue : 'C
        label : 'L
        key : 'K
    }

// DU factory: 'C - case, 'L - label, 'K - key
type UnionFactory< 'C, 'L, 'K when 'C : comparison and 'L : comparison and 'K : comparison> (all : array< 'C * 'L * 'K> ) =
    let map = all |> Array.map (fun (c, l, _) -> (c, l)) |> Map.ofArray
    let mapRev = all |> Array.map (fun (c, l, _) -> (l, c)) |> Map.ofArray
    let mapVal = all |> Array.map (fun (c, _, k) -> (c, k)) |> Map.ofArray
    let mapValRev = all |> Array.map (fun (c, _, k) -> (k, c)) |> Map.ofArray

    let allListValue : System.Collections.Generic.List<CaseFactory< 'C, 'L, 'K>> = 
        let x = 
            all 
            |> List.ofArray
            |> List.sortBy (fun (_, s, _) -> s)
            |> List.map (fun (c, s, v) -> { caseValue = c; label = s; key = v } : CaseFactory< 'C, 'L, 'K>)
        new System.Collections.Generic.List<CaseFactory< 'C, 'L, 'K>> (x)

    //Use key, NOT label to create.
    [<CompiledName("FromKey")>]
    member this.fromKey (k : 'K) : 'C = mapValRev.Item (k)

    // For integer keys you can pass "1" instead of 1
    [<CompiledName("FromKeyString")>]
    member this.fromKeyString (s : string) : 'C = this.fromKey (convert s)

     //Use key, NOT label to create.
    [<CompiledName("TryFromKey")>]
    member this.tryFromKey (k : 'K) : 'C option = mapValRev.TryFind k

    [<CompiledName("TryFromKeyString")>]
    member this.tryFromKeyString (s : string) : 'C option = mapValRev.TryFind (convert s)

     //Use key, NOT label to create.
    [<CompiledName("TryFromKey")>]
    member this.tryFromKey (k : 'K option) : 'C option = 
        match k with 
        | Some x -> this.tryFromKey x
        | None -> None

    [<CompiledName("AllList")>]
    member this.allList : System.Collections.Generic.List< CaseFactory< 'C, 'L, 'K>> = allListValue

    [<CompiledName("FromLabel")>]
    member this.fromLabel (l : 'L) : 'C = mapRev.[l]

    [<CompiledName("TryFromLabel")>]
    member this.tryFromLabel (l : 'L) : 'C option = mapRev.TryFind l

    [<CompiledName("TryFromLabel")>]
    member this.tryFromLabel (l : 'L option) : 'C option = 
        match l with 
        | Some x -> this.tryFromLabel x
        | None -> None

    [<CompiledName("GetLabel")>]
    member this.getLabel (c : 'C) : 'L = map.[c]

    [<CompiledName("GetKey")>]
    member this.getKey (c : 'C) : 'K = mapVal.[c]

6. , factory singleton , :

type CollegeSizeFactory private () =
    inherit UnionFactory<CollegeSize, string, int64> (CollegeSize.all)
    static let instance = CollegeSizeFactory ()

7. ... , - , factory. , :

type CollegeSizeFactory private () =
    inherit UnionFactory<CollegeSize, string, int64> (CollegeSize.all)
    static let instance = CollegeSizeFactory ()
    static member Instance = instance

    static member FromLabel s = CollegeSizeFactory.Instance.fromLabel s
    static member FromKey k = CollegeSizeFactory.Instance.fromKey k
    static member FromKeyString s = CollegeSizeFactory.Instance.fromKeyString s

, (, FromLabel s), factory .

, factory, , , :

type CollegeSizeFactory private () =
    inherit UnionFactory<CollegeSize, string, int64> (CollegeSize.all)

, , . , , , .

.

+4
1

, . - .

, , , ( , , ). , , , , .

type CollegeSizeFactory () =
    inherit UnionFactory<CollegeSize, string, int64> (CollegeSize.all)

module Factories = 
    let collegeSize = lazy CollegeSizeFactory()

type CollegeSize with
    static member Factory = Factories.collegeSize.Value

CollegeSize.Factory.tryFromKey 1

, , , , , , .

, , , , all , . , , , .

+3

Source: https://habr.com/ru/post/1690468/


All Articles