Is there a way to call the constructor for an F# record type in F#?
My motivation is I've been using the applicative validation from FSharpx but find myself writing lots of boilerplate functions that just construct records.
The buildAddress function for instance in simply boilerplate and I would love to get rid of it if possible.
let buildAddress streetNumber streetLine1 streetLine2 suburb state postcode =
{
Address.StreetNumber = streetNumber
StreetLine1 = streetLine1
StreetLine2 = streetLine2
Suburb = suburb
State = state
Postcode = postcode
}
let validateAddress streetNumber streetLine1 streetLine2 suburb state postcode =
buildAddress
<!> isNumber streetNumber
<*> Success streetLine1
<*> Success streetLine2
<*> Success suburb
<*> Success state
<*> validatePostcode postcode
F# records are actually generated with a constructor, but it doesn't seem to be available from F# code. Probably the CompilationMapping attribute handles that, since there aren't any attributes on the constructor itself.
What you can do, is get to the constructor using standard .NET reflection (Activator.CreateInstance) or the FSharp one (FSharpValue.MakeRecord, FSharpValue.PreComputeRecordConstructor will get you a function to use). All of them will want to get the arguments as an array of boxed objects however, so it won't mesh well with applicative validation.
I don't think you'll get a cleaner solution than what you have now.
Related
I have a record type which occurs quite often in a nested complex data structure. Because the record type has an automatically generated ToString the ToString of my bigger structure becomes way to confusing and I do not care for the string representation of my record.
So I want to have an empty string as representation for my record. Overriding ToString seems to not do anything, using StructuredFormatDisplay does not work with empty strings since it requires an input of the form "Text {Field} Text". Right now I have
[<StructuredFormatDisplay("{}")>]
type MyRecord
{ 5 fields... }
override __.ToString () = ""
But this results in The method MyRecord.ToString could not be found.
So what is the correct way to not have a string representation for a record type?
The comments all provide correct information about how to achieve your goal. Pulling it all together, here's what I would do in a real-world scenario where I wanted a record type to always have the empty string as its string representation:
open System
[<StructuredFormatDisplay("{StringDisplay}")>]
type MyRecord =
{
A: int
B: string
C: decimal
D: DateTime
E: Guid
}
member __.StringDisplay = String.Empty
override this.ToString () = this.StringDisplay
This way, regardless of what technique is used to print the record, or if its ToString method is used by an external caller, the representation will always be the same:
let record = {A = 3; B = "Test"; C = 5.6M; D = DateTime.Now; E = Guid.NewGuid()}
printfn "Structured Format Display: %A" record
printfn "Implicit ToString Call: %O" record
printfn "Explicit ToString Call: %s" <| record.ToString()
This prints:
Structured Format Display:
Implicit ToString Call:
Explicit ToString Call:
One thing to keep in mind is that this will even override the way the record is displayed by F# interactive. Meaning, the record evaluation itself now shows up as:
val record : MyRecord =
I am not a functional programmer.
I am learning F#.
I got a problem here.
Let me start from following piece of code:
type XmlNode(tagName, innerValue) =
member this.TagName = tagName
member this.InnerValue = innerValue
member this.Atts = Dictionary<string, obj>()
I don't use F# dict because (as I know) that one is readonly, however I obviously need to modify my attributes.
So I am really struggling to make it pure functional way:
type XmlNode with member this.WriteTo (output:StringBuilder) =
output.Append("<" + this.TagName) |> ignore
//let writeAtts =
// List.map2 (fun key value -> " " + key + "=" + value.ToString())
(List.ofSeq this.Atts.Keys) (List.ofSeq this.Atts.Values)
// |> List.reduce (fun acc str -> acc + " " + str)
//output.Append((writeAtts)) |> ignore
output.Append(">" + this.InnerValue + "</" + this.TagName + ">") |> ignore
output
The code I commented out was my (probably stupid) attemp to use mapping and reduction to concat all the atts in the single correctly formatted string. And that compiles OK.
But when I try to access my Atts property:
[<EntryPoint>]
let main argv =
let root = new XmlNode("root", "test")
root.Atts.Add("att", "val") // trying to add a new KVP
let output = new StringBuilder()
printfn "%O" (root.WriteTo(output))
Console.ReadLine()|>ignore
0 // return an integer exit code
...new attribute does not appear inside the Atts property, i.e. it remains empty.
So:
1) help me to make my code more functional.
2) and to understand how to deal with modificable dictionaries in F#.
Thank you.
First, your immediate problem: the way you defined the Atts property, it's not one value that is "stored" somewhere and is accessible via property. Instead, your definition means "every time somebody reads this property, create a new dictionary and return it". This is why your new attribute doesn't appear in the dictionary: it's a different dictionary every time you read root.Atts.
To create a property with a backing field and initial value, use member val:
type XmlNode(...) =
...
member val Atts = Dictionary<string,object>()
Now, answers to some implied questions.
First order of business: "modify the attributes" and "purely functional" are contradictory ideas. Functional programming implies immutable data. Nothing changes ever. The way to advance your computation is to create a new datum at every step, without overwriting the previous one. This basic idea turns out to be immensely valuable in practice: safer threading, trivial "undo" scenarios, trivial parallelization, trivial distribution to other machines, and even reduced memory consumption via persistent data structures.
Immutability is a very important point, and I urge you not to glance over it. Accepting it requires a mental shift. From my own (and other people I know) experience, it is very hard coming from imperative programming, but it is well worth it.
Second: do not use classes and properties. Technically speaking, object-oriented programming (in the sense of message passing) is not contradictory to functional, but the Enterprise flavor that is used in practice and implemented in C++, Java, C# et al., is contradictory, because it emphasizes this idea that "methods are operations that change an object's state", which is not functional (see above). So it's better to avoid object-oriented constructs, at least while you're learning. And especially since F# provides much better ways to encode data:
type XmlNode = { TagName: string; InnerValue: string; Atts: (string*string) list }
(notice how my Atts is not a dictionary; we'll come to this in a bit)
Similarly, to represent operations on your data, use functions, not methods:
let printNode (node: XmlNode) = (* we'll come to the implementation later *)
Third: why do you say that you "obviously" need to modify the attributes? The code you've shown does not call for this. For example, using my definition of XmlNode above, I can rewrite your code this way:
[<EntryPoint>]
let main argv =
let root = { TagName = "root"; InnerValue = "test"; Atts = ["att", "val"] }
printfn "%s" (printNode root)
...
But even if that was a real need, you shouldn't do it "in place". As I've described above while talking about immutability, you should not mutate the existing node, but rather create a new node that differs from the original one in whatever way you wanted to "modify":
let addAttr node name value = { node with Atts = (name, value) :: node.Atts }
In this implementation, I take a node and name/value of an attribute, and produce a new node whose Atts list consists of whatever was in the original node's Atts with the new attribute prepended.
The original Atts list stays intact, unmodified. But this does not mean twice the memory consumption: because we know that the original list never changes, we can reuse it: we create the new list by only allocating memory for the new item and including a reference to the old list as "other items". If the old list was subject to change, we couldn't do that, we would have to create a full copy (see "Defensive Copy"). This strategy is known as "Persistent Data Structure". It is one of the pillars of functional programming.
Finally, for string formatting, I recommend using sprintf instead of StringBuilder. It offers similar performance benefits, but in addition provides type safety. For example, code sprintf "%s" 5 will not compile, complaining that the format expects a string, but the final argument 5 is a number. With this, we can implement the printNode function:
let printNode (node: XmlNode) =
let atts = seq { for n, v in node.Atts -> sprintf " %s=\"%s\"" n v } |> String.concat ""
sprintf "<%s%s>%s</%s>" node.TagName atts node.InnerValue node.TagName
For reference, here's your complete program, rewritten in functional style:
type XmlNode = { TagName: string; InnerValue: string; Atts: (string*string) list }
let printNode (node: XmlNode) =
let atts = seq { for n, v in node.Atts -> sprintf " %s=\"%s\"" n v } |> String.concat ""
sprintf "<%s%s>%s</%s>" node.TagName atts node.InnerValue node.TagName
[<EntryPoint>]
let main argv =
let root = { TagName = "root"; InnerValue = "test"; Atts = ["att", "val"] }
printfn "%s" (printNode root)
Console.ReadLine() |> ignore
0
One of the benefits of the type system on F# is avoid a null exception... or that was something I believe... because I'm getting a null problem:
[<CLIMutable>]
type Customer = {
[<AutoIncrement>] id:option<int64>
code:string
name:string
}
I'm running a SQL code:
let SqlFTS<'T>(table:string, searchTable:string, query:string) =
use db = openDb()
let sql = sprintf "SELECT * FROM %s WHERE id in (SELECT docid FROM %s WHERE data MATCH %A)" table searchTable query
printfn "%A" sql
db.SqlList<'T>(sql) |> Seq.toArray
testCase "Customers" <|
fun _ ->
let rows = GenData.genCustomers(50)
Customers.insert(rows)
isEqual "Failed to insert" 50L (DB.SqlCount<Customers.Customer>())
//Until here, it works
let c = Customers.byId(1L)
printfn "%A" c
//Customers.byId return me a record with all the properties as NULLS!
//Then c.name is null, and the code above fail.
let rows = Customers.searchCustomers(c.name)
This was very unexpected. Why I can get a record with all values to null?
Let's start with line 1:
[<CLIMutable>]
The docs for CLIMutable state
Adding this attribute to a record type causes it to be compiled to a Common Language Infrastructure (CLI) representation with a default constructor with property getters and setters.
That default constructor means the fields will be initialized to default values. The default value for string is null. That's valid to the CLR and it's valid to C# and VB.NET. You may not be able to call the default ctor from F#, but pretty much anyone else can.
Welcome to interop; it can be a pain.
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.
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"].