F# | Generics with 'or' Constraints on Lists

Viewed 54

I have the following two types:

    type MedicalConditionStore = {
        MedicalCondition: MedicalCondition
        LastMod: UTCTick
    }

    type RejectedMedicalConditionStore = {
        RejectedMedicalCondition: MedicalCondition
        LastMod: UTCTick
    }

And I would like to create the following method:

let accumulateConditions medicalConditionsList = 
   ...

Where medicalConditionsList could be either MedicalConditionStore list or a RejectedMedicalConditionStore list

Intuitively, I was thinking to combine Generics with Constraints, but I cannot manage to find the proper syntax to do that. I have tried something like this, but it doesn't work:

let accumulateConditions (medicalConditionsList: ^a list when ^a :(member F: MedicalCondition)) = 
    ...

Or like this:

let accumulateConditions (medicalConditionsList: RejectedMedicalConditionStore list | MedicalConditionStore list) = 
    ...

Anyone has a clue on how to approach this, without creating another discriminated union type over both of them - a situation in which I always need to wrap and unwrap the members from their general type to the specific one and vice versa using pattern matching?

Thank you!

1 Answers

You would be able to achieve this using static member constraints, but I think that is a wrong approach - it will result in fragile and complex code.

As mentioned in the comments, I think it would make much more sense to reconsider your domain. The best design really depends on all the different things that can be done with medical conditions. You could mark conditions with their state:

type MedicalConditionState = Rejected | Accepted 

type MedicalConditionStore = {
    MedicalCondition: MedicalCondition
    LastMod: UTCTick
    State: MedicalConditionState }

Or you could keep two separate lists at a higher level:

type MedicalConditionStore = {
    MedicalCondition: MedicalCondition
    LastMod: UTCTick }

type MedicalConditions = {
    Accepted: MedicalConditionStore list
    Rejected: MedicalConditionStore list }

Alternatively, as you seem to want to access just the underlying MedicalCondition, you could also use good-old-fashioned-interface type, which is a perfectly reasonable thing to do in F# (although in this case, I think revisiting your domain model would solve the problem more elegantly):

type IMedicalCondition = 
    abstract MedicalCondition: MedicalCondition

type MedicalConditionStore = 
    { MedicalCondition: MedicalCondition
      LastMod: UTCTick }
    interface IMedicalCondition with
      member x.MedicalCondition = x.MedicalCondition

type RejectedMedicalConditionStore = 
    { RejectedMedicalCondition: MedicalCondition
      LastMod: UTCTick }
    interface IMedicalCondition with
      member x.MedicalCondition = x.RejectedMedicalCondition

let accumulateConditions 
  (medicalConditionsList:seq<#IMedicalCondition>) = 
    // (...)
Related