Alex Netkachov
Alex Netkachov

Reputation: 13562

Generic method on record

I wonder if there is a better way of implementing a function that accepts records and modify them.

So I have entities of two types, both have corresponding files on the disk:

type Picture = { Artist : string; File : string }
type Book = { Author : string; File : string }

I want generic function that can copy both pictures and books. In the OOP world I would probably create common interface IArtefact { File : string }, implement it in both records and then create Move method that works on it. Something like:

let move<'a:IArtefact>(a : 'a) (path : string) =
    File.Move(a.File, path)
    { a with File = path }

However I suppose that F# does not support such concept. What is the F# way of doing so?

Upvotes: 2

Views: 447

Answers (2)

scrwtp
scrwtp

Reputation: 13577

This is possible, why wouldn't it be ;)

type IArtefact = 
    abstract File: string

type Picture = 
    { Artist : string; File : string }
    interface IArtefact with
        member this.File = this.File

let p = { Artist = "test"; File = "test2" }
(p :> IArtefact).File

Edit: If you want to handle updates:

type IArtefact = 
    abstract File: string
    abstract WithFile: string -> IArtefact

type Picture = 
    { Artist : string; File : string }
    interface IArtefact with
        member this.File = this.File
        member this.WithFile(file) = { this with File = file } :> IArtefact

Upvotes: 4

CaringDev
CaringDev

Reputation: 8551

While there is no generic way of change-copying records, there is one for moving anything that has a File:

let inline move from where : string =
    let oldFile = (^T : (member File : string) from)
    do() // move here
    where

type Picture = { Artist: string; File: string }
type Book = { Author: string; File: string }

let p = { Artist = "Vincent van Gogh"; File = "Rooftops" }
let p' = { p with File = move p "The Potato Eaters" }

let b = { Author = "Don Syme"; File = "Generics in CLR" }
let b' = { b with File = move b "Expert F#" }

This could then be expanded to move anything that knows how to be moved:

let inline move' a where =
    let oldFile = (^T : (member File : string) a)
    do() // move here
    (^T: (member moved : string -> ^T) a, where)

type Picture' =
    { Artist: string; File: string } with
    member this.moved where = { this with File = where }

type Book' =
    { Author: string; File: string } with
    member this.moved where = { this with File = where }

let p2 = { Artist = "Vincent van Gogh"; File = "Rooftops" }
let p2' = move' p2 "The Potato Eaters"

let b2 = { Author = "Don Syme"; File = "Generics in CLR" }
let b2' = move' b2 "Expert F#"

Upvotes: 2

Related Questions