Reputation: 372
Suppose I have following types:
type AddressLow = {
FlatNo: int
PinCode: string
}
type AddressHigh = {
FlatNo: int
AreaName: string
PinCode: string
}
type PersonDataLow = {
id:int
name:string
address: AddressLow
}
type PersonDataHigh = { //same label names, different type for address
id:int
name:string
address: AddressHigh
}
Following two functions are to build the addresses:
let GetAddressLow () =
{AddressLow.FlatNo = 10; PinCode = "5245"}
let GetAddressHigh () =
{AddressHigh.FlatNo = 10; AreaName = "Bel Air"; PinCode = "8225"}
Following function is to build the PersonData:
let GetPerson fGetAddress inputId inputName = //return type inferred as PersonDataHigh
{
id = inputId
name = inputName
address = fGetAddress()
}
let p1 = GetPerson GetAddressLow 4 "John Smith" //compile error
let p2 = GetPerson GetAddressHigh 6 "Will Smith" //works
For above function, the return type is inferred as PersonDataHigh
by F#.
Hence, to return different types for PersonData(i.e. PersonDataHigh
and PersonDataLow
) I have to write two different functions.
Another approach is to use discriminated unions(DU) but that involves number of conversions back and forth between DU type and case-identifiers of the DU type.
Is it possible to use constraints for the return type so as to write the function just once? Say, something like this:
let inline GetPerson (fGetAddress) (inputId) (inputName)
: ^T when ^T: (member id: int) and ^T: (member name: string) and (^T: (member address: AddressLow) or ^T: (member address: AddressHigh)) = //compile error
{
id = inputId
name = inputName
address = fGetAddress()
}
If not, is using DU the best choice here? I am using F# 3.0.
Thanks.
Upvotes: 2
Views: 146
Reputation: 12184
Have you thought about nesting the low or highness within a single Address
type?
Since you most of the data is shared between the two types of Address
, I don't think making it a discriminated union or two different types is the most sensible option. Instead, just make one of its properties a disciminated union.
The simplest way to do this is make the AreaName
an option
.
type Address = {
FlatNo: int
AreaName : string option
PinCode: string
}
type PersonData = {
id:int
name:string
address: Address
}
Then you can go:
let GetAddressLow () =
{FlatNo = 10; AreaName = None; PinCode = "5245"}
let GetAddressHigh () =
{FlatNo = 10; AreaName = Some "Bel Air"; PinCode = "8225"}
You then don't need anything fancy to create your GetPerson
function.
Upvotes: 6