A lot of functions in the standard library trod along happily if a misspelled keyword list option is passed:
iex(3)> String.split("a1a11a", "1", triim: true)
["a", "a", "", "a"]
Compare to Python, which has keyword arguments baked into the language:
>>> from sklearn.linear_model import LogisticRegression
>>> LogisticRegression(qwerty=5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __init__() got an unexpected keyword argument 'qwerty'
The Elixir behavior has bitten me many times. Putting the onus on the developer to check whether options are all valid isn’t working in my opinion, given that these kinds of checks are incredibly rare. One solution is to add a helper function to the standard library:
defaults = [opt: false, another_opt: true, rare_opt: 5]
correct_opts = [opt: true, another_opt: false]
Options.get!(correct_opts, defaults)
# [opt: true, another_opt: false, rare_opt: 5]
misspelled_opts = [blopt: true, another_opt: false]
Options.get!(misspelled_opts, defaults)
# throws, since blopt is not in defaults
This solution is too verbose, I think, since it requires developers to name all options at least once more than they usually would. The reason why this is almost a non-issue in Python is that it would take more effort to allow misspelled keyword arguments. With that in mind, I think the use of macros might be warranted.
def my_fun(positional_arg, opts \\ []) do
options!(opts, my_opt = false, another_opt = true, rare_opt = 5)
IO.inspect(rare_opt) # the value passed for opts[:rare_opt]
end
my_fun("pos_arg", my_opt: true)
my_fun("pos_arg", fake_opt: true) # throws
The intended behavior is that of keyword arguments in Python, meaning that the options are accessible as another_opt
rather than opts[:another_opt]
after the options!
macro call.
I don’t necessary like the assignment syntax hijacking, but I do think a solution of this kind is called for.