I find that it’s quite common for me to write a function that requires the input to be iterable. But I don’t think there is a good way to restrict it to that. I think I would love to see a trait system built into Julia e.g.
fn(x::Symbol) = fn((x,))
function fn(x_itr:::IterableTrait)
## do something to each success element of `x_itr`
end
What’s the best way to achieve what I want so far in your opinion.
While it wasn’t clear in the prose, the pseudo-code shown seems to imply that the OP wanted to dispatch on whether something is iterable and act differently depending on whether or not it’s iterable, in which case simply duck typing won’t work.
I don’t think it’s too hard to imagine cases where you care whether or not an input is has properties like being iterable or being callable and I don’t think telling people “don’t care about that” is particularly helpful (though one may often be right that there’s a better solution that doesn’t involve doing this).
I think the message is that trying to design an API which does something different depending on whether the input is iterable (callable etc) is the wrong way to go about it. Eg an imaginary
function do_stuff(thing)
if is_iterable(thing)
collect(thing)
elseif is_callable(thing)
thing()
else
fallback(thing)
end
end
can be a very confusing design, regardless of whether it is done with traits or not.
Technically a trait for something supporting the iterate protocol would be very easy to add to the language, but I suspect that one of the reasons this has not happened is that usually there is a better way to organize code.
which introduced mergewith(f, dicts...) that superseded merge(f, dicts...). This is because merge(f, dicts...) cannot be robustly distinguished from merge(dicts...) since any type can implement the call operator. I like how Jeff summarized it:
With multiple dispatch, just as the meaning of a function is important to get consistent, it’s also important to get the meanings of the argument slots consistent when possible. It’s not ideal for a function to treat an argument in a fundamentally different way based on its type
If the object doesn’t implement iterate, it’ll throw an error anyway.
If it is about giving a good error message, something like Base.Experimental.register_error_hint would be a better approach. Not sure if it’s usable here though.
If you use BinaryTraits.jl, then you can statically verify the types assigned to the Iterable trait. So you would find any implementation problem during development rather than at runtime.
This still requires that the user go and assign any type they might accept with the Iterable trait though right? I think the OP wants to just automatically detect if something is iterable and dispatch on that.