Reputation: 2807
I'm writing a library where there will be some functions that take many (more than 50) parameters (not my design but I need to model it).
I want to minimise the pain for the caller calling these functions, taking a simple example:
type Person =
{ name : string option
address : string option
}
how can I create these records with the least pain.
Attempt 1 (consciously bad)
module Person =
let make : string option -> string option -> Person =
fun name address ->
{ name = name
address = address
}
let p1 = Person.make (Some "Jim") None
this is how I would often write such code, but obviously the issue here is with many many parameters the client call to create a person would have lots of "Some" and "None", painful
Attempt 2 (from microsoft documention)
type Person =
{ name : string option
address : string option
}
static member make (?name : string,?address : string) =
{ name = name
address = address
}
let p1 = Person.make(name = "bill bloggs")
This is better, but I've suddenly had to flip my style to use explicit static members, I don't have a massive problem with this, but I wouldn't want to mix the previous style with this style based on how I feel. I could write all my code like this, but if feels unidiomatic of F#.
Attempt 3 (can't remember where I saw this)
type Person2 =
{ name : string option
address : string option
}
module Person2 =
let make (setter : Person2 -> Person2) =
setter { name = None; address = None }
let p2 = Person2.make (fun p -> { p with name = Some "bill bloggs" })
This uses the record update syntax to optionally 'update' parameters with default values. It preserves the idiomatic style of attempt 1, but really is more 'noisy' than attempt 2.
Question:
which of these is idiomatic? (or is another style idiomatic not listed)?
Upvotes: 1
Views: 68
Reputation: 61903
Other ideas (used in github.com/jet/* quite a bit - https://github.com/jet/FsKafka/blob/master/src/FsKafka/FsKafka.fs is a decent example, but I applied that lots of other places in having APIs with lots of options without reams of fluent trainwreck support code)
type Person(?name, ?address) =
()
And/or you can chain constructors and add some logic/helpers on top of that:
type Person(?name, ?address) =
new(?name, ?address) = Person(name = name, address = address)
If you definitely want records and you don't have a good reason for a module
, I'd stick with the factory method on the type
as a type
's Methods can have optional args whereas a module
's functions do not make that possible. (And don't use lower case method names, you'll hate yourself eventually if you do!)
type Person =
{ name: string option; address: string option }
static member Create(?name, ?address): Person =
{ name = name; address = address }
Upvotes: 1
Reputation: 17153
I like record update syntax, but without the noise. Just break out the "empty" instance as a starter:
type Person =
{
name : string option
address : string option
}
let empty =
{
name = None
address = None
}
let p3 =
{ empty with
name = Some "bill bloggs" }
Upvotes: 2