2016-01-27 43 views
1

我想知道是否有更好的方法來實現接受記錄並修改它們的函數。記錄中的通用方法

所以我有兩種類型的實體,都具有磁盤上相應的文件:

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

我想一般的功能,可以同時複製的圖片和書籍。在OOP世界中,我可能會創建通用接口IArtefact { File : string },在兩個記錄中實現它,然後創建適用於它的Move方法。例如:

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

但是我想F#不支持這樣的概念。 F#的做法是什麼?

+0

我不認爲在F#中不支持任何重要的C#功能。正如泛型一樣支持接口。在F#中解決問題的好方法不在StackOverflow的範圍之內 - 也許你會在CodeReview或程序員中獲得更多的幫助。 – Luaan

+0

據我所知,沒有辦法定義實現一個接口的記錄,然後在F#中將該記錄投射到該接口。這是相當重要的「C#特性」。不過,我可能錯了,或者有其他方法存在。因此,在SO,IMHO上提出這樣的問題是完全正確的。這不是一個代碼審查,也不是一般的編程問題。 –

+0

爲什麼不定義如下的函數? '讓移動(文件:字符串)(路徑:字符串)'?然後你可以用'aPicture.File'和'aBook.File'來調用它。 –

回答

2

雖然有變化,複製記錄中沒有任何通用的方法,有一個移動任何有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#" } 

然後,這可以擴大移動任何知道如何移動的東西:

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#" 
+0

這看起來可以接受,非常感謝。我認爲我會這樣做。 –

+1

類似'{p with File,Metadata = f()}'其中'f:單元 - >字符串*元語言中的元數據也會相當不錯:) –

+0

就像觀察一樣,這段代碼混合了一些隱喻:它使用不可變對象(帶有File成員的東西)來表示可變狀態(磁盤上文件的名稱)。當代碼完成你所要求的內容時,你可能最終會得到多線程/異步代碼中的兩個圖片對象:一個在移動之前具有文件名(現在無效),另一個在移動之後。考慮使用具有可防止同時移動的可變成員的類,或者執行副本而不是移動,將對象IDisposable刪除,並使調用者負責使其成爲事務性(yuck)。 –

4

這是可能的,爲什麼不會是;)

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 

編輯:如果你想處理更新:

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 
+0

目前還不清楚如何定義方法,返回與更新的文件字段的新的artefact ... '讓m <'a當'a:> IArtefact>(p:'a)= {p with File =「新的「} ;;'看起來很接近但不能編譯... –

+1

當你所知道的是接口實現時,你不能創建任意對象的」副本「。界面背後的實際對象可以是任何東西,你不可能知道如何構建另一種相同的類型。 –

+0

我明白,不要尋找這樣的解決方案。我感興趣的是F#是否有辦法用它的記錄來做到這一點,沒有什麼比這更重要的了。理想情況下,我會很樂意在'a:record>(q:'a):'a = {q}'時寫'let m <'a就是這樣。 –