Find out number of function arguments or if passed at all

Hi

(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?

Thx a lot!!!

Just define two methods?

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
3 Likes

Wow okay!
Thanks for the super quick reply!! :grinning:

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…? :thinking: Yeah I think I like that concept :slight_smile:

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.

This other thread might be of interest.

Instead of passing the empty string, you can directly define your default argument value:

function onFile(filename::String=choose_user_file())
    println("Do something with file ", filename)
end
4 Likes

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…)

Instead of passing the empty string, you can directly define your default argument value:

Cool :slight_smile:

I really like this idea of directly calling a function with proper return type as default value. I think this will be my favorite from now on :+1:

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