netmatrix01
netmatrix01

Reputation: 640

F# properties design

So when ever im creating some properties in my F# code , as F# doesn't support auto properties , as far as i know. I have to create backing fields and initialize them to null, which doesn't seems right in functional programming terms. For e.g.


 let mutable albums : DbSet = null
 let mutable genres : DbSet = null

member x.Albums with get() = albums and set(value) = albums <- value member x.Genres with get() = genres and set (value) = genres <- value

Is there a better way of doing this ?. Many thanks for your suggestions.

Upvotes: 6

Views: 1220

Answers (3)

pad
pad

Reputation: 41290

Unless you're doing something complex, I would recommend to use records instead of classes. Basically, they are classes with extra features: immutability, structural equality, pattern matching, etc:

type Playlists = {
    Albums: DbSet;
    Genres: DbSet
    }

You can get record's fields easily:

let p = {Albums = ...; Genres = ...}
let albums = p.Albums
let genres = p.Genres

In default records fields are immutable; you can declare mutable fields in records, but it is considered as a bad practice. Though you cannot set properties, you can create a new record from an old one. Default immutability is normally not a problem, furthermore it makes the code more functional and easier to reason about:

   let p = {Albums = a; Genres = g}

   // Create new records by updating one field
   let p1 = {p with Albums = a1} 
   let p2 = {p with Genres = g2} 

If you insist to create classes, using a constructor with explicit parameters is recommended:

type Playlists(a: DbSet, g: DbSet) =
     let mutable albums = a
     let mutable genres = g
     // ...

When a default constructor is necessary, you can use Unchecked.default<'T> for non nullable fields, or better use their default constructors:

 // Set fields using dump values
 let mutable albums = new DbSet()
 let mutable genres = new DbSet()

But make sure that you set those fields before actually using them.

Upvotes: 5

Daniel
Daniel

Reputation: 47904

FYI - auto-properties are planned for F# 3.0. See the preview documentation [MSDN]. Looks like your example would become:

type Music() =
  member val Albums : DbSet = null with get, set
  member val Genres : DbSet = null with get, set

Upvotes: 5

Tomas Petricek
Tomas Petricek

Reputation: 243051

F# does not support auto properties when you need a mutable property, but it supports a lightweight syntax when you need just a readonly property. If you're writing some functional code, then using readonly properties might actually be more appropriate:

type Music(genres : DbSet, albums : DbSet) = 
  member x.Albums = albums
  member x.Genres = genres

This is essentially the same as records suggested by pad, but it may be more appropriate if you want to have better control over how the types look (and how they appear in C#, or for data-binding).

If DbSet is a mutable type, then you probably can just use the above type and initialize it just once (you'll still be able to modify the DbSet values). If you want to change the DbSet value, you can add a method that returns a cloned object:

  member x.WithAlbums(newAlbums) = 
    Music(genres, newAlbums)

Using null or Unchecked.defaultOf<_> in F# is considered a very bad practice and you should always try to create fully initlized object. If the value may be missing, you can use option type to represent that, but then you have to always write handler for missing value, to make your program safe.

Upvotes: 11

Related Questions