3

I am trying to build a generic function to manipulate a record my code looks like:

type Status = Active | Inactive

type IStatus =
    abstract member Status: Status

type User = 
    { 
        Username : string;
        Status : Status
    }
    interface IStatus with
        member x.Status = x.Status


let ChangeStatus<'T when 'T :> IStatus> newStatus (t:'T) =
    {t with Status = newStatus}

Now I get the following error:

expression was expected to have type
    'T    
but here has type
    User    

Obviously I just want to create a type constraint for Records which implement IStatus. Am I thinking too OO? Or is there merit to this approach and how do I create this ChangeStatus function?

Thank you for reading.

Mr. Baudin
  • 2,104
  • 2
  • 16
  • 24

1 Answers1

4

I don't think it's possible what you're trying to do, because it would need a "generic record cloner" I mean a generic record expression and that's not supported at the moment.

You can create a clone method for each subclass, that should work but you will have to repeat the code to clone the record. It might be a generic solution but involving reflection.

However if you change your design you can get the desired functionality. For instance you can use a generic nested record:

type Status = Active | Inactive

type StatusRecord<'T> =
    { 
        Item   : 'T
        Status : Status
    }

let changeStatus newStatus t = {t with Status = newStatus}

// TEST

type User  = {Username  : string}
type Group = {Groupname : string; members : User list}

let user  = {Status = Active; Item = {Username = "User1"}}
let group = {Status = Active; Item = {Groupname = "Group1"; members = []}}

This is a very lightweight solution, you will write less code but it will change your design which depending on the rest of your code will make sense or not.

Gus
  • 25,839
  • 2
  • 51
  • 76
  • Once you start nesting records and needing to update the interior, it can be worth looking at lenses. See: http://bugsquash.blogspot.co.uk/2011/11/lenses-in-f.html and http://stackoverflow.com/questions/8179485/updating-nested-immutable-data-structures – TheInnerLight Dec 21 '15 at 09:59
  • 1
    @TheInnerLight I agree. I didn't want to complicate the answer, however note that the lenses described in that post are not polymorphic. Actually there's a polymorphic lenses module in [FSharpPlus](https://github.com/gmpl/FSharpPlus), which may be worth using if the design justifies it. – Gus Dec 21 '15 at 10:33
  • Thank you for the answers. I will need a weekend with a dark room and a candle to wrap my head around the implications. One of which would of course be the "Cartesian Product" of all of the combinations of the "extra properties". – Mr. Baudin Dec 21 '15 at 12:05
  • @TheInnerLight if I understand it correctly lenses are used to create: "well-behaved bidirectional transformations". This is exactly what I am trying to do so I'll dive into that, thank you for the information! – Mr. Baudin Dec 21 '15 at 12:07