Am I assuming correctly you’re mistakenly saying “positional” instead of “keyword” sometimes? Keyword arguments are what needs to be named in calls, positional arguments cannot be named in calls.
If you’re trying to say that Python has positional-or-keyword arguments instead of strictly positional (unnamed) arguments, then Python 3.8+ actually has those as well:
def myfun2(p1, p2, /, pk1, pk2, *, k1, k2):
print(p1, p2, pk1, pk2, k1, k2)
PEP 570 describes the rationale, but the bulk of it concerns API development. For example, if you allow specifying a name as a keyword (a
for myfun
), then you’re stuck with it for backwards compatibility.
As for why Julia doesn’t have positional-or-keyword arguments at all, it just makes multimethod dispatch inherently ambiguous, even if we’re still not dispatching with respect to keyword arguments. Languages can only pull off positional-or-keyword arguments with unimethods.
This is a very separate topic from the above. I don’t know exactly why and I haven’t been able to find any statements explaining this. However, we can make a very reasonable guess. Even in languages that don’t have a discrepancy in the capabilities of positional versus keyword arguments, keyword arguments aren’t used the same way as positional arguments. The primary utility of keyword arguments is to specify a few values by name out of many default values. Broadcasting doesn’t play nicely with default values, and we can demonstrate it with default positional values:
julia> foo(a = [1 0;0 1], b = [2, 3]) = a*b;
julia> foo() # expected matrix multiplication
2-element Vector{Int64}:
2
3
julia> foo([1 0;0 1], [2, 3]) # expected same result
2-element Vector{Int64}:
2
3
julia> foo.() # expected result of broadcasting over nothing
2-element Vector{Int64}:
2
3
julia> foo.([1 0;0 1], [2, 3]) # uh oh, now it's elementwise multiplication
2×2 Matrix{Int64}:
2 0
0 3
See, we don’t expect to broadcast over default values we may not even be aware of, and the language also can’t parse dotted calls to broadcast over absent arguments. Consequently, we don’t typically have collections as default positional values in practice.
Reasonably, the response would be that we would only broadcast over arguments we specify. That could actually be allowed for keyword arguments, though we would now need new syntax to manually opt in to broadcasting in order to remain backwards-compatible with the current state where no keyword arguments are broadcasted over:
Proposal: keyword argument broadcasting · Issue #34737 · JuliaLang/julia
However, there wasn’t the demand to put it in v1 and it still doesn’t have any motion today. Elementwise operations across several input dimensions typically involve a small fixed set of inputs, and those don’t tend to end up as keyword arguments in practice. Infix operators can only have 2 positional arguments, after all. For now, in the rare case someone does want to do this, it can still be done with a full broadcast
call with the kernel function passing positional arguments to keyword arguments e.g. broadcast((p1, k2p) -> foo(p1; k1=k2p), P, K)
.