DEV Community

Cover image for API Design: Optional Parameters

API Design: Optional Parameters

Sam Rose on April 02, 2019

When we write functions, it's common to want to give the user options to suit a range of use-cases. There are good ways and bad ways of doing it, a...
Collapse
 
michaeltharrington profile image
Michael Tharrington

Hey Sam, really digging this!

Just a quick tip. I noticed that you have another post in this series:

You could also edit both this post and the other to include "series: whatever name you'd like for your series" in the front matter. This'll connect your two posts with a cool little dot scroll option at the top of each post that lets the reader easily flip between posts in the series.

Umm yeah, no idea what this option is actually called, so chose to call it a "dot scroll" ... but anyway, it looks like this:

my series

... in my posts here & here.

Not a must-do, just a nice-to-have in case you wanna!

Side note, not sure if you've ever read Kin Lane (apievangelist.com/) but I'm thinking you might dig some of his stuff. He talks about APIs from a number of angles - design, politics, you name it... though he's currently on hiatus. Still, he's extremely prolific and has a backlog of entries that go on for ages. Definitely worth a read.

Collapse
 
theodesp profile image
Theofanis Despoudis • Edited

What about a more SOLID solution that is a little bit more compromising

func ProvideOptions(os ...Option) Options  {
    o := Options{}
    for _, option := range os {
        option(&o)
    }
    return o
}

func Get(url string, o Options) (Request, error) {
      // Use immediately
        ....
}

func main()  {
    req, _ := Get("https://example.com", ProvideOptions())

}

Maybe because I like to make the Get function a little bit cleaner and not having to deal with handling Configuration

Collapse
 
samwho profile image
Sam Rose

Nice!

Collapse
 
vinceramces profile image
Vince Ramces Oliveros

While there's optional parameter(which might be risky if we put default values in some edge cases.). There's named parameters. which is more readable to the user. think of it as a superset of optional parameters where you don't have to initialize a value. some might think that this can be a verbose. some which might be helpful.

I didn't even know what named/optional parameters were until I used flutter.

Named parameters can be used in methods/functions, and class properties as well.

Collapse
 
samwho profile image
Sam Rose

For sure! I talk about these in the second half of the post. 😀

Collapse
 
vinceramces profile image
Vince Ramces Oliveros

From what I've understood from optional parameters is that, optional parameters have default values, while named parameters don't need. but in Dart, when declaring a named parameters/arguments, they use @required annotation that a specific argument should have a value declared when using it. Great Article by the way. Sorry if I differentiate optional and named parameters. This was something Dart clarifies me of using it when developing flutter apps.

Collapse
 
rhymes profile image
rhymes

Really good article, thank you Sam!

Collapse
 
samwho profile image
Sam Rose

Thank you so much! I'm glad you enjoyed it. If you don't mind, could you spare a few minutes to tell me things you like and didn't like? I'm looking to level up my writing. :)

Collapse
 
rhymes profile image
rhymes • Edited

ahha nothing I didn't like, the builder in Java is very verbose :D

BTW a little trick by more recent Python versions. You explained the problem with optional parameters, because you can do things like this:

>>> def get(url, follow_redirects=False, headers=None):
...     pass
...
>>> get("https://example.com")
>>> get("foobar", 3)

And for a reader it's not easy to know if 3 is referred to either parameter, or at least is not explicit at all which makes life complicated with many arguments to specify.

Since recently you can do this:

>>> def get(url, *, follow_redirects=False, headers=None):
...     pass
...
>>> get("foobar")
>>> get("foobar", True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: get() takes 1 positional argument but 2 were given
>>> get("foobar", follow_redirects=True)

The compiler now forces you to specify the name of each optional argument

Thread Thread
 
samwho profile image
Sam Rose

Oh that's super nice, I had no idea that was possible!

Thread Thread
 
rhymes profile image
rhymes • Edited

Yeah, it can also become a catch all for all unnamed optional parameters.

>>> def get(url, *args, follow_redirects=False, headers=None):
...     print(f"u: {url}, a: {args}, f: {follow_redirects}, h: {headers}")
...
>>> get("https://dev.to")
u: https://dev.to, a: (), f: False, h: None
>>> get("https://dev.to", 1, 2, 3)
u: https://dev.to, a: (1, 2, 3), f: False, h: None
>>> get("https://dev.to", 1, 2, 3, follow_redirects=True, headers={"user-agent": "acme"})
u: https://dev.to, a: (1, 2, 3), f: True, h: {'user-agent': 'acme'}
>>> get("https://dev.to", follow_redirects=True, headers={"user-agent": "acme"})
u: https://dev.to, a: (), f: True, h: {'user-agent': 'acme'}