Discrimination Union Type Restriction

Consider that

type Foo =
  | I of int
  | S of string



let test = [ I(5); I(9); I(7)]

This works, but now I want "test" to be of type Foo and still be a list of either I or S. For example,

let test = L( [ I(5); I(9); I(42) ] ) //works!
let test2 = L( [ I(5); I(9); S("stuff") ] ) //type error
let test3 = L( [ I(5); I(9); L([]) ] ) //type error

I am trying this.

type Foo =
  | I of int
  | S of string
  | L of 'T list when 'T :> Foo

I know this clearly does not work. For me it was just something natural.

Thank you very much for your help!

+3
source share
4 answers

If I and S are really interesting on their own, i.e. If you want to know lists from S lists, why not be explicit?

type I = int
type S = string

type Foo =
    | I
    | S
    | LI of I list
    | LS of S list
+2
source

You cannot use a generic constraint for a "discriminatory association." But you can bind a specific type to this discriminatory compound, for example the following.

type Foo = 
    | I of int 
    | S of string
    | L of Foo list


// Usage
let test = L( [ I(5); I(9); S("stuff"); L([]); I(42) ] )

Does this answer your question?

+7

. , , ,

type FooAtom = 
    | I of int 
    | S of string

type Foo = 
    | A of FooAtom
    | L of FooAtom list

let test  = L( [ I(5); I(9); S("stuff"); I(42) ] )
let test2 = A( I(5) )
+5

You cannot specify a "list of some members of Foo and other (not Foo) things" in terms of the F # type system. But you can use runtime and another level of indirection:

type Foo = I of int | S of string

let (|F|L|) (o: obj) =
  match o with
  | :? Foo as v -> F v
  | :? list<Foo> as v -> L v
  | _ -> failwith "unknown type"
;;

let f = function
  | F _ -> "Foo"
  | L _ -> "List of Foos"
;;

List.map f [ box (I 1); box (S "stuff"); box ([I 2; S "foo"]) ]
+1
source

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


All Articles