netmatrix01
netmatrix01

Reputation: 640

F# alternative List Syntax

so i have got a type Genre with Property Name on it.

Im creating a list of Genre Names like below.

let genres = new Genre()
    [ genres.Name <- "Disco"; 
      genres.Name <- "Jazz"; 
      genres.Name <- "Rock"; ] |>ignore

Wondering if there is more succint way of creating this ?.

Upvotes: 0

Views: 257

Answers (4)

missingfaktor
missingfaktor

Reputation: 92026

Slightly more terser:

type Genre = Genre of string
let genres = List.map Genre ["Disco"; "Jazz"; "Rock"]

printfn "%A" genres

Prints [Genre "Disco"; Genre "Jazz"; Genre "Rock"].

Upvotes: 1

Tomas Petricek
Tomas Petricek

Reputation: 243041

The code in your example creates just a single Genre object and then creates a list of unit values. A unit value is a bit like void in C# - it is the result of perfroming an expression that does not return anything, but has a side-effect. In your case, the side-effect is modifying the Name property of the single instance (that's what the <- operator does), so you end with a single genres value whose Name is "Rock".

There are numerous ways to change the code to do what you want - to start with what you wrote:

let genre = new Genre() 
genre.Name <- "Disco"

This creates a single genre value, so you could create values genre1, genre2 and genre3 and then turn them into a list using:

let genres = [ genre1; genre2; genre3 ]

That would be quite a lot of repetition. If you have a default constructor for Genre that takes the name, you can just write Genre("Disco"). If you don't, you can use F# object initialization syntax and specify the value of the property during the construction as Genre(Name="Disco"). Note you can also omit new if the object does not implement IDisposable.

Now you can construct a list like this:

let genres = [ Genre(Name="Disco"); Genre(Name="Jazz"); Genre(Name="Rock") ]

Now, you can start using functional features like List.map (as suggested by Daniel) or F# list comprehension syntax to make the construction even shorter. In this case, I would probably prefer list comprehension and I'd write:

let genres = [ for name in ["Disco"; "Jazz"; "Rock"] -> Genre(Name = name) ]

This does the same thing as List.map, but using an F# syntax that has been designed for this purpose.

EDIT: Aside, using mutable properties in F# is not always the best way to go. You could try solving the same problem using F# records, which give you an easy way to create copies with modified properties. For example:

// A genre has a name and an era
type Genre = { Name : string; Era : string; }

// Create a template with the basic properties set
let template = { Name = "Default"; Era = "Twentieth century" }

// Create a list of 20th century genres
let genres = [ { template with Name = "Disco" }
               { template with Name = "Jazz" }
               { template with Name = "Rock" } ]

Unlike in the previous case, records are immutable and so you don't risk the confusion that is caused when you create a mutable object and then mutate it. Here, you get a list of three different objects (that are created by copying the template).

Upvotes: 8

Daniel
Daniel

Reputation: 47904

["Disco"; "Jazz"; "Rock"]
|> List.map (fun name -> Genre(name))

Upvotes: 4

John Palmer
John Palmer

Reputation: 25516

I think the simplest way would be to use a construcotr which did the assignment for you, then you could write

let genres = Genre("Disco")::Genre("Jazz")::Genre("Rock")::[]

Upvotes: 2

Related Questions