Search code examples
f#clone

DRY self-replicating type


Is there a succint way to express self-replicating types in F#? — That is, without repeating oneself.

// Manual self-replication

type Foo (par1 : Type1, par2 : Type2, par3 : Type3, par4 : Type4) =

    let unique = new UniqueState() // unique for every instance of Foo

    member this.SelfReplicate =
        new Foo(par1, par2, par3, par4) // repeating myself

let a = new Foo(x, y, z, w)
let b = a.SelfReplicate

Attempt with manually injected self-replicator:

// Semi-automagic self-replication

type Foo' (par1 : Type1, par2 : Type2, par3 : Type3, par4 : Type4, replicate : unit -> Foo') =

    let unique = new UniqueState() // unique for every instance of Foo'

    member this.SelfReplicate = replicate() // not repeating myself

let rec foo' () = new Foo'(x, y, z, w, foo')
let a = foo'()
let b = a.SelfReplicate

I'm not sure how this can be any more succint without compiler magic. It just seems like there should be a way to capture the current arguments and type without repeating them syntactically.


Solution

  • You could define a type WithUnique<'T> which is a wrapper over a value of type 'T and adds a unique value to this. You may need to think about how you want the equality testing on those types to work - if you use record (as I do below), then two instances with different unique value will not be equal:

    let rnd = System.Random()
    let uniqueState() = rnd.Next()
    
    type WithUnique<'T> = 
      { Value : 'T; Unique : int }
      static member Create(v) : WithUnique<'T> = 
        { Value = v; Unique = uniqueState() }
      member x.Replicate() = 
        { Value = x.Value; Unique = uniqueState() }
    

    The value of 'T is just one type, but this can be a tuple (or a record) if you need to wrap multiple things:

    let wu1 = WithUnique.Create( (10, "hi") )
    let wu2 = wu1.Replicate()
    

    Given the above, wu1=wu2 will be false.