I agree to be as generic as possible. I should have used this second 
example instead, a quite common scenario where being generic doesn't make 
as much sense:

function output(context::Context, strings::Vector{String})
    context.something()
    # Call the single-string version of the function on each item
    for string in strings output(string) end
end

output(context, ["Hi there"])

I have already encountered this pattern a number of times in just my few 
weeks with Julia; wrapping a function with a version that takes a single 
item with one that takes a collection of them.

The style guide at 
http://julia.readthedocs.org/en/latest/manual/style-guide/#handle-excess-argument-diversity-in-the-caller
 
seems to encourage my approach, 
and 
http://julia.readthedocs.org/en/latest/manual/style-guide/#don-t-use-unnecessary-static-parameters
 
directly condemns Iain's recommendation.


On Sunday, May 25, 2014 7:17:22 PM UTC-4, Jason Merrill wrote:
>
> On Sunday, May 25, 2014 2:11:41 PM UTC-7, Adam Smith wrote:
>>
>> Actually, no. I'm not going to pretend this is a good thing. You're right 
>> that it is "consistent and logical" when you're using an academic 
>> type-correctness viewpoint. However, it is not consistent from a developer 
>> perspective, and here's why.
>>
>> On most functions, I don't need to specify parametric types (which is 
>> good: there is less clutter). Let's say I have a function:
>> function output(context::Context, string::String)
>>     context.something()
>>     println(string)
>> end
>>
>> output(context, "Hi there")
>>
>> And a few days later I decide it would be better to accept a list of 
>> strings instead, so I do the most natural thing, and I just put Vector{} 
>> around the type that was already working:
>>
>> function output(context::Context, strings::Vector{String})
>>     context.something()
>>     for string in strings println(string) end
>> end
>>
>> output(context, ["Hi there"])
>>
>> This is absolutely what every new Julia developer will expect to work 
>> (regardless of how "correct" it is), and it will fail. It's especially 
>> confusing because for some types, it works (like changing Int to 
>> Vector{Int}). It is simply bizarre to new developers using the language 
>> that the type matching worked on a single element of that type, but not on 
>> a parametric collection of that type.
>>
>> Yes, I know why it is the way it is, and yes, I know why a textbook says 
>> it should be this way, but thinking from a UX perspective (where the "user" 
>> is a developer new-ish to Julia), it's quite off-putting.
>>
>
> It's easier to compose different libraries together when implementers 
> don't overtype their methods, and instead rely more on duck typing. To take 
> your "output" example, the contract of the vector that's passed in is 
> actually only that println needs to be defined on all of its elements, and 
> not that every element must be a string.
>
> Suppose for performance reasons I decide to use some more exotic text data 
> structure, like a Rope, and for reasons that are out of my control, it 
> inherits from FunctionalContainer or something like that instead of from 
> String. Julia doesn't have multiple inheritance, so this can easily happen. 
> Then if I want to use your library, and you've decided to require a vector 
> of String (or out of necessity, some concrete subtype of String), I'm out 
> of luck. If you instead define output(context::Context, strings::Vector)then 
> I can get on with my business.
>
> For this reason, I think it's better to be sparing with explicit type 
> parameters: only add them when you actually want two different method 
> bodies depending on the type.
>
> To make this kind of duck typing more explicit and robust, it might be 
> nice if Julia eventually added shapes or interfaces or something like that 
> that let you specify a little bit more about the contract of arguments in a 
> way that depends on what methods are defined on them, but not on their 
> explicit type.
>

Reply via email to