How to make discriminatory union F # from other cases of union of types?

Imagine this discriminated union:

type Direction = 
    | North
    | South
    | East
    | West

Now imagine that I need a type that accepts only tuples (North, South) or (East, West). Perhaps this will describe the routes of trains that run only from north to south or from east to west. (North, East) and (South, West) should be banned, perhaps because trains do not work this way.

This does not work:

type TrainLines = 
    | North, South
    | East, West

Even if this does not work, perhaps you can see what I'm trying to do.

This works, but does not limit the possibilities only (North, South) and (East, West):

type TrainLines = Direction * Direction

Any recommendations would be appreciated.

+3
source share
4 answers

, , , , ,

type TrainLines =
    | NorthSouth
    | EastWest

. , ,

    with member this.Directions =
           match this with
           | NorthSouth -> [North; South]
           | EastWest -> [East; West]
+10

, , North, South, East West . , - North * South; North, South - Direction * Direction, . ,

type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
+2

, , (, ) (, ).

: , " ",

//fictional syntax for static range constraints
type TrainLine = (a,b) where (a=North and b=South) or (a=East and b=West)

let northSouth = TrainLine(North,South) // compiles
let northEast = TrainLine(North,East) // does not compile

, , , :

let parseDirection userInputString = 
    match userInputString with
    | "North" -> North
    | "South" -> South
    | "East" -> East
    | "West" -> West
    | _ -> failwith "invalid direction"

let directionA = parseDirection(System.Console.ReadLine())
let directionB = parseDirection(System.Console.ReadLine())

//compiler can't enforce constraint because direction values unknown until runtime
let trainLine = TrainLine(directionA,directionB) 

F # Active Patterns, , :

let (|NorthSouth|EastWest|Invalid|) (a,b) = 
    match a,b with
    | North,South -> NorthSouth
    | East,West -> EastWest
    | _ -> Invalid

let trainLines = [(North,South); (South,North); (East,West); (North,East);(North,North); (South,East)]

let isValidTrainLine trainLine = 
    match trainLine with
    | NorthSouth -> true
    | EastWest -> true
    | Invalid -> false

let validTrainLines = trainLines |> List.filter isValidTrainLine
//val it : (Direction * Direction) list = [(North, South); (East, West)]
+2
source

You really need the polymorphic options from OCaml:

[ `North | `South | `East | `West ]
[ `North | `South ] * [ `East | `West ]

but F # is currently unable to express it. I really find that I really need it in my work ...

You can enter unnecessary layers of connection types:

type ns = North | South
type ew = East | West
type nsew = NorthSouth of ns | EastWest of ew

and then use ns * ew.

Another solution that can sometimes work well is to use an interface to ensure consistency between two separate types of connections:

type IDir = abstract AsInt : int
type ns =
  | North
  | South
  interface IDir with
    method d.AsInt =
      match d with North -> 0 | South -> 1
type ew =
  | East
  | West
  interface IDir with
    method d.AsInt =
      match d with East -> 2 | West -> 3

Unfortunately, this imposes all the disadvantages of OOP ...

+1
source

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


All Articles