Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: controlling dispatch with varargs of defined length (rebased) #10911

Closed
wants to merge 11 commits into from
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ New language features
and macros in packages and user code ([#8791]). Type `?@doc` at the repl
to see the current syntax and more information.

* Varargs functions may now declare the varargs length as `x::Vararg{T,N}` to
restrict dispatch.

Language changes
----------------

Expand Down
2 changes: 2 additions & 0 deletions base/base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ call(T::Type{WeakRef}, v::ANY) = Core.call(T, v)

call{T}(::Type{T}, args...) = convert(T, args...)::T

typealias NTuple{N,T} Tuple{Vararg{T,N}}

convert{T}(::Type{T}, x::T) = x

convert(::Type{Tuple{}}, ::Tuple{}) = ()
Expand Down
2 changes: 1 addition & 1 deletion base/boot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#abstract Any <: Any
#abstract Type{T}

#abstract Vararg{T}
#abstract Vararg{T,N}
#Tuple = (Any...)

#type Symbol
Expand Down
1 change: 1 addition & 0 deletions base/datafmt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module DataFmt

importall Base
import Base: _default_delims, tryparse_internal
import Base: NTuple

export countlines, readdlm, readcsv, writedlm, writecsv

Expand Down
1 change: 1 addition & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export
MathConst,
Matrix,
MergeSort,
NTuple,
Nullable,
ObjectIdDict,
OrdinalRange,
Expand Down
4 changes: 2 additions & 2 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,8 @@ const getfield_tfunc = function (A, s0, name)
if !isa(s,DataType)
return Any
end
if is(s.name,NTuple.name)
return s.parameters[2]
if s <: Tuple && s.types.length == 1 && isvatuple(s)
return s.types[1].parameters[1]
end
if s.abstract
return Any
Expand Down
16 changes: 14 additions & 2 deletions base/methodshow.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Method and method-table pretty-printing

function argtype_decl(n, t) # -> (argname, argtype)
if isvarargtype(t)
return argtype_decl_vararg(n, t)
end
if isa(n,Expr)
n = n.args[1] # handle n::T in arg list
end
Expand All @@ -12,14 +15,23 @@ function argtype_decl(n, t) # -> (argname, argtype)
if t === Any && !isempty(s)
return s, ""
end
if isvarargtype(t)
return s, string(t)
end

function argtype_decl_vararg(n, t)
s = string(n.args[1])
if n.args[2].head == :...
# x... or x::T... declaration
if t.parameters[1] === Any
return string(s, "..."), ""
else
return s, string(t.parameters[1], "...")
end
end
return s, string(t)
# x::Vararg, x::Vararg{T}, or x::Vararg{T,N} declaration
s, length(n.args[2].args) < 4 ?
string("Vararg{", t.parameters[1], "}") :
string("Vararg{", t.parameters[1], ",", t.parameters[2], "}")
end

function arg_decl_parts(m::Method)
Expand Down
1 change: 1 addition & 0 deletions base/multidimensional.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module IteratorsMD
import Base: eltype, length, start, done, next, last, getindex, setindex!, linearindexing, min, max, eachindex
import Base: simd_outer_range, simd_inner_length, simd_index, @generated
import Base: @nref, @ncall, @nif, @nexprs, LinearFast, LinearSlow, to_index
import Base: NTuple

export CartesianIndex, CartesianRange

Expand Down
1 change: 1 addition & 0 deletions base/sparse.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ importall Base.LinAlg
import Base.promote_eltype
import Base.@get!
import Base.Broadcast.eltype_plus, Base.Broadcast.broadcast_shape
import Base.NTuple

export AbstractSparseArray, AbstractSparseMatrix, AbstractSparseVector, SparseMatrixCSC,
blkdiag, dense, droptol!, dropzeros!, etree, issparse, nnz, nonzeros, nzrange,
Expand Down
2 changes: 2 additions & 0 deletions doc/manual/functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,8 @@ the zero or more values passed to ``bar`` after its first two arguments:
In all these cases, ``x`` is bound to a tuple of the trailing values
passed to ``bar``.

It is possible to constrain the number of values passed as a variable argument; this will be discussed later in :ref:`man-vararg-fixedlen`.

On the flip side, it is often handy to "splice" the values contained in
an iterable collection into a function call as individual arguments. To
do this, one also uses ``...`` but in the function call instead:
Expand Down
26 changes: 26 additions & 0 deletions doc/manual/methods.rst
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,32 @@ can also constrain type parameters of methods::
The ``same_type_numeric`` function behaves much like the ``same_type``
function defined above, but is only defined for pairs of numbers.

.. _man-vararg-fixedlen:

Parametrically-constrained Varargs methods
------------------------------------------

Function parameters can also be used to constrain the number of arguments that may be supplied to a "varargs" function (:ref:`man-varargs-functions`). The notation ``Vararg{T,N}`` is used to indicate such a constraint. For example:

.. doctest::

julia> bar(a,b,x::Vararg{Any,2}) = (a,b,x)

julia> bar(1,2,3)
ERROR: MethodError: `bar` has no matching method bar(::Int, ::Int, ::Int)

julia> bar(1,2,3,4)
(1,2,(3,4))

julia> bar(1,2,3,4,5)
ERROR: MethodError: `bar` has no method matching bar(::Int, ::Int, ::Int, ::Int, ::Int)

More usefully, it is possible to constrain varargs methods by a parameter. For example::

function getindex{T,N}(A::AbstractArray{T,N}, indexes::Vararg{Number,N})

would be called only when the number of ``indexes`` matches the dimensionality of the array.

Note on Optional and keyword Arguments
--------------------------------------

Expand Down
2 changes: 2 additions & 0 deletions src/alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ typedef struct {
// Note that this function updates len
static jl_value_t *jl_new_bits_internal(jl_value_t *dt, void *data, size_t *len)
{
/*
if (jl_is_ntuple_type(dt)) {
jl_value_t *lenvar = jl_tparam0(dt);
jl_value_t *elty = jl_tparam1(dt);
Expand All @@ -119,6 +120,7 @@ static jl_value_t *jl_new_bits_internal(jl_value_t *dt, void *data, size_t *len)
memcpy(jl_data_ptr(v), data, nb);
return v;
}
*/

assert(jl_is_datatype(dt));
jl_datatype_t *bt = (jl_datatype_t*)dt;
Expand Down
167 changes: 166 additions & 1 deletion src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -1263,7 +1263,7 @@ void jl_init_primitives(void)
add_builtin("TypeName", (jl_value_t*)jl_typename_type);
add_builtin("TypeConstructor", (jl_value_t*)jl_typector_type);
add_builtin("Tuple", (jl_value_t*)jl_anytuple_type);
add_builtin("NTuple", (jl_value_t*)jl_ntuple_type);
// add_builtin("NTuple", (jl_value_t*)jl_ntuple_type);
add_builtin("Vararg", (jl_value_t*)jl_vararg_type);
add_builtin("Type", (jl_value_t*)jl_type_type);
add_builtin("DataType", (jl_value_t*)jl_datatype_type);
Expand Down Expand Up @@ -1617,6 +1617,171 @@ DLLEXPORT void jl_(void *jl_value)
in_jl_--;
}

// Useful because the jl_typeof macro isn't available from debugger
void jl_static_show_typeof(JL_STREAM *out, jl_value_t *v)
{
if (v == NULL) {
jl_printf(out, "#<null>");
}
else if (jl_typeof(v) == NULL) {
jl_printf(out, "<?::#null>");
}
else if (jl_astaggedvalue(v)->type_bits < 4096U) {
jl_printf(out, "<?::#%d>", (int)jl_astaggedvalue(v)->type_bits);
}
else if (jl_is_lambda_info(v)) {
jl_printf(out, "lambda_info");
}
else if (jl_is_svec(v)) {
jl_printf(out, "svec");
}
else if (jl_is_datatype(v)) {
jl_printf(out, "DataType");
}
else if (jl_is_func(v)) {
jl_printf(out, "#<function>");
}
else if (jl_typeis(v, jl_intrinsic_type)) {
jl_printf(out, "#<intrinsic function>");
}
else if (jl_is_int64(v)) {
jl_printf(out, "Int64");
}
else if (jl_is_int32(v)) {
jl_printf(out, "Int32");
}
else if (jl_typeis(v,jl_int16_type)) {
jl_printf(out, "Int16");
}
else if (jl_typeis(v,jl_int8_type)) {
jl_printf(out, "Int8");
}
else if (jl_is_uint64(v)) {
jl_printf(out, "UInt64");
}
else if (jl_is_uint32(v)) {
jl_printf(out, "UInt32");
}
else if (jl_typeis(v,jl_uint16_type)) {
jl_printf(out, "UInt16");
}
else if (jl_typeis(v,jl_uint8_type)) {
jl_printf(out, "UInt8");
}
else if (jl_is_cpointer(v)) {
jl_printf(out, "cpointer");
}
else if (jl_is_float32(v)) {
jl_printf(out, "Float32");
}
else if (jl_is_float64(v)) {
jl_printf(out, "Float64");
}
else if (v == jl_true || v == jl_false) {
jl_printf(out, "jl_boolean");
}
else if (v == jl_nothing) {
jl_printf(out, "nothing");
}
else if (jl_is_byte_string(v)) {
jl_printf(out, "ByteString");
}
else if (jl_is_uniontype(v)) {
jl_printf(out, "Union");
}
else if (jl_is_typector(v)) {
jl_printf(out, "TypeConstructor");
}
else if (jl_is_typevar(v)) {
jl_printf(out, "TypeVar");
}
else if (jl_is_module(v)) {
jl_printf(out, "Module");
}
else if (jl_is_symbol(v)) {
jl_printf(out, "Symbol");
}
else if (jl_is_gensym(v)) {
jl_printf(out, "GenSym");
}
else if (jl_is_symbolnode(v)) {
jl_printf(out, "SymbolNode");
}
else if (jl_is_globalref(v)) {
jl_printf(out, "GlobalRef");
}
else if (jl_is_labelnode(v)) {
jl_printf(out, "LabelNode");
}
else if (jl_is_gotonode(v)) {
jl_printf(out, "GotoNode");
}
else if (jl_is_quotenode(v)) {
jl_printf(out, "QuoteNode");
}
else if (jl_is_newvarnode(v)) {
jl_printf(out, "NewVarNode");
}
else if (jl_is_topnode(v)) {
jl_printf(out, "topnode");
}
else if (jl_is_linenode(v)) {
jl_printf(out, "LineNode");
}
else if (jl_is_expr(v)) {
jl_printf(out, "Expr");
}
else if (jl_is_array(v)) {
jl_printf(out, "Array");
}
else if (jl_typeis(v,jl_loaderror_type)) {
jl_printf(out, "LoadError");
}
else if (jl_typeis(v,jl_errorexception_type)) {
jl_printf(out, "ErrorException");
}
else if (jl_is_datatype(jl_typeof(v))) {
jl_datatype_t *dv = (jl_datatype_t*)jl_typeof(v);
if (dv->name->module != jl_core_module) {
jl_static_show_x(out, (jl_value_t*)dv->name->module, 0);
jl_printf(out, ".");
}
jl_printf(out, "%s", dv->name->name->name);
if (dv->parameters && (jl_value_t*)dv != dv->name->primary) {
size_t j, tlen = jl_nparams(dv);
if (tlen > 0) {
jl_printf(out, "{");
for (j = 0; j < tlen; j++) {
jl_value_t *p = jl_tparam(dv,j);
jl_static_show_x(out, p, 0);
if (j != tlen-1)
jl_printf(out, ", ");
}
jl_printf(out, "}");
}
else if (jl_is_tuple_type(dv)) {
jl_printf(out, "{}");
}
}
}
else {
jl_printf(out, "<?Unknown?>");
}
}

DLLEXPORT void jlt_(void *jl_value)
{
in_jl_++;
JL_TRY {
(void)jl_static_show_typeof(JL_STDOUT, (jl_value_t*)jl_value);
jl_printf(JL_STDOUT,"\n");
}
JL_CATCH {
jl_printf(JL_STDOUT, "\n!!! ERROR in jlt_ -- ABORTING !!!\n");
}
in_jl_--;
}

DLLEXPORT void jl_breakpoint(jl_value_t *v)
{
// put a breakpoint in you debugger here
Expand Down
4 changes: 2 additions & 2 deletions src/dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -1897,7 +1897,7 @@ void jl_init_serializer(void)
jl_labelnode_type, jl_linenumbernode_type,
jl_gotonode_type, jl_quotenode_type, jl_topnode_type,
jl_type_type, jl_bottom_type, jl_ref_type, jl_pointer_type,
jl_vararg_type, jl_ntuple_type, jl_abstractarray_type,
jl_vararg_type, /*jl_ntuple_type, */jl_abstractarray_type,
jl_densearray_type, jl_box_type, jl_void_type,
jl_typector_type, jl_typename_type,
jl_task_type, jl_uniontype_type, jl_typetype_type, jl_typetype_tvar,
Expand All @@ -1910,7 +1910,7 @@ void jl_init_serializer(void)
jl_datatype_type->name, jl_uniontype_type->name, jl_array_type->name,
jl_expr_type->name, jl_typename_type->name, jl_type_type->name,
jl_methtable_type->name, jl_method_type->name, jl_tvar_type->name,
jl_ntuple_type->name, jl_abstractarray_type->name, jl_vararg_type->name,
/*jl_ntuple_type->name, */jl_abstractarray_type->name, jl_vararg_type->name,
jl_densearray_type->name, jl_void_type->name, jl_lambda_info_type->name,
jl_module_type->name, jl_box_type->name, jl_function_type->name,
jl_typector_type->name, jl_intrinsic_type->name, jl_task_type->name,
Expand Down
Loading