Теперь представьте, что мне нужен тип, который принимает только кортежи (Север, Юг) или (Восток, Запад).
Интересный запрос функции: звучит так, как вы хотите "статический диапазонограничения ", например
//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)]