Как сделать F # дискриминируемое объединение из случаев объединения другого типа? - PullRequest
3 голосов
/ 16 ноября 2010

Представьте себе этот различимый союз:

type Direction = 
    | North
    | South
    | East
    | West

Теперь представьте, что мне нужен тип, который принимает только кортежи (Север, Юг) или (Восток, Запад).Возможно, это опишет маршруты поездов, которые идут только с севера на юг или с востока на запад.(Север, Восток) и (Юг, Запад) должны быть запрещены, возможно, потому, что поезда не ходят так.

Это не работает:

type TrainLines = 
    | North, South
    | East, West

Даже если этоне работает, возможно, вы видите, что я пытаюсь сделать.

Это работает, но не ограничивает возможности только (Север, Юг) и (Восток, Запад):

type TrainLines = Direction * Direction

Любое руководство будет приветствоваться.

Ответы [ 4 ]

10 голосов
/ 16 ноября 2010

Это не совсем то, что вы просили, но я думаю, что

type TrainLines =
    | NorthSouth
    | EastWest

пойдет тебе на пользу. При необходимости вы можете добавить, например,

    with member this.Directions =
           match this with
           | NorthSouth -> [North; South]
           | EastWest -> [East; West]
2 голосов
/ 17 ноября 2010

Теперь представьте, что мне нужен тип, который принимает только кортежи (Север, Юг) или (Восток, Запад).

Интересный запрос функции: звучит так, как вы хотите "статический диапазонограничения ", например

//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 # имеет хороший набор функций в активных шаблонах, которые могут помочь преобразовать входные данные во время выполнения в набор известных случаев, а затем продолжить со статической достоверностью:

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 голосов
/ 16 ноября 2010

Вы не можете делать то, что хотите, потому что North, South, East и West не являются их собственными типами. Таким образом, вы не можете иметь что-то вроде North * South; North, South - это значение типа Direction * Direction, но не единственное. Также как вы не можете определить тип

type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
1 голос
/ 17 ноября 2010

Вы действительно хотите полиморфные варианты от OCaml:

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

, но F # в настоящее время не может выразить это.Я действительно считаю, что мне это очень нужно в моей работе ...

Вы можете ввести ненужные слои типов объединения:

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

, а затем использовать ns * ew.

Другим решением, которое иногда может работать хорошо, является использование интерфейса для обеспечения согласованности между двумя отдельными типами объединения:

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

К сожалению, это налагает все недостатки ООП ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...