I don't think there's any way of directly accessing "subsets" of a type, and I don't think it would be wise for there to be. In this situation I would recommend

1
2
3
4
5
6
7
8
type data_mode = A | B

type user_only_mode = C | D

type user_mode = DataMode of data_mode | UserOnlyMode of user_only_mode

type policy = UserPolicy of user * user_mode | DataPolicy of data * data_mode

This explicitly declares the type structure, and remember this doesn't make pattern matching any more cumbersome due to nested matchings

1
2
3
4
5
6
7
8
9
10
 

match policy with

 UserPolicy (user, DataMode d) -> ...

| UserPolicy (user, UserOnlyMode d) -> ... 

| DataPolicy (data, DataMode d) -> ...

If this involves replicating code, you could always combine the first two (though then we would have potentially another matching)

1
2
3
4
5
6
7
8
 

match policy with

 UserPolicy (user, usm) -> let ... in (match usm with -> ...)

| DataPolicy (data, DataMode d) -> ...
By on 9/4/2006 9:16 AM ()

What you need is a way to express sub-typing between union data types. Unfortunately there is no way to do this in F#.

Trying to encode the sub-typing relationship with extra types can be troublesome because, as mdchurchill points out, it complicates pattern matching and at the end of the day data_mode values are not substitutable with user_mode values.

Therefore I would not bother trying to encode the sub-typing and just have simple distinct types for user_mode and data_mode and define functions to convert between them. If you are worried about name collision of the constructors you can put them in separate modules.

module UserMode = struct

type t = A | B | C | D

end

module DataMode = struct

type t = A | B

end

type policy = UserPolicy of user * UserMode.t | DataPolicy of data * DataMode.t

let data_to_user = function DataMode.A -> UserMode.A | DataMode.B -> UserMode.B

let user_to_data = function UserMode.A -> DataMode.A | UserMode.B -> DataMode.B | _ -> failwith "not a valid data mode"

Using a new experimental feature of F# called as active patterns it is possible to potentially improve pattern matching over this policy type by defining active patterns that match a UserMode as a DataMode and vice versa.

open Microsoft.FSharp.Experimental

let AsDataMode = { new IPattern<_,_> with get_Query () = function UserMode.A -> Some DataMode.A | UserMode.B -> Some DataMode.B | _ -> None }

let AsUserMode = { new IPattern<_,_> with get_Query () = function DataMode.A -> Some UserMode.A | DataMode.B -> Some UserMode.B }

let f p1 p2 =

match p1, p2 with

| UserPolicy (_, AsDataMode dm1), DataPolicy (_, dm2) -> dm1 = dm2

| UserPolicy (_, um1), DataPolicy (_, AsUserMode um2) -> um1 = um2

| _ -> false

Here the first clause of the match will fail if the user policy does not have a user mode that is compatible with a data mode (i.e., a UserMode.C or UserMode.D). Essentially AsUserMode/AsDataMode are up/down casts being performed in patterns themselves, hence the potential down-cast failure of AsDataMode is handled by the semantics of pattern matching and all mode values have the same type on the right-hand side of a clause (DataMode.t in first clause, UserMode.t in second clause).

By on 9/5/2006 11:50 AM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper