MrD at KookerellaLtd
MrD at KookerellaLtd

Reputation: 2807

mechanisms for handing functions with multiple optional parameters

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

Answers (2)

Ruben Bartelink
Ruben Bartelink

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

Brian Berns
Brian Berns

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

Related Questions