如何使F#歧视联盟脱离其他类型的联盟情况?
问题描述:
想象一下这样的区分联合:如何使F#歧视联盟脱离其他类型的联盟情况?
type Direction =
| North
| South
| East
| West
现在想象一下,我想这样一种类型,只接受(北,南)或(东,西)的元组。也许这将描述只能从北到南或从东到西的列车路线。 (北,东)和(南,西)应该被禁止,也许是因为列车不这样运行。
这不起作用:
type TrainLines =
| North, South
| East, West
即使不工作,也许你可以看到我想要做的事。
这工作,但不限制possibilites只(北美,南美)和(东,西):
type TrainLines = Direction * Direction
任何指导意见将受到欢迎。
答
这不正是你问什么,但我认为这很可能是
type TrainLines =
| NorthSouth
| EastWest
会对你有好处。如果需要,您可以添加例如
with member this.Directions =
match this with
| NorthSouth -> [North; South]
| EastWest -> [East; West]
答
你不能做你想要什么,因为North
,South
,East
和West
不是类型的自己。所以你不能有像North * South
; North, South
是类型Direction * Direction
的值,但不是唯一的值。就像你不能定义类型
type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
答
现在想象一下,我想这样一种类型, 只接受(北,南) 或(东,西)的元组。
有趣的功能请求:听起来像你想要的“静态范围约束”,例如,
//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)]
答
你真的想从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
可悲的是,这会带来所有OOP的缺点...