Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copy and update record expression with generic Types

Tags:

f#

c#-to-f#

I have two types, and they share one named value.

type Type1 = 
    {
        p1: int;
        p2: int;
    } 
type Type2 = 
    {
        p1 : int;
        p3 : int;
    }

Is it possible to create a function that change only this named value (p1) and returns the new record?

I tried and got this far:

type IType = 
    abstract member p1: int;

type Type1 = 
    {
        p1: int;
        p2: int;
    } 
    interface IType with
        member this.p1 = this.p1

type Type2 = 
    {
        p1 : int;
        p3 : int;
    }
    interface IType with
        member this.p1 = this.p1

let changeP1ToTen (value: 'a when 'a :> IType) = 
    let newValue = {value with p1 = 10}
    newValue

let type1 = 
    {
        p1 = 50
        p2 = 80
    }
let newType1 = 
    changeP1ToTen(type1)

This doesn't work since the compiler assumes that {value with p1 = 10} is Type2 when it could be either.

If there is a better and clever solution it would help too.

I know this is possible if I use mutable for my types or use a class instead of a simple record but I was wondering if there was a better way to deal with it rather than the OO approach.

like image 435
Destino Avatar asked Nov 14 '17 16:11

Destino


1 Answers

I mean, you could try this...

type Type1 =
    {
        p1 : int;
        p2 : int;
    }
type Type2 =
    {
        p1 : int;
        p2 : int;
    }
type Wrapper =
    | TOne of Type1
    | TTwo of Type2

let changeP1ToTen = function
    | TOne of t1 -> { t1 with p1 = 10 }
    | TTwo of t2 -> { t2 with p1 = 10 }

let type1 = { p1 = 50; p2 = 80 }

// Requires extra wrapping step, is that a dealbreaker?
let newtype1 = TOne type1 |> changeP1ToTen

// If that's a problem for ya, here's a function to fix it
let iTypeToWrapper (value: 'a when 'a :> IType) =
    match value with
        | :? Type1 as t1 -> TOne t1
        | :? Type2 as t2 -> TTwo t2

let othernewtype1 = iTypeToWrapper type1 |> changeP1ToTen

The problem with the iTypeToWrapper function is that it will fail if you pass in something other than a Type1 or Type2. To fix this, you can use the following, if using optionals is acceptable for your use case.

// val iTypeToWrapper : ('a when 'a :> IType) -> Wrapper option
let iTypeToWrapper (value: 'a when 'a :> IType) =
    match value with
        | :? Type1 as t1 -> TOne t1 |> Some
        | :? Type2 as t2 -> TTwo t2 |> Some
        | _ -> None
like image 177
Devin Lynch Avatar answered Jan 03 '23 22:01

Devin Lynch