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 ?.
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).
["Disco"; "Jazz"; "Rock"]
|> List.map (fun name -> Genre(name))
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")::[]
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"].
Related
Please, help an F# beginner understand this! I have the following F# code (which I've just created to help me learn, so I realise it's probably terrible):
type Account = {
ID: Guid
Name: string
};
module Accounts =
let mutable accounts:Account list = []
// Why does this always return empty?
let lst =
accounts
let load id =
accounts |> List.find(fun a-> a.ID = id)
let save account =
accounts <- account::accounts
Accounts.save { ID = Guid.NewGuid(); Name = "Account One"}
let id = Guid.NewGuid()
Accounts.save { ID = id; Name = "Account Two"}
let account = Accounts.load id
If I write out the value of account at this stage (after the above code), I see the Account record I expect (Account Two), and if I dump Accounts.accounts, I can see that the mutable list contains both Account records.
So why does Account.lst always return an empty list, even though I can see that the list definitely isn't empty?
Because lst is a value, not a function.
In F#, the technical difference between a value and a function is that a function has parameters, and a value does not.
When you declared lst, you didn't give it any parameters, so the compiler understood it as "I want to take the value of accounts at this particular point in time, and refer to that value as lst from now on". So every time you write lst, you're not calling a function, but merely referring to that same empty list that was the value of accounts at initialization time.
To make lst a true function, you need to give it a parameter. And because it doesn't really need any actual data, the natural parameter to give it is the unit value - ().
let lst () = accounts
P.S. This whole trickiness ultimately comes from mutating state. Try to avoid it when possible.
Because lst is a value not a function. You need to convert it to a function:
let lst () =
accounts
then you can call it by:
Accounts.lst()
I am new to F# and having trouble translating some C# code.
I have a class similar to this:
type car () =
member val Model = "" with get,set
member val Year = "" with get,set
I have this query expression that pulls car data from the database:
query{
for row in db do
select // <-- what is the correct syntax to create a sequence of new car
// classes here
}
it's a lot easier if you don't translate 1:1 or at least if you add an constructor.
For example using a primary-constructor this should work:
type Car (model, year) =
member __.Model with get() = model
member __.Year with get() = year
query {
for row in db do
select (Car (row.Model, row.Year))
}
now of course I don't know how the rows in your db looks like and this will give you immutable data - but for what I see it should be fine
I just realised that this might be a problem (just as in C#) as the ctor probably cannot be translated into a SQL-statement - you can still try but I guess you really need to do
query {
for row in db do
select
} |> Seq.map (fun row -> Car (row.Model, row.Year))
instead (sorry - cannot really try right now)
I am trying to create a record which uses one of the previously defined fields to calculate the value of another, within the same constructor. e.g.
myRecordType = {Foo:int; Bar:int[]}
myRecord = {Foo = 5;
Bar = Array.init Foo (fun i -> i)}
When I try this it doesn't recognise Foo as already existing. I also can't reference Foo using myRecord.Foo, but that makes sense since myRecord hasn't been constructed yet. However I would've thought that Foo and Bar would be in the same scope, so Bar could access Foo, but apparently not.
The size of the Bar array depends on the value of Foo, so how is it possible to set up a record of this format?
You cannot reference other fields, you shouldn't even take for granted the order of fields evaluation.
You can do it by additional let binding:
let myrecord =
let calcedFoo = 5
{ Foo = calcedFoo; Bar = Array.init calcedFoo id }
You can think of record construction expression as a function where right sides of assignments are parameters passed to that function.
I would go with what #Bartek suggests and use local let binding to store all pre-computed values that you need to have before you can create the record.
That said (depending on your domain), you might also be able to change the structure of your record, so that you have one record with all the primary values and another record that contains the primary one and adds all secondary values:
type MyRecordPrimary = { Foo:int }
type MyRecord = { Primary:MyRecordPrimary; Bar:int[] }
let myPrim = { Foo = 5 }
let myRec = { Primary = myPrim; Bar = Array.init myPrim.Foo (fun i -> i)}
Doing this just to make the construction a bit shorter would be silly, but if the primary record actually represents something in your domain, then this might be a good alternative.
This question is in follow up to an earlier question, Preserving Field names across the F#/C# boundary
Because of the current limitation encountered with F# type providers (see the earlier question), I want to map the type-provider-generated list to my own list of records, in which the record is, in part,
type inspection = {
inspectionID : string;
inspectorID : int;
EstablishmentID : string;
EstablishmentName : string; // other members elided
}
I think the way to do this will use Seq.map, but I am not certain. (Recall I am doing a learning exercise.) So here is what I tried:
type restaurantCsv = CsvProvider<"C:\somepath\RestaurantRatings2013.csv",HasHeaders=true>
// which generates a type, but it is an "erased" type, so member names do not propogate
// over to C#.
type RawInspectionData(filename : string) =
member this.allData = restaurantCsv.Load(filename) // works fine
member this.allInspections =
this.allData.Data
|> Seq.map(fun rcrd -> new inspection[{inspectionID = rcrd.InspectionID;}])
and, of course, the complete statement would have the other member names as part of the inspection, here elided for brevity. Someone pointed me to p 43 of F# For Scientists, which is why I thought to use this format with the curly braces. But this yields a syntax error, "Unexpected symbol '{' in expression. Expected ',', ']' or other token."
Hopefully, though, this snippet is adequate to show what I would like to do, create a Generated Type from the Erased Type. How can I accomplish this?
Your code is going in the right direction. When using Seq.map (which is like Select in LINQ), you need to turn a single element of the original sequence into a single element of the new sequence. So the lambda function just needs to create a single instance of the record.
A record is constructed using { Field1 = value1; Field2 = value2; ... } so you need:
type RawInspectionData(filename : string) =
let allData = restaurantCsv.Load(filename) // works fine
member this.allInspections =
allData.Data
|> Seq.map(fun rcrd -> {inspectionID = rcrd.InspectionID})
I also changed allData from a member to a local let definition (which makes it private field of the class). I suppose that your original code new inspection[{...}] tried to create a singleton array with the element - to create an array you'd write [| { Field = value; ... } |] (and the compiler would infer the type of the array for you). But in this case, no arrays are needed.
I am writing a function to return a list minus the third value.
Here is my current code:
let listString = [ "1"; "2"; "3"; "4" ];;
let del3 (listA :'a) = [listA.Head; listA.Tail.Head] # [listA.Tail.Tail.Tail];;
del3 listString
and I am getting the error:
Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved.
What should I change to fix the error?
I think a simpler approach based on a pattern match might be better
let del3 = function |a::b::c::d -> a::b::d | _ -> failwith "insufficient input"
You need to let the compiler know that listA is a list. Also Tail returns a list, so for the second list you're appending you don't want to wrap the tail in a list, otherwise you're going to have a list of a list:
let listString = [ "1"; "2"; "3"; "4" ]
let del3 (listA :'a list) = [listA.Head; listA.Tail.Head] # listA.Tail.Tail.Tail
del3 listString;;
A solution to handle lists of all sizes:
let del3 = function
| a::b::c::tail -> a::b::tail
| list -> list
When accessing members, methods or properties of an object, F# needs to know the type of that object. It can't just infer the type from the fact that you're accessing a property named Head because there might be many different classes that have such a property.
To fix this problem, either give listA a type annotation or use List.head and List.tail instead of the properties.