2010-11-16 88 views
3

想象一下这样的区分联合:如何使F#歧视联盟脱离其他类型的联盟情况?

type Direction = 
    | North 
    | South 
    | East 
    | West 

现在想象一下,我想这样一种类型,只接受(北,南)或(东,西)的元组。也许这将描述只能从北到南或从东到西的列车路线。 (北,东)和(南,西)应该被禁止,也许是因为列车不这样运行。

这不起作用:

type TrainLines = 
    | North, South 
    | East, West 

即使不工作,也许你可以看到我想要做的事。

这工作,但不限制possibilites只(北美,南美)和(东,西):

type TrainLines = Direction * Direction 

任何指导意见将受到欢迎。

回答

10

这不正是你问什么,但我认为这很可能是

type TrainLines = 
    | NorthSouth 
    | EastWest 

会对你有好处。如果需要,您可以添加例如

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

你不能做你想要什么,因为NorthSouthEastWest不是类型的自己。所以你不能有像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#的确实有一个很好的集在活动模式功能,它可以帮助运行时输入转换成一组已知的情况下,然后用静态的坚定信心:

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)] 
1

你真的想从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的缺点...