(This could be relevant for all kinds of functions in general, but I’ll give a specific example here).
so I’m quite new to julia and currently trying to write a custom package/module with some data loading and processing functionalities for a very specific type of files.
I know a bit about positional and keyword arguments and defaults.
I want to write a function that does some processing on files in a folder specified (or not!!!) by the user.
For functions that work on files I like to do sth like:
function onFile(filename::String="")
if filename == ""
filename = "chosen_file.dat" # Show the user a file dialog to select a file
end
println("Do something with file ", filename)
end
to give the user the option to provide a filename directly or open a dialog when the argument is not given. Giving an empty string as filename is certainly invalid and behaviour is as expected.
No assume a function is supposed to do something on a folder (process all files in folder or sth like that). In this case
function onFolder(foldername::String="")
if foldername == ""
foldername = "chosen_folder" # Show the user a file dialog to select a folder
# But WAIT! Giving "" is perfectly fine for selecting the current folder!
end
println("Do something on folder ", foldername)
end
So that doesn’t work because “” is a valid option. And even if the function is called with no argument whatsoever, it should still process the current folder (like, e.g., readdir() does). Of course I could define something invalid here like “?” or so.
But what would be a nice way in good programming style to actually check if a positional argument was passed at all (and not define a rediculous default case)? I could omit the ::String typing and use nothing as default but then I must manually check if a string was passed…
Coming from MATLAB there is the varargin concept. Is there something comparable in julia?
So in short… How do I check if the caller passed any arguments at all?
julia> onFolder() = println("Do something on folder chosen_folder")
onFolder (generic function with 1 method)
julia> onFolder(foldername) = println("Do something on folder $foldername")
onFolder (generic function with 2 methods)
julia> onFolder()
Do something on folder chosen_folder
julia> onFolder("other folder")
Do something on folder other folder
Maybe that is just the good julia programming style I am looking for!
Still curious anyway if there is a built-in way to check for the number of passed arguments?
(Although your solution sounds like perfectly fine, of course)
Or is this just good style to write multiple methods and then those methods just automatically “know” how many arguments were given…? Yeah I think I like that concept
An alternative solution is to use keyword arguments with nothing as a sentinel value* (although this is not exactly analogous to Matlab’s nargin):
function onFile(;filename=nothing)
if isnothing(filename)
filename = "chosen_file.dat"
end
println("Do something with file ", filename)
end
In my experience, the need for nargin in Matlab is generally caused by the lack of multiple dispatch forcing manual creation of different methods within the function body. The combination of multiple dispatch and keyword arguments with sentinel or default values is a much more flexible and convenient solution.
(Also, in the very specific case of an indeterminate number of arguments of the same type, Varargs functions are a thing.)
*Edit: There’s nothing wrong with using nothing as a sentinel value, it’s a very idiomatic pattern for Julia code, especially for keyword arguments.
Yes, thanks.
I’ve actually been using this approach frequently and find it quite nice as well.
But then I stumbled across the conflict with the strong typing… If I want to force filename::String I cannot assign nothing as default.
(Not that it is super important for me to have that typing enforced in the particular case. But that’s what got me thinking about alternate solutions…)
For simplicity, I had omitted the type requirements, but a Union is the solution for maintain a strict type parameter:
function onFile(;filename::Union{Nothing,String}=nothing)
if isnothing(filename)
filename = "chosen_file.dat"
end
println("Do something with file ", filename)
end