相互に再帰的な(相互に参照できるという意味)という2つの型を定義する必要がある場合は、それらを単一のファイルに配置し、 type ... ...
の構文を使用します。
あなたの例では、これは Genre
と Album
を次のように定義する必要があることを意味します。
// Start a definition block using 'type' as normal
type Genre() =
let mutable name = String.Empty
let mutable albums = [new Album()]
member x.Name
with get() = name
and set (value) = name <- value
member x.Albums
with get() = albums
and set ( value ) = albums <- value
// Continue single type definition block using 'and'
and Album() =
let mutable genre = new Genre()
let mutable albumId = 0
let mutable artist = new Artist()
member x.Genre
with get() = genre
and set (value) = genre <- value
member x.AlbumId
with get() = albumId
and set ( value ) = albumId <- value
member x.Artist
with get() = artist
and set ( value ) = artist <- value
しかし、あなたの例では非常にC#スタイルでF#を使用しているので、コードは実際には非常にエレガントではなく、関数型プログラミングの利点の多くを与えるわけではありません。
あなたが使っている構造を表現したいのであれば、そのジャンルへの参照を Album
型に追加しないでしょう。 Genre
の中にアルバムのリストを置くと、データ構造を処理するときに(つまり、他の構造、おそらくF#レコードに変換することができます。データバインディングに渡す必要があります)。 F#の恩恵は、ドメインを数行書くことができるということですが、それは機能型に対してのみ機能します。
区別された共用体を単一のケースで使用すると、次のように書くことができます。
// Type aliases to make code more readable
type Name = string
type AlbumID = int
// Simple type definitions to represent the domain
type Artist = Artist of Name
type Album = Album of AlbumID * Artist
type Genre = Genre of Name * Album list