0
$\begingroup$

Overloading functions for different types of parameters is very powerful. I can easily write different versions of a function depending on the function parameters being Integer, Real, Complex, Sring List and so on.

Now I want to write function which operates on data structures looking like this:

{_String, _List} Example: {"my data structure", {1,2,3,4,5}}

My function shall also operate either

  • two strings (containing file names) plus an optional third one which can be either "CSV" or "TSV"

Examples: f["file1","fiel2"] or f["file1","fiel2","TSV"]

or on

  • an arbitrary number>2 of strings (containing file names but neither "CSV" or "TSV") plus an optional last parameter which can be either "CSV" or "TSV"

Examples: f["file1","fiel2","file3"] or f["file1","fiel2","file3","TSV"]

  • or a string (containing a file name) as first parameter, a data structure of the type described above as second one plus an optional third one which can be either "CSV" or "TSV"

Examples: f["file1",{"my data structure", {1,2,3}}] or f["file1",{"my data structure", {1,2,3}}, "CSV"]

I did it like this:

Remove[f];
dataStruct={_String, _List};
f[a_String, b_String, type_:"CSV"]:=Print["1. variant with two Strings, type=", type];
f[a_String, b:dataStruct, type_:"CSV"]:=Print["2. variant with String and dataStruct, type=", type];
f[a:dataStruct, b:dataStruct, type_:"CSV"]:=Print["3. variant with two dataStruct, type=", type];
f[{a_String, b_String, ___String}, type_:"CSV"]:=Print["4. variant with List of Strings, type=", type];

f["foo", "bar"]
f["foo", "bar", "CSV"]

work as expected.

f["foo", "bar", "metrics"]

returns the last parameter "metrics" as type instead of matching the fourth definition.

f["foo", {"bar", {}}]
f["foo", {"bar", {}}, "CSV"]

invoke the second definition

f[{"foo", {1, 2}}, {"bar", {3, 4, 5}}]

takes the third variant.

How can I cope with this?

$\endgroup$
2
  • $\begingroup$ Aren't you missing the enclosing List in your "metrics" example? $\endgroup$
    – Edmund
    Commented Dec 1, 2022 at 23:55
  • $\begingroup$ Yes, your observation is perfectly right! f[{"foo", "bar", "metrics"}] and f[{"foo", "bar", "metrics"}, "CSV"] use the fourth definition! $\endgroup$ Commented Dec 2, 2022 at 12:36

1 Answer 1

1
$\begingroup$

One way would be to use symbols for the filetype (it will force disambiguation):

Remove[g];
dataStruct = {_String, _List};
g[a_String, b_String, type_Symbol : CSV] := Print["1. variant with two Strings, type=", type];
g[a_String, b : dataStruct, type_Symbol : CSV] := Print["2. variant with String and dataStruct, type=", type];
g[a : dataStruct, b : dataStruct, type_Symbol : CSV] := Print["3. variant with two dataStruct, type=", type];
g[a_String, b_String, ___String, type_Symbol : CSV] := Print["4. variant with List of Strings, type=", type];

g["foo", "bar", "metrics"] gives 4. variant with List of Strings, type=CSV

Another would be to use options instead of default arguments:

Remove[h];
Options[h] = {MyFileType -> "CSV"};
dataStruct = {_String, _List};
h[a_String, b_String, OptionsPattern[]] := Print["1. variant with two Strings, type=", OptionValue[MyFileType]];
h[a_String, b : dataStruct, OptionsPattern[]] := Print["2. variant with String and dataStruct, type=", OptionValue[MyFileType]];
h[a : dataStruct, b : dataStruct, OptionsPattern[]] := Print["3. variant with two dataStruct, type=", OptionValue[MyFileType]];
h[a_String, b_String, ___String, OptionsPattern[]] := Print["4. variant with List of Strings, type=", OptionValue[MyFileType]];

h["foo", "bar", "metrics"] gives 4. variant with List of Strings, type=CSV

h["foo", {"bar", {}}, MyFileType -> "TSV"] gives 2. variant with String and dataStruct, type=TSV

Another would be to use more specific representations than String, maybe a special head. But without knowing your semantics, I have no specific suggestion along those lines.

I generally avoid default argument values. It seems to inevitably bump up against ambiguities as your method signatures evolve. Without further info, I think options is probably the cleanest way to go.

$\endgroup$
1
  • $\begingroup$ Thank you lericr for your detailed discussion and two alternative advices. The first one, to request a Symbol for the default argument is more handy since it requires less changing of existing code and it is less typing. However, the comment above your reply shows that I also fell for a catastrophic clerical error. Nevertheless, many thanks! $\endgroup$ Commented Dec 2, 2022 at 12:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.