I’ve just run into the following situation:
Currently, VSTPlugin.search
already takes 8 arguments:
*search { arg server, dir, useDefault=true, verbose=true, wait = -1, action, save=true, parallel=true, timeout=nil;
The new release would add 2 more parameters. Now, 8 parameters already feel awkward, but 10 parameters would definitely be too much, and there might be even more in the future…
So I am considering breaking the API and changing the method signature to:
*.search { arg server, dir, options, verbose=true, wait= -1, action;
with options
being an (optional) Event holding all additional settings.
Usually, I try to avoid API breaks as much as possible, but I think in this case it’s not too much of a problem because usually a project will only contain a single call to VSTPlugin.search
. Also, it is easy to change
VSTPlugin.search(dir: [ "/my/vst/dir" ], save: true, parallel: false)
to
VSTPlugin.search(dir: [ "/my/vst/dir", options: ( save: true, parallel: false ))
.
Needless to say, I should have used this approach from the very beginning. Such much for YAGNI…
Generally, I have been thinking about the pros and cons of using the Parameter Object Pattern in a language like SuperCollider:
Pro:
- keeps method signature small
- can be easily expanded
- parameter order doesn’t matter
- names can change without breaking the API (by keeping the old names as aliases)
Con:
- Options not visible in auto-complete
- Can be more verbose compared to positional arguments
Also note that when using a dedicated class, like ServerOptions
, a seperate initialization step is needed. This is not the case when using Events, which is very similar to how people emulate named parameters in languages like JS or Lua:
// JS: passing an object literal
foo({ bar: 10, baz: 5 });
-- Lua: passing a table literal
foo({ bar = 10, baz = 5 })
-- actually, because it's the only argument, we can even omit the paranthesis:
foo { bar = 10, baz = 5 }
In a language like C++, which only has positional parameters, it is clear that a function with too many parameters becomes unweildy. However, in languages with keyword arguments, this is not a real problem, because you can simply skip all the arguments that you’re not interested in (assuming they are optional).
So is it only an aesthetic issue?
IMO, Python allows to solve this in a very elegant way, because optional keyword arguments can be collected in the **kwargs
parameter:
foo(a, b, **kwargs):
print(a, b)
for key, value in kwargs.items():
print("{0} = {1}".format(key, value))
foo(1, b = 2, bar = 3, baz = 4) # 'bar' and 'baz' are contained in 'kwargs'
This means you don’t have to list all possible parameters in the function signature and get all the flexibility of passing an object, but all parameters look and feel the same.
What are your opinions on this matter? What is your personal limit where you would consider switching to a parameter object - if at all?