From a008dfa3f506470686523ee476754099056f0532 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 11 Feb 2016 17:18:14 -0500 Subject: [PATCH 1/6] Implement f.(args...) as a synonym for broadcast(f, args...), as discussed in #8450. Also, support f.:x as a synonym for getfield(f, :x), so that one no longer needs to do f.(:x). For example, it was common to use Base.(:+) and similar, and this can now be written Base.:+ --- base/REPLCompletions.jl | 8 ++-- base/docs/helpdb/Base.jl | 74 +++++++++++++++---------------- base/inference.jl | 8 ++-- base/interactiveutil.jl | 2 +- base/loading.jl | 2 +- base/pkg/resolve/fieldvalue.jl | 4 +- base/pkg/resolve/versionweight.jl | 24 +++++----- base/precompile.jl | 2 +- base/show.jl | 2 +- examples/juliatypes.jl | 2 +- src/julia-parser.scm | 8 +++- src/julia-syntax.scm | 10 ++++- test/core.jl | 25 ++++++----- test/replutil.jl | 4 +- test/unicode/utf8proc.jl | 4 +- 15 files changed, 98 insertions(+), 81 deletions(-) diff --git a/base/REPLCompletions.jl b/base/REPLCompletions.jl index 4709cb07ffd4b..d9c30b1e5812c 100644 --- a/base/REPLCompletions.jl +++ b/base/REPLCompletions.jl @@ -251,8 +251,8 @@ function get_value(sym::Expr, fn) end fn, true end -get_value(sym::Symbol, fn) = isdefined(fn, sym) ? (fn.(sym), true) : (nothing, false) -get_value(sym::QuoteNode, fn) = isdefined(fn, sym.value) ? (fn.(sym.value), true) : (nothing, false) +get_value(sym::Symbol, fn) = isdefined(fn, sym) ? (getfield(fn, sym), true) : (nothing, false) +get_value(sym::QuoteNode, fn) = isdefined(fn, sym.value) ? (getfield(fn, sym.value), true) : (nothing, false) get_value(sym, fn) = sym, true # Return the value of a getfield call expression @@ -269,7 +269,7 @@ function get_type_call(expr::Expr) f_name = expr.args[1] # The if statement should find the f function. How f is found depends on how f is referenced if isa(f_name, TopNode) - ft = typeof(Base.(f_name.name)) + ft = typeof(getfield(Base, f_name.name)) found = true else ft, found = get_type(f_name, Main) @@ -458,7 +458,7 @@ function completions(string, pos) end end end - ffunc = (mod,x)->(isdefined(mod, x) && isa(mod.(x), Module)) + ffunc = (mod,x)->(isdefined(mod, x) && isa(getfield(mod, x), Module)) comp_keywords = false end startpos == 0 && (pos = -1) diff --git a/base/docs/helpdb/Base.jl b/base/docs/helpdb/Base.jl index 1e50a3993b4c7..208c887553897 100644 --- a/base/docs/helpdb/Base.jl +++ b/base/docs/helpdb/Base.jl @@ -74,7 +74,7 @@ asin Subtype operator, equivalent to `issubtype(T1,T2)`. """ -Base.(:(<:)) +Base.:(<:) """ schedule(t::Task, [val]; error=false) @@ -235,7 +235,7 @@ besselj Divide two integers or rational numbers, giving a `Rational` result. """ -Base.(:(//)) +Base.:(//) """ At_mul_B(A, B) @@ -279,7 +279,7 @@ sortrows Element-wise right division operator. """ -Base.(:(./)) +Base.:(./) """ IPv6(host::Integer) -> IPv6 @@ -557,7 +557,7 @@ ind2sub(a, index) Element-wise multiplication operator. """ -Base.(:(.*)) +Base.:(.*) """ ror!(dest::BitArray{1}, src::BitArray{1}, i::Integer) -> BitArray{1} @@ -760,7 +760,7 @@ julia> [1:5;] |> x->x.^2 |> sum |> inv 0.01818181818181818 ``` """ -Base.(:(|>)) +Base.:(|>) """ assert(cond) @@ -899,7 +899,7 @@ diag Element-wise exponentiation operator. """ -Base.(:(.^)) +Base.:(.^) """ isspace(c::Union{Char,AbstractString}) -> Bool @@ -1185,7 +1185,7 @@ sizeof(::AbstractString) See the [`is`](:func:`is`) operator. """ -Base.(:(===)) +Base.:(===) """ ReadOnlyMemoryError() @@ -1638,7 +1638,7 @@ searchsorted Right division operator: multiplication of `x` by the inverse of `y` on the right. Gives floating-point results for integer arguments. """ -Base.(:(/)) +Base.:(/) """ connect([host],port) -> TCPSocket @@ -2540,7 +2540,7 @@ reduce(op, itr) Element-wise greater-than-or-equals comparison operator. """ -Base.(:(.>=)) +Base.:(.>=) """ stdm(v, m) @@ -2693,7 +2693,7 @@ eachindex Element-wise less-than comparison operator. """ -Base.(:(.<)) +Base.:(.<) """ UndefRefError() @@ -2973,7 +2973,7 @@ rest getfield(value, name::Symbol) Extract a named field from a `value` of composite type. The syntax `a.b` calls -`getfield(a, :b)`, and the syntax `a.(b)` calls `getfield(a, b)`. +`getfield(a, :b)`. """ getfield @@ -4175,7 +4175,7 @@ rot180(A, k) Element-wise less-than-or-equals comparison operator. """ -Base.(:(.<=)) +Base.:(.<=) """ checkbounds(array, indexes...) @@ -4525,7 +4525,7 @@ bkfact! Exponentiation operator. """ -Base.(:(^))(x, y) +Base.:(^)(x, y) """ ^(s, n) @@ -4537,7 +4537,7 @@ julia> "Test "^3 "Test Test Test " ``` """ -Base.(:(^))(s::AbstractString, n::Int) +Base.:(^)(s::AbstractString, n::Int) """ position(s) @@ -5363,7 +5363,7 @@ istaskdone Element-wise greater-than comparison operator. """ -Base.(:(.>)) +Base.:(.>) """ search(string, chars, [start]) @@ -5666,7 +5666,7 @@ get Element-wise not-equals comparison operator. """ -Base.(:(.!=)) +Base.:(.!=) """ lufact!(A) -> LU @@ -6231,7 +6231,7 @@ isdiag Equivalent to `!is(x, y)`. """ -Base.(:(!==)) +Base.:(!==) """ trailing_ones(x::Integer) -> Integer @@ -6735,7 +6735,7 @@ Collections should generally implement `==` by calling `==` recursively on all c New numeric types should implement this function for two arguments of the new type, and handle comparison to other types via promotion rules where possible. """ -Base.(:(==)) +Base.:(==) """ mapreducedim(f, op, A, dims[, initial]) @@ -7048,7 +7048,7 @@ diagm Element-wise subtraction operator. """ -Base.(:(.-)) +Base.:(.-) """ imag(z) @@ -7082,7 +7082,7 @@ arguments of the new type. Because of the behavior of floating-point NaN values, implements a partial order. Types with a canonical partial order should implement `<`, and types with a canonical total order should implement `isless`. """ -Base.(:(<)) +Base.:(<) """ EnvHash() -> EnvHash @@ -7654,7 +7654,7 @@ rpad setfield!(value, name::Symbol, x) Assign `x` to a named field in `value` of composite type. The syntax `a.b = c` calls -`setfield!(a, :b, c)`, and the syntax `a.(b) = c` calls `setfield!(a, b, c)`. +`setfield!(a, :b, c)`. """ setfield! @@ -7690,7 +7690,7 @@ countlines Matrix multiplication. """ -Base.(:(*))(::AbstractMatrix, ::AbstractMatrix) +Base.:(*)(::AbstractMatrix, ::AbstractMatrix) """ \\(A, B) @@ -7707,14 +7707,14 @@ When `A` is sparse, a similar polyalgorithm is used. For indefinite matrices, th factorization does not use pivoting during the numerical factorization and therefore the procedure can fail even for invertible matrices. """ -Base.(:(\))(A,B) +Base.:(\)(A,B) """ .\\(x, y) Element-wise left division operator. """ -Base.(:(.\))(x,y) +Base.:(.\)(x,y) """ \\(x, y) @@ -7722,7 +7722,7 @@ Base.(:(.\))(x,y) Left division operator: multiplication of `y` by the inverse of `x` on the left. Gives floating-point results for integer arguments. """ -Base.(:(\))(x::Number,y::Number) +Base.:(\)(x::Number,y::Number) """ @@ -7732,7 +7732,7 @@ Base.(:(\))(x::Number,y::Number) Multiplication operator. `x*y*z*...` calls this function with all arguments, i.e. `*(x, y, z, ...)`. """ -Base.(:(*))(x, y...) +Base.:(*)(x, y...) """ ``` @@ -7746,7 +7746,7 @@ julia> "Hello " * "world" "Hello world" ``` """ -Base.(:(*))(s::AbstractString, t::AbstractString) +Base.:(*)(s::AbstractString, t::AbstractString) """ complement!(s) @@ -8172,7 +8172,7 @@ ispunct Returns a tuple containing the dimensions of `A`. Optionally you can specify the dimension(s) you want the length of, and get the length of that dimension, or a tuple of the -lengths of dimensions you asked for.: +lengths of dimensions you asked for. julia> A = rand(2,3,4); @@ -8232,7 +8232,7 @@ airy Boolean not. """ -Base.(:(!)) +Base.:(!) """ length(A) -> Integer @@ -8325,7 +8325,7 @@ NullException Element-wise equality comparison operator. """ -Base.(:(.==)) +Base.:(.==) """ cfunction(function::Function, ReturnType::Type, (ArgumentTypes...)) @@ -8377,7 +8377,7 @@ intersect Not-equals comparison operator. Always gives the opposite answer as `==`. New types should generally not implement this, and rely on the fallback definition `!=(x,y) = !(x==y)` instead. """ -Base.(:(!=)) +Base.:(!=) """ @spawn @@ -8542,7 +8542,7 @@ isprime(::BigInt, ?) Greater-than comparison operator. Generally, new types should implement `<` instead of this function, and rely on the fallback definition `>(x,y) = y)) +Base.:(>) """ match(r::Regex, s::AbstractString[, idx::Integer[, addopts]]) @@ -8665,7 +8665,7 @@ isa Less-than-or-equals comparison operator. """ -Base.(:(<=)) +Base.:(<=) """ ProcessExitedException() @@ -9782,7 +9782,7 @@ midpoints Element-wise addition operator. """ -Base.(:(.+)) +Base.:(.+) """ reverseind(v, i) @@ -10235,7 +10235,7 @@ iswritable Bitwise or. """ -Base.(:(|)) +Base.:(|) """ yieldto(task, arg = nothing) @@ -10428,7 +10428,7 @@ enumerate Greater-than-or-equals comparison operator. """ -Base.(:(>=)) +Base.:(>=) """ dawson(x) @@ -10472,7 +10472,7 @@ colon(start, step, stop) Bitwise exclusive or. """ -Base.(:$)(x, y) +Base.:$(x, y) """ getsockname(sock::Union{TCPServer, TCPSocket}) -> (IPAddr,UInt16) diff --git a/base/inference.jl b/base/inference.jl index da0a9a906cf3f..a6606ccfad40f 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -2891,8 +2891,8 @@ function inlining_pass(e::Expr, sv, linfo) end if isdefined(Main, :Base) && - ((isdefined(Main.Base, :^) && is(f, Main.Base.(:^))) || - (isdefined(Main.Base, :.^) && is(f, Main.Base.(:.^)))) + ((isdefined(Main.Base, :^) && is(f, Main.Base.:^)) || + (isdefined(Main.Base, :.^) && is(f, Main.Base.:.^))) if length(e.args) == 3 && isa(e.args[3],Union{Int32,Int64}) a1 = e.args[2] basenumtype = Union{corenumtype, Main.Base.Complex64, Main.Base.Complex128, Main.Base.Rational} @@ -2900,10 +2900,10 @@ function inlining_pass(e::Expr, sv, linfo) exprtype(a1,sv) ⊑ basenumtype) if e.args[3]==2 e.args = Any[GlobalRef(Main.Base,:*), a1, a1] - f = Main.Base.(:*); ft = abstract_eval_constant(f) + f = Main.Base.:*; ft = abstract_eval_constant(f) elseif e.args[3]==3 e.args = Any[GlobalRef(Main.Base,:*), a1, a1, a1] - f = Main.Base.(:*); ft = abstract_eval_constant(f) + f = Main.Base.:*; ft = abstract_eval_constant(f) end end end diff --git a/base/interactiveutil.jl b/base/interactiveutil.jl index e7638186c6cc6..132cb59546fb3 100644 --- a/base/interactiveutil.jl +++ b/base/interactiveutil.jl @@ -588,7 +588,7 @@ function _summarysize(obj::ANY, seen, excl) ft = typeof(obj).types for i in 1:nfields(obj) if !isbits(ft[i]) && isdefined(obj,i) - val = obj.(i) + val = getfield(obj, i) if !isa(val,excl) size += summarysize(val, seen, excl)::Int end diff --git a/base/loading.jl b/base/loading.jl index 315f9cac5c69e..1325530f6991e 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -561,7 +561,7 @@ function stale_cachefile(modpath, cachefile) if !isdefined(Main, M) require(M) # should recursively recompile module M if stale end - if module_uuid(Main.(M)) != uuid + if module_uuid(getfield(Main, M)) != uuid return true end end diff --git a/base/pkg/resolve/fieldvalue.jl b/base/pkg/resolve/fieldvalue.jl index 3c710494f22a7..fe175ce356b0b 100644 --- a/base/pkg/resolve/fieldvalue.jl +++ b/base/pkg/resolve/fieldvalue.jl @@ -40,8 +40,8 @@ Base.zero(::Type{FieldValue}) = FieldValue() Base.typemin(::Type{FieldValue}) = (x=typemin(Int); y=typemin(VersionWeight); FieldValue(x,y,y,x,typemin(Int128))) -Base.(:-)(a::FieldValue, b::FieldValue) = FieldValue(a.l0-b.l0, a.l1-b.l1, a.l2-b.l2, a.l3-b.l3, a.l4-b.l4) -Base.(:+)(a::FieldValue, b::FieldValue) = FieldValue(a.l0+b.l0, a.l1+b.l1, a.l2+b.l2, a.l3+b.l3, a.l4+b.l4) +Base.:-(a::FieldValue, b::FieldValue) = FieldValue(a.l0-b.l0, a.l1-b.l1, a.l2-b.l2, a.l3-b.l3, a.l4-b.l4) +Base.:+(a::FieldValue, b::FieldValue) = FieldValue(a.l0+b.l0, a.l1+b.l1, a.l2+b.l2, a.l3+b.l3, a.l4+b.l4) function Base.isless(a::FieldValue, b::FieldValue) a.l0 < b.l0 && return true diff --git a/base/pkg/resolve/versionweight.jl b/base/pkg/resolve/versionweight.jl index ff62fc5774c83..c7007331494d8 100644 --- a/base/pkg/resolve/versionweight.jl +++ b/base/pkg/resolve/versionweight.jl @@ -18,8 +18,8 @@ Base.zero{T}(::Type{HierarchicalValue{T}}) = HierarchicalValue(T) Base.typemin{T}(::Type{HierarchicalValue{T}}) = HierarchicalValue(T[], typemin(T)) -for (bf,f) in ((:(:-), :-), (:(:+),:+)) - @eval function Base.($bf){T}(a::HierarchicalValue{T}, b::HierarchicalValue{T}) +for f in (:-, :+) + @eval function Base.$f{T}(a::HierarchicalValue{T}, b::HierarchicalValue{T}) av = a.v bv = b.v la = length(a.v) @@ -42,7 +42,7 @@ for (bf,f) in ((:(:-), :-), (:(:+),:+)) end end -Base.(:-){T}(a::HierarchicalValue{T}) = HierarchicalValue(-a.v, -a.rest) +Base.:-{T}(a::HierarchicalValue{T}) = HierarchicalValue(-a.v, -a.rest) function Base.cmp{T}(a::HierarchicalValue{T}, b::HierarchicalValue{T}) av = a.v @@ -81,10 +81,10 @@ Base.zero(::Type{VWPreBuildItem}) = VWPreBuildItem() Base.typemin(::Type{VWPreBuildItem}) = (x=typemin(Int); VWPreBuildItem(x, typemin(HierarchicalValue{Int}), x)) -Base.(:-)(a::VWPreBuildItem, b::VWPreBuildItem) = VWPreBuildItem(a.nonempty-b.nonempty, a.s-b.s, a.i-b.i) -Base.(:+)(a::VWPreBuildItem, b::VWPreBuildItem) = VWPreBuildItem(a.nonempty+b.nonempty, a.s+b.s, a.i+b.i) +Base.:-(a::VWPreBuildItem, b::VWPreBuildItem) = VWPreBuildItem(a.nonempty-b.nonempty, a.s-b.s, a.i-b.i) +Base.:+(a::VWPreBuildItem, b::VWPreBuildItem) = VWPreBuildItem(a.nonempty+b.nonempty, a.s+b.s, a.i+b.i) -Base.(:-)(a::VWPreBuildItem) = VWPreBuildItem(-a.nonempty, -a.s, -a.i) +Base.:-(a::VWPreBuildItem) = VWPreBuildItem(-a.nonempty, -a.s, -a.i) function Base.cmp(a::VWPreBuildItem, b::VWPreBuildItem) c = cmp(a.nonempty, b.nonempty); c != 0 && return c @@ -122,18 +122,18 @@ Base.zero(::Type{VWPreBuild}) = VWPreBuild() const _vwprebuild_min = VWPreBuild(typemin(Int), typemin(HierarchicalValue{VWPreBuildItem})) Base.typemin(::Type{VWPreBuild}) = _vwprebuild_min -function Base.(:-)(a::VWPreBuild, b::VWPreBuild) +function Base.:-(a::VWPreBuild, b::VWPreBuild) b === _vwprebuild_zero && return a a === _vwprebuild_zero && return -b VWPreBuild(a.nonempty-b.nonempty, a.w-b.w) end -function Base.(:+)(a::VWPreBuild, b::VWPreBuild) +function Base.:+(a::VWPreBuild, b::VWPreBuild) b === _vwprebuild_zero && return a a === _vwprebuild_zero && return b VWPreBuild(a.nonempty+b.nonempty, a.w+b.w) end -function Base.(:-)(a::VWPreBuild) +function Base.:-(a::VWPreBuild) a === _vwprebuild_zero && return a VWPreBuild(-a.nonempty, -a.w) end @@ -177,17 +177,17 @@ Base.zero(::Type{VersionWeight}) = VersionWeight() Base.typemin(::Type{VersionWeight}) = (x=typemin(Int); y=typemin(VWPreBuild); VersionWeight(x,x,x,y,y,x)) -Base.(:-)(a::VersionWeight, b::VersionWeight) = +Base.:-(a::VersionWeight, b::VersionWeight) = VersionWeight(a.major-b.major, a.minor-b.minor, a.patch-b.patch, a.prerelease-b.prerelease, a.build-b.build, a.uninstall-b.uninstall) -Base.(:+)(a::VersionWeight, b::VersionWeight) = +Base.:+(a::VersionWeight, b::VersionWeight) = VersionWeight(a.major+b.major, a.minor+b.minor, a.patch+b.patch, a.prerelease+b.prerelease, a.build+b.build, a.uninstall+b.uninstall) -Base.(:-)(a::VersionWeight) = +Base.:-(a::VersionWeight) = VersionWeight(-a.major, -a.minor, -a.patch, -a.prerelease, -a.build, -a.uninstall) diff --git a/base/precompile.jl b/base/precompile.jl index 6ff332beba9fa..00e48982dbdcf 100644 --- a/base/precompile.jl +++ b/base/precompile.jl @@ -108,7 +108,7 @@ precompile(Base.LineEdit.write_prompt, (Base.Terminals.TerminalBuffer, Base.Line precompile(Base.Multimedia.TextDisplay, (Base.TTY,)) precompile(Base.Multimedia.display, (Int,)) precompile(Base.ProcessGroup, (Int, Array{Any,1}, Array{Any,1})) -precompile(Base.REPL.(:(==)), (Base.REPL.REPLDisplay{Base.REPL.LineEditREPL}, Base.REPL.REPLDisplay{Base.REPL.LineEditREPL})) +precompile(Base.REPL.:(==), (Base.REPL.REPLDisplay{Base.REPL.LineEditREPL}, Base.REPL.REPLDisplay{Base.REPL.LineEditREPL})) precompile(Base.REPL.LineEditREPL, (Base.Terminals.TTYTerminal, Bool, String, String, String, String, String, Bool, Bool, Bool, Bool)) precompile(Base.REPL.LineEditREPL, (Base.Terminals.TTYTerminal,)) precompile(Base.REPL.REPLBackendRef, (Channel{Any}, Channel{Any})) diff --git a/base/show.jl b/base/show.jl index 3340d007ac5d5..6754e328fc5a7 100644 --- a/base/show.jl +++ b/base/show.jl @@ -96,7 +96,7 @@ function show_default(io::IO, x::ANY) if !isdefined(x, f) print(io, undef_ref_str) else - show(recur_io, x.(f)) + show(recur_io, getfield(x, f)) end if i < nf print(io, ',') diff --git a/examples/juliatypes.jl b/examples/juliatypes.jl index c0a07bfde5ea8..359dc7d5d9837 100644 --- a/examples/juliatypes.jl +++ b/examples/juliatypes.jl @@ -242,7 +242,7 @@ function issub_union(t, u::UnionT, env, R, state::UnionState) return true end ui = state.stack[state.depth]; state.depth += 1 - choice = u.(1+ui) + choice = getfield(u, 1+ui) return R ? issub(t, choice, env) : issub(choice, t, env) end diff --git a/src/julia-parser.scm b/src/julia-parser.scm index 5cdec04cf6924..6a2607824b206 100644 --- a/src/julia-parser.scm +++ b/src/julia-parser.scm @@ -1020,7 +1020,13 @@ (take-token s) (loop (cond ((eqv? (peek-token s) #\() - `(|.| ,ex ,(parse-atom s))) + (begin + (take-token s) + `(|.| ,ex (tuple ,@(parse-arglist s #\) ))))) + ((eqv? (peek-token s) ':) + (begin + (take-token s) + `(|.| ,ex (quote ,(parse-atom s))))) ((eq? (peek-token s) '$) (take-token s) (let ((dollarex (parse-atom s))) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index c767ec63bf7c0..ec1f4a7f52ce2 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1528,8 +1528,14 @@ ,(expand-forms (last e))))))) '|.| - (lambda (e) - `(call (top getfield) ,(expand-forms (cadr e)) ,(expand-forms (caddr e)))) + (lambda (e) ; e = (|.| f x) + (let ((f (expand-forms (cadr e))) + (x (expand-forms (caddr e)))) + (if (or (eq? (car x) 'quote) (eq? (car x) 'inert) (eq? (car x) '$)) + `(call (top getfield) ,f ,x) + ; otherwise, came from f.(args...) --> broadcast(f, args...), + ; where x = (call (top tuple) args...) at this point: + `(call broadcast ,f ,@(cddr x))))) '|<:| syntactic-op-to-call '|>:| syntactic-op-to-call diff --git a/test/core.jl b/test/core.jl index c629ba96aa274..9e63194a98209 100644 --- a/test/core.jl +++ b/test/core.jl @@ -2008,12 +2008,13 @@ f7652() = issubtype(fieldtype(t_a7652, :a), Int) g7652() = fieldtype(DataType, :types) @test g7652() == fieldtype(DataType, :types) == SimpleVector @test fieldtype(t_a7652, 1) == Int -h7652() = a7652.(1) = 2 +h7652() = setfield!(a7652, 1, 2) h7652() @test a7652.a == 2 -i7652() = a7652.(1) = 3.0 -i7652() -@test a7652.a == 3 +# commented out due to issue #16195: setfield! does not perform conversions +# i7652() = setfield!(a7652, 1, 3.0) +# i7652() +# @test a7652.a == 3 # issue #7679 @test map(f->f(), Any[ ()->i for i=1:3 ]) == Any[1,2,3] @@ -2205,8 +2206,8 @@ end @test try; [][]; catch ex; isempty((ex::BoundsError).a::Array{Any,1}) && ex.i == (1,); end @test try; [][1,2]; catch ex; isempty((ex::BoundsError).a::Array{Any,1}) && ex.i == (1,2); end @test try; [][10]; catch ex; isempty((ex::BoundsError).a::Array{Any,1}) && ex.i == (10,); end -f9534a() = (a=1+2im; a.(-100)) -f9534a(x) = (a=1+2im; a.(x)) +f9534a() = (a=1+2im; getfield(a, -100)) +f9534a(x) = (a=1+2im; getfield(a, x)) @test try; f9534a() catch ex; (ex::BoundsError).a === 1+2im && ex.i == -100; end @test try; f9534a(3) catch ex; (ex::BoundsError).a === 1+2im && ex.i == 3; end f9534b() = (a=(1,2.,""); a[5]) @@ -2221,10 +2222,10 @@ f9534d() = (a=(1,2,4,6,7); a[7]) f9534d(x) = (a=(1,2,4,6,7); a[x]) @test try; f9534d() catch ex; (ex::BoundsError).a === (1,2,4,6,7) && ex.i == 7; end @test try; f9534d(-1) catch ex; (ex::BoundsError).a === (1,2,4,6,7) && ex.i == -1; end -f9534e(x) = (a=IOBuffer(); a.(x) = 3) -@test try; f9534e(-2) catch ex; is((ex::BoundsError).a,Base.IOBuffer) && ex.i == -2; end -f9534f() = (a=IOBuffer(); a.(-2)) -f9534f(x) = (a=IOBuffer(); a.(x)) +f9534e(x) = (a=IOBuffer(); setfield!(a, x, 3)) +@test try; f9534e(-2) catch ex; isa((ex::BoundsError).a,Base.IOBuffer) && ex.i == -2; end +f9534f() = (a=IOBuffer(); getfield(a, -2)) +f9534f(x) = (a=IOBuffer(); getfield(a, x)) @test try; f9534f() catch ex; isa((ex::BoundsError).a,Base.IOBuffer) && ex.i == -2; end @test try; f9534f(typemin(Int)+2) catch ex; isa((ex::BoundsError).a,Base.IOBuffer) && ex.i == typemin(Int)+2; end x9634 = 3 @@ -3619,6 +3620,10 @@ end end, Function) +# f.(x) vectorization syntax (#15032) +@test (x -> 2x).([1,2,3]) == [2,4,6] +@test ((x,y) -> 2x+y^2).([1,2,3],[3,4,5]) == [1,2,3]*2 + [3,4,5].^2 + # let syntax with multiple lhs let z = (3,9,42) let (a,b,c) = z diff --git a/test/replutil.jl b/test/replutil.jl index ba529e088e5dc..e826d4d39e7c7 100644 --- a/test/replutil.jl +++ b/test/replutil.jl @@ -250,7 +250,7 @@ let err_str, @test contains(err_str, "MethodError: objects of type Array{Float64,1} are not callable") end @test stringmime("text/plain", FunctionLike()) == "(::FunctionLike) (generic function with 0 methods)" -@test ismatch(r"^@doc \(macro with \d+ method[s]?\)$", stringmime("text/plain", Base.(Symbol("@doc")))) +@test ismatch(r"^@doc \(macro with \d+ method[s]?\)$", stringmime("text/plain", getfield(Base, Symbol("@doc")))) method_defs_lineno = @__LINE__ Base.Symbol() = throw(ErrorException("1")) @@ -276,7 +276,7 @@ let err_str, @test sprint(show, which(EightBitTypeT, Tuple{})) == "(::Type{EightBitTypeT})() at $sp:$(method_defs_lineno + 4)" @test sprint(show, which(EightBitTypeT{Int32}, Tuple{})) == "(::Type{EightBitTypeT{T}}){T}() at $sp:$(method_defs_lineno + 5)" @test sprint(show, which(reinterpret(EightBitTypeT{Int32}, 0x54), Tuple{})) == "(::EightBitTypeT)() at $sp:$(method_defs_lineno + 6)" - @test startswith(sprint(show, which(Base.(Symbol("@doc")), Tuple{Vararg{Any}})), "@doc(x...) at boot.jl:") + @test startswith(sprint(show, which(getfield(Base, Symbol("@doc")), Tuple{Vararg{Any}})), "@doc(x...) at boot.jl:") @test startswith(sprint(show, which(FunctionLike(), Tuple{})), "(::FunctionLike)() at $sp:$(method_defs_lineno + 7)") @test stringmime("text/plain", FunctionLike()) == "(::FunctionLike) (generic function with 1 method)" @test stringmime("text/plain", Core.arraysize) == "arraysize (built-in function)" diff --git a/test/unicode/utf8proc.jl b/test/unicode/utf8proc.jl index 2fd82cd917ae8..6ba0ac6f4e812 100644 --- a/test/unicode/utf8proc.jl +++ b/test/unicode/utf8proc.jl @@ -25,7 +25,7 @@ #1. Canonical equivalence let ==(a::Array{Char},b::Array{Char}) = normalize_string(string(a...), :NFC)==normalize_string(string(b...), :NFC) - ==(a,b) = Base.(:(==))(a,b) + ==(a,b) = Base.:(==)(a,b) @test ['C', '̧'] == ['Ç'] @test ['q', '̇', '̣'] == ['q', '̣', '̇'] @test ['가'] == ['ᄀ', 'ᅡ'] @@ -34,7 +34,7 @@ end #2. Compatibility Equivalence let ==(a::Array{Char},b::Array{Char}) = normalize_string(string(a...), :NFKC)==normalize_string(string(b...), :NFKC) - ==(a,b) = Base.(:(==))(a,b) + ==(a,b) = Base.:(==)(a,b) @test ['ℌ'] == ['ℍ'] == ['H'] @test ['ﻨ'] == ['ﻧ'] == ['ﻦ'] == ['ﻥ'] @test ['①'] == ['1'] From 682547a48ab14258c907e87d297a0db400f0c53a Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Wed, 4 May 2016 12:26:14 -0400 Subject: [PATCH 2/6] deprecate Base.(:+) etc. --- base/deprecated.jl | 18 ++++++++++++++++++ src/julia-syntax.scm | 19 ++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/base/deprecated.jl b/base/deprecated.jl index 331e483161010..52eed18251f20 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1093,6 +1093,24 @@ end #15995 @deprecate symbol Symbol +#15032: Expressions like Base.(:+) now call broadcast. Since there is unlikely +# to be any legitimate reason to broadcast to a Module (not iterable!), +# it should be safe to catch this with a deprecation method. +# Note: this deprecation does not handle method definitions Base.(:+)(...) = ..., +# which are handled by the deprecate-dotparen function in julia-syntax.scm +# More generally, expressions like x.(sym) for any Symbol turn into broadcast +# but should really be getfield, and since you can't broadcast to a symbol +# using a deprecated broadcast method should be safe. [Unfortunately, +# we can't catch x.(i::Int) in the same way, because broadcasting to an +# integer is a valid use of broadcast Most such usages will simply +# give an error, since x is probably not callable if getfield was intended.] +function broadcast(m::Module, s::Symbol) + depwarn("$m.(:$s) is deprecated; use $m.:$s or getfield($m, :$s) instead.", :broadcast) + getfield(m, s) +end +@deprecate broadcast(x::Any, s::Symbol) getfield(x, s) + +#16167 macro ccallable(def) depwarn("@ccallable requires a return type", Symbol("@ccallable")) if isa(def,Expr) && (def.head === :(=) || def.head === :function) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index ec1f4a7f52ce2..80862ca13d6be 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -222,6 +222,23 @@ (pair? (caddr e)) (memq (car (caddr e)) '(quote inert)) (symbol? (cadr (caddr e)))))) +;; e.g. Base.(:+) is deprecated in favor of Base.:+ +(define (deprecate-dotparen e) + (if (and (length= e 3) (eq? (car e) '|.|) + (or (atom? (cadr e)) (sym-ref? (cadr e))) + (length= (caddr e) 2) (eq? (caaddr e) 'tuple) + (pair? (cadr (caddr e))) (memq (caadr (caddr e)) '(quote inert))) + (let* ((s_ (cdadr (caddr e))) + (s (if (symbol? s_) s_ + (if (and (length= s_ 1) (symbol? (car s_))) (car s_) #f)))) + (if s + (let ((newe (list (car e) (cadr e) (cadr (caddr e))))) + (syntax-deprecation #f (string (deparse (cadr e)) ".(:" s ")") + (string (deparse (cadr e)) ".:" s)) + newe) + e)) + e)) + ;; convert final (... x) to (curly Vararg x) (define (dots->vararg a) (if (null? a) a @@ -873,7 +890,7 @@ (let* ((head (cadr name)) (argl (cddr name)) (has-sp (and (pair? head) (eq? (car head) 'curly))) - (name (if has-sp (cadr head) head)) + (name (deprecate-dotparen (if has-sp (cadr head) head))) (sparams (if has-sp (cddr head) '())) (isstaged (eq? (car e) 'stagedfunction)) (adj-decl (lambda (n) (if (and (decl? n) (length= n 2)) From 3e0f74bb69c94b58ac87519943f02f75f0f6166d Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 5 May 2016 13:05:55 -0400 Subject: [PATCH 3/6] documentation updates for new f.(args...) syntax --- NEWS.md | 15 +++++++++++-- doc/manual/arrays.rst | 4 +++- doc/manual/functions.rst | 30 ++++++++++++++++++++++++++ doc/manual/mathematical-operations.rst | 7 ++++-- doc/stdlib/arrays.rst | 2 +- doc/stdlib/base.rst | 4 ++-- 6 files changed, 54 insertions(+), 8 deletions(-) diff --git a/NEWS.md b/NEWS.md index d05757004ca62..24be9ebbd9f1d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,9 +4,11 @@ Julia v0.5.0 Release Notes New language features --------------------- - * Generator expressions, e.g. `f(i) for i in 1:n` (#4470). This returns an iterator + * Generator expressions, e.g. `f(i) for i in 1:n` ([#4470]). This returns an iterator that computes the specified values on demand. + * Broadcasting syntax: ``f.(args...)`` is equivalent to ``broadcast(f, args...)`` ([#15032]). + * Macro expander functions are now generic, so macros can have multiple definitions (e.g. for different numbers of arguments, or optional arguments) ([#8846], [#9627]). However note that the argument types refer to the syntax tree representation, and not @@ -20,6 +22,8 @@ New language features * `PROGRAM_FILE` global is now available for determining the name of the running script ([#14114]). + * The syntax `x.:sym` (e.g. `Base.:+`) is now supported, and `x.(:sym)` is deprecated ([#15032]). + Language changes ---------------- @@ -62,6 +66,11 @@ Breaking changes instead, a call that cannot be resolved to a single method results in a `MethodError`. ([#6190]) + * The syntax `x.(i::Integer)` for `getfield(x,i)` now means `broadcast(x,i)`, + and `x.(f) = v` for `setfield!(x, f, v)` no longer works. + (`x.(::Symbol)` still works for `getfield` but is deprecated.) + Use `getfield` or `setfield!` ([#15032]). + * `pmap` keyword arguments `err_retry=true` and `err_stop=false` are deprecated. `pmap` no longer retries or returns `Exception` objects in the result collection. `pmap(retry(f), c)` or `pmap(@catch(f), c)` can be used instead. @@ -181,6 +190,7 @@ Deprecated or removed [#4163]: https://github.com/JuliaLang/julia/issues/4163 [#4211]: https://github.com/JuliaLang/julia/issues/4211 +[#4470]: https://github.com/JuliaLang/julia/issues/4470 [#6190]: https://github.com/JuliaLang/julia/issues/6190 [#8036]: https://github.com/JuliaLang/julia/issues/8036 [#8846]: https://github.com/JuliaLang/julia/issues/8846 @@ -211,11 +221,12 @@ Deprecated or removed [#14469]: https://github.com/JuliaLang/julia/issues/14469 [#14759]: https://github.com/JuliaLang/julia/issues/14759 [#14798]: https://github.com/JuliaLang/julia/issues/14798 +[#15032]: https://github.com/JuliaLang/julia/issues/15032 [#15192]: https://github.com/JuliaLang/julia/issues/15192 [#15242]: https://github.com/JuliaLang/julia/issues/15242 [#15258]: https://github.com/JuliaLang/julia/issues/15258 [#15409]: https://github.com/JuliaLang/julia/issues/15409 -[#15430]: https://github.com/JuliaLang/julia/issues/15431 +[#15431]: https://github.com/JuliaLang/julia/issues/15431 [#15550]: https://github.com/JuliaLang/julia/issues/15550 [#15609]: https://github.com/JuliaLang/julia/issues/15609 [#15763]: https://github.com/JuliaLang/julia/issues/15763 diff --git a/doc/manual/arrays.rst b/doc/manual/arrays.rst index 974636fa86557..ee85263e9a273 100644 --- a/doc/manual/arrays.rst +++ b/doc/manual/arrays.rst @@ -509,6 +509,8 @@ the name of the function to vectorize. Here is a simple example: 1 4 16 25 36 49 +.. _man-broadcasting: + Broadcasting ------------ @@ -548,7 +550,7 @@ function elementwise: 1.71056 0.847604 1.73659 0.873631 -Elementwise operators such as ``.+`` and ``.*`` perform broadcasting if necessary. There is also a :func:`broadcast!` function to specify an explicit destination, and :func:`broadcast_getindex` and :func:`broadcast_setindex!` that broadcast the indices before indexing. +Elementwise operators such as ``.+`` and ``.*`` perform broadcasting if necessary. There is also a :func:`broadcast!` function to specify an explicit destination, and :func:`broadcast_getindex` and :func:`broadcast_setindex!` that broadcast the indices before indexing. Moreover, ``f.(args...)`` is equivalent to ``broadcast(f, args...)``, providing a convenient syntax to broadcast any function (:ref:`man-dot-vectorizing`:.). Implementation -------------- diff --git a/doc/manual/functions.rst b/doc/manual/functions.rst index ebef984ac9eb2..786f9f12d1c8e 100644 --- a/doc/manual/functions.rst +++ b/doc/manual/functions.rst @@ -605,6 +605,36 @@ With the ``do`` block syntax, it helps to check the documentation or implementation to know how the arguments of the user function are initialized. +.. _man-dot-vectorizing: + +Dot Syntax for Vectorizing Functions +------------------------------------ + +In technical-computing languages, it is common to have "vectorized" versions of +functions, which simply apply a given function ``f(x)`` to each element of an +array ``A`` to yield a new array via ``f(A)``. This kind of syntax is +convenient for data processing, but in other languages vectorization is also +often required for performance: if loops are slow, the "vectorized" version of a +function can call fast library code written in a low-level language. In Julia, +vectorized functions are *not* required for performance, and indeed it is often +beneficial to write your own loops (:ref:`man-performance-tips`:), but they can +still be convenient. Therefore, *any* Julia function ``f`` can be applied +elementwise to any array (or other collection) with the syntax ``f.(A)``. + +Of course, you can omit the dot if you write a specialized "vector" method +of ``f``, e.g. via ``f(A::AbstractArray) = map(f, A)``, and this is just +as efficient as ``f.(A)``. But that approach requires you to decide in advance +which functions you want to vectorize. + +More generally, ``f.(args...)`` is actually equivalent to +``broadcast(f, args...)``, which allows you to operate on multiple +arrays (even of different shapes), or a mix of arrays and scalars +(:ref:`man-broadcasting`:). For example, if you have ``f(x,y) = 3x + 4y``, +then ``f.(pi,A)`` will return a new array consisting of ``f(pi,a)`` for each +``a`` in ``A``, and ``f.(vector1,vector2)`` will return a new vector +consisting of ``f(vector1[i],vector2[i])`` for each index ``i`` +(throwing an exception if the vectors have different length). + Further Reading --------------- diff --git a/doc/manual/mathematical-operations.rst b/doc/manual/mathematical-operations.rst index be2aaffe0f320..257588a1b75b1 100644 --- a/doc/manual/mathematical-operations.rst +++ b/doc/manual/mathematical-operations.rst @@ -426,6 +426,11 @@ class of numerical values as permit sensible definitions, including integers, floating-point numbers, rationals, and complexes, wherever such definitions make sense. +Moreover, these functions (like any Julia function) can be applied +in "vectorized" fashion to arrays and other collections with the +syntax ``f.(A)``, e.g. ``sin.(A)`` will compute the elementwise +sine of each element of an array ``A``. See :ref:`man-dot-vectorizing`:. + .. _man-rounding-functions: Rounding functions @@ -579,5 +584,3 @@ Function Description .. |airylist| replace:: :func:`airy(z) `, :func:`airyai(z) `, ``airy(0,z)`` .. |airyprimelist| replace:: :func:`airyprime(z) `, :func:`airyaiprime(z) `, ``airy(1,z)`` - - diff --git a/doc/stdlib/arrays.rst b/doc/stdlib/arrays.rst index 436c88282f002..45f1140a3b6f5 100644 --- a/doc/stdlib/arrays.rst +++ b/doc/stdlib/arrays.rst @@ -19,7 +19,7 @@ Basic functions .. Docstring generated from Julia source - Returns a tuple containing the dimensions of ``A``\ . Optionally you can specify the dimension(s) you want the length of, and get the length of that dimension, or a tuple of the lengths of dimensions you asked for.: + Returns a tuple containing the dimensions of ``A``\ . Optionally you can specify the dimension(s) you want the length of, and get the length of that dimension, or a tuple of the lengths of dimensions you asked for. .. code-block:: julia diff --git a/doc/stdlib/base.rst b/doc/stdlib/base.rst index 5d47c1f0f2dfa..0515d44de61ff 100644 --- a/doc/stdlib/base.rst +++ b/doc/stdlib/base.rst @@ -514,13 +514,13 @@ Types .. Docstring generated from Julia source - Extract a named field from a ``value`` of composite type. The syntax ``a.b`` calls ``getfield(a, :b)``\ , and the syntax ``a.(b)`` calls ``getfield(a, b)``\ . + Extract a named field from a ``value`` of composite type. The syntax ``a.b`` calls ``getfield(a, :b)``\ . .. function:: setfield!(value, name::Symbol, x) .. Docstring generated from Julia source - Assign ``x`` to a named field in ``value`` of composite type. The syntax ``a.b = c`` calls ``setfield!(a, :b, c)``\ , and the syntax ``a.(b) = c`` calls ``setfield!(a, b, c)``\ . + Assign ``x`` to a named field in ``value`` of composite type. The syntax ``a.b = c`` calls ``setfield!(a, :b, c)``\ . .. function:: fieldoffset(type, i) From e847332defff98f042e9374ea272aae3f021da5e Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 5 May 2016 14:10:48 -0400 Subject: [PATCH 4/6] syntax deprecation warning for x.(f) = ..., recommending setfield! --- NEWS.md | 7 +++---- src/julia-syntax.scm | 15 +++++++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/NEWS.md b/NEWS.md index 24be9ebbd9f1d..e9d1da0269fbb 100644 --- a/NEWS.md +++ b/NEWS.md @@ -66,10 +66,9 @@ Breaking changes instead, a call that cannot be resolved to a single method results in a `MethodError`. ([#6190]) - * The syntax `x.(i::Integer)` for `getfield(x,i)` now means `broadcast(x,i)`, - and `x.(f) = v` for `setfield!(x, f, v)` no longer works. - (`x.(::Symbol)` still works for `getfield` but is deprecated.) - Use `getfield` or `setfield!` ([#15032]). + * The syntax `x.(i::Integer)` for `getfield(x,i)` now means `broadcast(x,i)`. + [`x.(s::Symbol)` still works for `getfield(x,s)`, and `x.(f) = v` works for `setfield!(x, f, v)`, but these are deprecated.] + Use `getfield` or `setfield!` instead ([#15032]). * `pmap` keyword arguments `err_retry=true` and `err_stop=false` are deprecated. `pmap` no longer retries or returns `Exception` objects in the result collection. diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 80862ca13d6be..a1fc8e121a189 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1587,16 +1587,23 @@ lhss) ,rr)))))) ((symbol-like? lhs) - `(= ,(cadr e) ,(expand-forms (caddr e)))) + `(= ,lhs ,(expand-forms (caddr e)))) ((atom? lhs) (error (string "invalid assignment location \"" (deparse lhs) "\""))) (else (case (car lhs) ((|.|) ;; a.b = - (let ((a (cadr (cadr e))) - (b (caddr (cadr e))) - (rhs (caddr e))) + (let* ((a (cadr lhs)) + (b_ (caddr lhs)) + (b (if (and (length= b_ 2) (eq? (car b_) 'tuple)) + (begin + (syntax-deprecation #f + (string (deparse a) ".(" (deparse (cadr b_)) ") = ...") + (string "setfield!(" (deparse a) ", " (deparse (cadr b_)) ", ...)")) + (cadr b_)) + b_)) + (rhs (caddr e))) (let ((aa (if (symbol-like? a) a (make-ssavalue))) (bb (if (or (atom? b) (symbol-like? b) (and (pair? b) (quoted? b))) b (make-ssavalue))) From fe53f212436df898445c4b53b1bf019a20067100 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 5 May 2016 14:27:11 -0400 Subject: [PATCH 5/6] expand deprecation warnings to cover all uses of x.(i) for getfield(x, i); this PR should no longer be a breaking change --- NEWS.md | 4 ---- base/deprecated.jl | 24 ++++++++++++------------ 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/NEWS.md b/NEWS.md index e9d1da0269fbb..97e1f44cb1d68 100644 --- a/NEWS.md +++ b/NEWS.md @@ -66,10 +66,6 @@ Breaking changes instead, a call that cannot be resolved to a single method results in a `MethodError`. ([#6190]) - * The syntax `x.(i::Integer)` for `getfield(x,i)` now means `broadcast(x,i)`. - [`x.(s::Symbol)` still works for `getfield(x,s)`, and `x.(f) = v` works for `setfield!(x, f, v)`, but these are deprecated.] - Use `getfield` or `setfield!` instead ([#15032]). - * `pmap` keyword arguments `err_retry=true` and `err_stop=false` are deprecated. `pmap` no longer retries or returns `Exception` objects in the result collection. `pmap(retry(f), c)` or `pmap(@catch(f), c)` can be used instead. diff --git a/base/deprecated.jl b/base/deprecated.jl index 52eed18251f20..cc129d71b1de6 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1093,22 +1093,22 @@ end #15995 @deprecate symbol Symbol -#15032: Expressions like Base.(:+) now call broadcast. Since there is unlikely -# to be any legitimate reason to broadcast to a Module (not iterable!), -# it should be safe to catch this with a deprecation method. -# Note: this deprecation does not handle method definitions Base.(:+)(...) = ..., -# which are handled by the deprecate-dotparen function in julia-syntax.scm -# More generally, expressions like x.(sym) for any Symbol turn into broadcast -# but should really be getfield, and since you can't broadcast to a symbol -# using a deprecated broadcast method should be safe. [Unfortunately, -# we can't catch x.(i::Int) in the same way, because broadcasting to an -# integer is a valid use of broadcast Most such usages will simply -# give an error, since x is probably not callable if getfield was intended.] +#15032: Expressions like Base.(:+) now call broadcast. Since calls +# to broadcast(x, ::Symbol) are unheard of, and broadcast(x, ::Integer) +# are unlikely, we can treat these as deprecated getfield calls. +function broadcast(x::Any, i::Union{Integer,Symbol}) + depwarn("x.(i) is deprecated; use getfield(x, i) instead.", :broadcast) + getfield(x, i) +end +# clearer to be more explicit in the warning for the Module case function broadcast(m::Module, s::Symbol) depwarn("$m.(:$s) is deprecated; use $m.:$s or getfield($m, :$s) instead.", :broadcast) getfield(m, s) end -@deprecate broadcast(x::Any, s::Symbol) getfield(x, s) +# expressions like f.(3) should still call broadcast for f::Function, +# and in general broadcast should work for scalar arguments, while +# getfield is certainly not intended for the case of f::Function. +broadcast(f::Function, i::Integer) = invoke(broadcast, (Function, Number), f, i) #16167 macro ccallable(def) From 8ad0eab4d5e53c27b8c2a62dd7e22b99d8d2f3dd Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Fri, 6 May 2016 10:16:23 -0400 Subject: [PATCH 6/6] tests for f.(args...) syntax --- test/broadcast.jl | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/broadcast.jl b/test/broadcast.jl index 4bf1260b759e5..f20ce3cb977c4 100644 --- a/test/broadcast.jl +++ b/test/broadcast.jl @@ -117,6 +117,17 @@ rt = Base.return_types(broadcast, Tuple{Function, Array{Float64, 3}, Array{Int, rt = Base.return_types(broadcast!, Tuple{Function, Array{Float64, 3}, Array{Float64, 3}, Array{Int, 1}}) @test length(rt) == 1 && rt[1] == Array{Float64, 3} +# f.(args...) syntax (#15032) +let x = [1,3.2,4.7], y = [3.5, pi, 1e-4], α = 0.2342 + @test sin.(x) == broadcast(sin, x) + @test sin.(α) == broadcast(sin, α) + @test factorial.(3) == broadcast(factorial, 3) + @test atan2.(x, y) == broadcast(atan2, x, y) + @test atan2.(x, y') == broadcast(atan2, x, y') + @test atan2.(x, α) == broadcast(atan2, x, α) + @test atan2.(α, y') == broadcast(atan2, α, y') +end + # issue 14725 let a = Number[2, 2.0, 4//2, 2+0im] / 2 @test eltype(a) == Number