278 lines
46 KiB
HTML
278 lines
46 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width, initial-scale=1.0"/><title>Methods · The Julia Language</title><script>(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||
|
||
ga('create', 'UA-28835595-6', 'auto');
|
||
ga('send', 'pageview');
|
||
</script><link href="https://cdnjs.cloudflare.com/ajax/libs/normalize/4.2.0/normalize.min.css" rel="stylesheet" type="text/css"/><link href="https://fonts.googleapis.com/css?family=Lato|Roboto+Mono" rel="stylesheet" type="text/css"/><link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet" type="text/css"/><link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css" rel="stylesheet" type="text/css"/><script>documenterBaseURL=".."</script><script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.2.0/require.min.js" data-main="../assets/documenter.js"></script><script src="../siteinfo.js"></script><script src="../../versions.js"></script><link href="../assets/documenter.css" rel="stylesheet" type="text/css"/><link href="../assets/julia-manual.css" rel="stylesheet" type="text/css"/></head><body><nav class="toc"><a href="../index.html"><img class="logo" src="../assets/logo.png" alt="The Julia Language logo"/></a><h1>The Julia Language</h1><select id="version-selector" onChange="window.location.href=this.value" style="visibility: hidden"></select><form class="search" id="search-form" action="../search.html"><input id="search-query" name="q" type="text" placeholder="Search docs"/></form><ul><li><a class="toctext" href="../index.html">Home</a></li><li><span class="toctext">Manual</span><ul><li><a class="toctext" href="introduction.html">Introduction</a></li><li><a class="toctext" href="getting-started.html">Getting Started</a></li><li><a class="toctext" href="variables.html">Variables</a></li><li><a class="toctext" href="integers-and-floating-point-numbers.html">Integers and Floating-Point Numbers</a></li><li><a class="toctext" href="mathematical-operations.html">Mathematical Operations and Elementary Functions</a></li><li><a class="toctext" href="complex-and-rational-numbers.html">Complex and Rational Numbers</a></li><li><a class="toctext" href="strings.html">Strings</a></li><li><a class="toctext" href="functions.html">Functions</a></li><li><a class="toctext" href="control-flow.html">Control Flow</a></li><li><a class="toctext" href="variables-and-scoping.html">Scope of Variables</a></li><li><a class="toctext" href="types.html">Types</a></li><li class="current"><a class="toctext" href="methods.html">Methods</a><ul class="internal"><li><a class="toctext" href="#Defining-Methods-1">Defining Methods</a></li><li><a class="toctext" href="#man-ambiguities-1">Method Ambiguities</a></li><li><a class="toctext" href="#Parametric-Methods-1">Parametric Methods</a></li><li><a class="toctext" href="#Redefining-Methods-1">Redefining Methods</a></li><li><a class="toctext" href="#Parametrically-constrained-Varargs-methods-1">Parametrically-constrained Varargs methods</a></li><li><a class="toctext" href="#Note-on-Optional-and-keyword-Arguments-1">Note on Optional and keyword Arguments</a></li><li><a class="toctext" href="#Function-like-objects-1">Function-like objects</a></li><li><a class="toctext" href="#Empty-generic-functions-1">Empty generic functions</a></li><li><a class="toctext" href="#man-method-design-ambiguities-1">Method design and the avoidance of ambiguities</a></li></ul></li><li><a class="toctext" href="constructors.html">Constructors</a></li><li><a class="toctext" href="conversion-and-promotion.html">Conversion and Promotion</a></li><li><a class="toctext" href="interfaces.html">Interfaces</a></li><li><a class="toctext" href="modules.html">Modules</a></li><li><a class="toctext" href="documentation.html">Documentation</a></li><li><a class="toctext" href="metaprogramming.html">Metaprogramming</a></li><li><a class="toctext" href="arrays.html">Multi-dimensional Arrays</a></li><li><a class="toctext" href="linear-algebra.html">Linear algebra</a></li><li><a class="toctext" href="networking-and-streams.html">Networking and Streams</a></li><li><a class="toctext" href="parallel-computing.html">Parallel Computing</a></li><li><a class="toctext" href="dates.html">Date and DateTime</a></li><li><a class="toctext" href="interacting-with-julia.html">Interacting With Julia</a></li><li><a class="toctext" href="running-external-programs.html">Running External Programs</a></li><li><a class="toctext" href="calling-c-and-fortran-code.html">Calling C and Fortran Code</a></li><li><a class="toctext" href="handling-operating-system-variation.html">Handling Operating System Variation</a></li><li><a class="toctext" href="environment-variables.html">Environment Variables</a></li><li><a class="toctext" href="embedding.html">Embedding Julia</a></li><li><a class="toctext" href="packages.html">Packages</a></li><li><a class="toctext" href="profile.html">Profiling</a></li><li><a class="toctext" href="stacktraces.html">Stack Traces</a></li><li><a class="toctext" href="performance-tips.html">Performance Tips</a></li><li><a class="toctext" href="workflow-tips.html">Workflow Tips</a></li><li><a class="toctext" href="style-guide.html">Style Guide</a></li><li><a class="toctext" href="faq.html">Frequently Asked Questions</a></li><li><a class="toctext" href="noteworthy-differences.html">Noteworthy Differences from other Languages</a></li><li><a class="toctext" href="unicode-input.html">Unicode Input</a></li></ul></li><li><span class="toctext">Standard Library</span><ul><li><a class="toctext" href="../stdlib/base.html">Essentials</a></li><li><a class="toctext" href="../stdlib/collections.html">Collections and Data Structures</a></li><li><a class="toctext" href="../stdlib/math.html">Mathematics</a></li><li><a class="toctext" href="../stdlib/numbers.html">Numbers</a></li><li><a class="toctext" href="../stdlib/strings.html">Strings</a></li><li><a class="toctext" href="../stdlib/arrays.html">Arrays</a></li><li><a class="toctext" href="../stdlib/parallel.html">Tasks and Parallel Computing</a></li><li><a class="toctext" href="../stdlib/linalg.html">Linear Algebra</a></li><li><a class="toctext" href="../stdlib/constants.html">Constants</a></li><li><a class="toctext" href="../stdlib/file.html">Filesystem</a></li><li><a class="toctext" href="../stdlib/io-network.html">I/O and Network</a></li><li><a class="toctext" href="../stdlib/punctuation.html">Punctuation</a></li><li><a class="toctext" href="../stdlib/sort.html">Sorting and Related Functions</a></li><li><a class="toctext" href="../stdlib/pkg.html">Package Manager Functions</a></li><li><a class="toctext" href="../stdlib/dates.html">Dates and Time</a></li><li><a class="toctext" href="../stdlib/iterators.html">Iteration utilities</a></li><li><a class="toctext" href="../stdlib/test.html">Unit Testing</a></li><li><a class="toctext" href="../stdlib/c.html">C Interface</a></li><li><a class="toctext" href="../stdlib/libc.html">C Standard Library</a></li><li><a class="toctext" href="../stdlib/libdl.html">Dynamic Linker</a></li><li><a class="toctext" href="../stdlib/profile.html">Profiling</a></li><li><a class="toctext" href="../stdlib/stacktraces.html">StackTraces</a></li><li><a class="toctext" href="../stdlib/simd-types.html">SIMD Support</a></li></ul></li><li><span class="toctext">Developer Documentation</span><ul><li><a class="toctext" href="../devdocs/reflection.html">Reflection and introspection</a></li><li><span class="toctext">Documentation of Julia's Internals</span><ul><li><a class="toctext" href="../devdocs/init.html">Initialization of the Julia runtime</a></li><li><a class="toctext" href="../devdocs/ast.html">Julia ASTs</a></li><li><a class="toctext" href="../devdocs/types.html">More about types</a></li><li><a class="toctext" href="../devdocs/object.html">Memory layout of Julia Objects</a></li><li><a class="toctext" href="../devdocs/eval.html">Eval of Julia code</a></li><li><a class="toctext" href="../devdocs/callconv.html">Calling Conventions</a></li><li><a class="toctext" href="../devdocs/compiler.html">High-level Overview of the Native-Code Generation Process</a></li><li><a class="toctext" href="../devdocs/functions.html">Julia Functions</a></li><li><a class="toctext" href="../devdocs/cartesian.html">Base.Cartesian</a></li><li><a class="toctext" href="../devdocs/meta.html">Talking to the compiler (the <code>:meta</code> mechanism)</a></li><li><a class="toctext" href="../devdocs/subarrays.html">SubArrays</a></li><li><a class="toctext" href="../devdocs/sysimg.html">System Image Building</a></li><li><a class="toctext" href="../devdocs/llvm.html">Working with LLVM</a></li><li><a class="toctext" href="../devdocs/stdio.html">printf() and stdio in the Julia runtime</a></li><li><a class="toctext" href="../devdocs/boundscheck.html">Bounds checking</a></li><li><a class="toctext" href="../devdocs/locks.html">Proper maintenance and care of multi-threading locks</a></li><li><a class="toctext" href="../devdocs/offset-arrays.html">Arrays with custom indices</a></li><li><a class="toctext" href="../devdocs/libgit2.html">Base.LibGit2</a></li><li><a class="toctext" href="../devdocs/require.html">Module loading</a></li></ul></li><li><span class="toctext">Developing/debugging Julia's C code</span><ul><li><a class="toctext" href="../devdocs/backtraces.html">Reporting and analyzing crashes (segfaults)</a></li><li><a class="toctext" href="../devdocs/debuggingtips.html">gdb debugging tips</a></li><li><a class="toctext" href="../devdocs/valgrind.html">Using Valgrind with Julia</a></li><li><a class="toctext" href="../devdocs/sanitizers.html">Sanitizer support</a></li></ul></li></ul></li></ul></nav><article id="docs"><header><nav><ul><li>Manual</li><li><a href="methods.html">Methods</a></li></ul><a class="edit-page" href="https://github.com/JuliaLang/julia/blob/master/doc/src/manual/methods.md"><span class="fa"></span> Edit on GitHub</a></nav><hr/><div id="topbar"><span>Methods</span><a class="fa fa-bars" href="#"></a></div></header><h1><a class="nav-anchor" id="Methods-1" href="#Methods-1">Methods</a></h1><p>Recall from <a href="functions.html#man-functions-1">Functions</a> that a function is an object that maps a tuple of arguments to a return value, or throws an exception if no appropriate value can be returned. It is common for the same conceptual function or operation to be implemented quite differently for different types of arguments: adding two integers is very different from adding two floating-point numbers, both of which are distinct from adding an integer to a floating-point number. Despite their implementation differences, these operations all fall under the general concept of "addition". Accordingly, in Julia, these behaviors all belong to a single object: the <code>+</code> function.</p><p>To facilitate using many different implementations of the same concept smoothly, functions need not be defined all at once, but can rather be defined piecewise by providing specific behaviors for certain combinations of argument types and counts. A definition of one possible behavior for a function is called a <em>method</em>. Thus far, we have presented only examples of functions defined with a single method, applicable to all types of arguments. However, the signatures of method definitions can be annotated to indicate the types of arguments in addition to their number, and more than a single method definition may be provided. When a function is applied to a particular tuple of arguments, the most specific method applicable to those arguments is applied. Thus, the overall behavior of a function is a patchwork of the behaviors of its various method definitions. If the patchwork is well designed, even though the implementations of the methods may be quite different, the outward behavior of the function will appear seamless and consistent.</p><p>The choice of which method to execute when a function is applied is called <em>dispatch</em>. Julia allows the dispatch process to choose which of a function's methods to call based on the number of arguments given, and on the types of all of the function's arguments. This is different than traditional object-oriented languages, where dispatch occurs based only on the first argument, which often has a special argument syntax, and is sometimes implied rather than explicitly written as an argument. <a href="#footnote-1">[1]</a> Using all of a function's arguments to choose which method should be invoked, rather than just the first, is known as <a href="https://en.wikipedia.org/wiki/Multiple_dispatch">multiple dispatch</a>. Multiple dispatch is particularly useful for mathematical code, where it makes little sense to artificially deem the operations to "belong" to one argument more than any of the others: does the addition operation in <code>x + y</code> belong to <code>x</code> any more than it does to <code>y</code>? The implementation of a mathematical operator generally depends on the types of all of its arguments. Even beyond mathematical operations, however, multiple dispatch ends up being a powerful and convenient paradigm for structuring and organizing programs.</p><div class="footnote" id="footnote-1"><a href="#footnote-1"><strong>[1]</strong></a><p>In C++ or Java, for example, in a method call like <code>obj.meth(arg1,arg2)</code>, the object obj "receives" the method call and is implicitly passed to the method via the <code>this</code> keyword, rather than as an explicit method argument. When the current <code>this</code> object is the receiver of a method call, it can be omitted altogether, writing just <code>meth(arg1,arg2)</code>, with <code>this</code> implied as the receiving object.</p></div><h2><a class="nav-anchor" id="Defining-Methods-1" href="#Defining-Methods-1">Defining Methods</a></h2><p>Until now, we have, in our examples, defined only functions with a single method having unconstrained argument types. Such functions behave just like they would in traditional dynamically typed languages. Nevertheless, we have used multiple dispatch and methods almost continually without being aware of it: all of Julia's standard functions and operators, like the aforementioned <code>+</code> function, have many methods defining their behavior over various possible combinations of argument type and count.</p><p>When defining a function, one can optionally constrain the types of parameters it is applicable to, using the <code>::</code> type-assertion operator, introduced in the section on <a href="types.html#Composite-Types-1">Composite Types</a>:</p><pre><code class="language-julia-repl">julia> f(x::Float64, y::Float64) = 2x + y
|
||
f (generic function with 1 method)</code></pre><p>This function definition applies only to calls where <code>x</code> and <code>y</code> are both values of type <a href="../stdlib/numbers.html#Core.Float64"><code>Float64</code></a>:</p><pre><code class="language-julia-repl">julia> f(2.0, 3.0)
|
||
7.0</code></pre><p>Applying it to any other types of arguments will result in a <a href="../stdlib/base.html#Base.MethodError"><code>MethodError</code></a>:</p><pre><code class="language-julia-repl">julia> f(2.0, 3)
|
||
ERROR: MethodError: no method matching f(::Float64, ::Int64)
|
||
Closest candidates are:
|
||
f(::Float64, !Matched::Float64) at none:1
|
||
|
||
julia> f(Float32(2.0), 3.0)
|
||
ERROR: MethodError: no method matching f(::Float32, ::Float64)
|
||
Closest candidates are:
|
||
f(!Matched::Float64, ::Float64) at none:1
|
||
|
||
julia> f(2.0, "3.0")
|
||
ERROR: MethodError: no method matching f(::Float64, ::String)
|
||
Closest candidates are:
|
||
f(::Float64, !Matched::Float64) at none:1
|
||
|
||
julia> f("2.0", "3.0")
|
||
ERROR: MethodError: no method matching f(::String, ::String)</code></pre><p>As you can see, the arguments must be precisely of type <a href="../stdlib/numbers.html#Core.Float64"><code>Float64</code></a>. Other numeric types, such as integers or 32-bit floating-point values, are not automatically converted to 64-bit floating-point, nor are strings parsed as numbers. Because <code>Float64</code> is a concrete type and concrete types cannot be subclassed in Julia, such a definition can only be applied to arguments that are exactly of type <code>Float64</code>. It may often be useful, however, to write more general methods where the declared parameter types are abstract:</p><pre><code class="language-julia-repl">julia> f(x::Number, y::Number) = 2x - y
|
||
f (generic function with 2 methods)
|
||
|
||
julia> f(2.0, 3)
|
||
1.0</code></pre><p>This method definition applies to any pair of arguments that are instances of <a href="../stdlib/numbers.html#Core.Number"><code>Number</code></a>. They need not be of the same type, so long as they are each numeric values. The problem of handling disparate numeric types is delegated to the arithmetic operations in the expression <code>2x - y</code>.</p><p>To define a function with multiple methods, one simply defines the function multiple times, with different numbers and types of arguments. The first method definition for a function creates the function object, and subsequent method definitions add new methods to the existing function object. The most specific method definition matching the number and types of the arguments will be executed when the function is applied. Thus, the two method definitions above, taken together, define the behavior for <code>f</code> over all pairs of instances of the abstract type <code>Number</code> – but with a different behavior specific to pairs of <a href="../stdlib/numbers.html#Core.Float64"><code>Float64</code></a> values. If one of the arguments is a 64-bit float but the other one is not, then the <code>f(Float64,Float64)</code> method cannot be called and the more general <code>f(Number,Number)</code> method must be used:</p><pre><code class="language-julia-repl">julia> f(2.0, 3.0)
|
||
7.0
|
||
|
||
julia> f(2, 3.0)
|
||
1.0
|
||
|
||
julia> f(2.0, 3)
|
||
1.0
|
||
|
||
julia> f(2, 3)
|
||
1</code></pre><p>The <code>2x + y</code> definition is only used in the first case, while the <code>2x - y</code> definition is used in the others. No automatic casting or conversion of function arguments is ever performed: all conversion in Julia is non-magical and completely explicit. <a href="conversion-and-promotion.html#conversion-and-promotion-1">Conversion and Promotion</a>, however, shows how clever application of sufficiently advanced technology can be indistinguishable from magic. <a href="#footnote-Clarke61">[Clarke61]</a></p><p>For non-numeric values, and for fewer or more than two arguments, the function <code>f</code> remains undefined, and applying it will still result in a <a href="../stdlib/base.html#Base.MethodError"><code>MethodError</code></a>:</p><pre><code class="language-julia-repl">julia> f("foo", 3)
|
||
ERROR: MethodError: no method matching f(::String, ::Int64)
|
||
Closest candidates are:
|
||
f(!Matched::Number, ::Number) at none:1
|
||
|
||
julia> f()
|
||
ERROR: MethodError: no method matching f()
|
||
Closest candidates are:
|
||
f(!Matched::Float64, !Matched::Float64) at none:1
|
||
f(!Matched::Number, !Matched::Number) at none:1</code></pre><p>You can easily see which methods exist for a function by entering the function object itself in an interactive session:</p><pre><code class="language-julia-repl">julia> f
|
||
f (generic function with 2 methods)</code></pre><p>This output tells us that <code>f</code> is a function object with two methods. To find out what the signatures of those methods are, use the <a href="../stdlib/base.html#Base.methods"><code>methods()</code></a> function:</p><pre><code class="language-julia-repl">julia> methods(f)
|
||
# 2 methods for generic function "f":
|
||
f(x::Float64, y::Float64) in Main at none:1
|
||
f(x::Number, y::Number) in Main at none:1</code></pre><p>which shows that <code>f</code> has two methods, one taking two <code>Float64</code> arguments and one taking arguments of type <code>Number</code>. It also indicates the file and line number where the methods were defined: because these methods were defined at the REPL, we get the apparent line number <code>none:1</code>.</p><p>In the absence of a type declaration with <code>::</code>, the type of a method parameter is <code>Any</code> by default, meaning that it is unconstrained since all values in Julia are instances of the abstract type <code>Any</code>. Thus, we can define a catch-all method for <code>f</code> like so:</p><pre><code class="language-julia-repl">julia> f(x,y) = println("Whoa there, Nelly.")
|
||
f (generic function with 3 methods)
|
||
|
||
julia> f("foo", 1)
|
||
Whoa there, Nelly.</code></pre><p>This catch-all is less specific than any other possible method definition for a pair of parameter values, so it will only be called on pairs of arguments to which no other method definition applies.</p><p>Although it seems a simple concept, multiple dispatch on the types of values is perhaps the single most powerful and central feature of the Julia language. Core operations typically have dozens of methods:</p><pre><code class="language-julia-repl">julia> methods(+)
|
||
# 180 methods for generic function "+":
|
||
+(x::Bool, z::Complex{Bool}) in Base at complex.jl:224
|
||
+(x::Bool, y::Bool) in Base at bool.jl:89
|
||
+(x::Bool) in Base at bool.jl:86
|
||
+(x::Bool, y::T) where T<:AbstractFloat in Base at bool.jl:96
|
||
+(x::Bool, z::Complex) in Base at complex.jl:231
|
||
+(a::Float16, b::Float16) in Base at float.jl:372
|
||
+(x::Float32, y::Float32) in Base at float.jl:374
|
||
+(x::Float64, y::Float64) in Base at float.jl:375
|
||
+(z::Complex{Bool}, x::Bool) in Base at complex.jl:225
|
||
+(z::Complex{Bool}, x::Real) in Base at complex.jl:239
|
||
+(x::Char, y::Integer) in Base at char.jl:40
|
||
+(c::BigInt, x::BigFloat) in Base.MPFR at mpfr.jl:303
|
||
+(a::BigInt, b::BigInt, c::BigInt, d::BigInt, e::BigInt) in Base.GMP at gmp.jl:303
|
||
+(a::BigInt, b::BigInt, c::BigInt, d::BigInt) in Base.GMP at gmp.jl:296
|
||
+(a::BigInt, b::BigInt, c::BigInt) in Base.GMP at gmp.jl:290
|
||
+(x::BigInt, y::BigInt) in Base.GMP at gmp.jl:258
|
||
+(x::BigInt, c::Union{UInt16, UInt32, UInt64, UInt8}) in Base.GMP at gmp.jl:315
|
||
...
|
||
+(a, b, c, xs...) at operators.jl:119</code></pre><p>Multiple dispatch together with the flexible parametric type system give Julia its ability to abstractly express high-level algorithms decoupled from implementation details, yet generate efficient, specialized code to handle each case at run time.</p><h2><a class="nav-anchor" id="man-ambiguities-1" href="#man-ambiguities-1">Method Ambiguities</a></h2><p>It is possible to define a set of function methods such that there is no unique most specific method applicable to some combinations of arguments:</p><pre><code class="language-julia-repl">julia> g(x::Float64, y) = 2x + y
|
||
g (generic function with 1 method)
|
||
|
||
julia> g(x, y::Float64) = x + 2y
|
||
g (generic function with 2 methods)
|
||
|
||
julia> g(2.0, 3)
|
||
7.0
|
||
|
||
julia> g(2, 3.0)
|
||
8.0
|
||
|
||
julia> g(2.0, 3.0)
|
||
ERROR: MethodError: g(::Float64, ::Float64) is ambiguous.
|
||
[...]</code></pre><p>Here the call <code>g(2.0, 3.0)</code> could be handled by either the <code>g(Float64, Any)</code> or the <code>g(Any, Float64)</code> method, and neither is more specific than the other. In such cases, Julia raises a <a href="../stdlib/base.html#Base.MethodError"><code>MethodError</code></a> rather than arbitrarily picking a method. You can avoid method ambiguities by specifying an appropriate method for the intersection case:</p><pre><code class="language-julia-repl">julia> g(x::Float64, y::Float64) = 2x + 2y
|
||
g (generic function with 3 methods)
|
||
|
||
julia> g(2.0, 3)
|
||
7.0
|
||
|
||
julia> g(2, 3.0)
|
||
8.0
|
||
|
||
julia> g(2.0, 3.0)
|
||
10.0</code></pre><p>It is recommended that the disambiguating method be defined first, since otherwise the ambiguity exists, if transiently, until the more specific method is defined.</p><p>In more complex cases, resolving method ambiguities involves a certain element of design; this topic is explored further <a href="methods.html#man-method-design-ambiguities-1">below</a>.</p><h2><a class="nav-anchor" id="Parametric-Methods-1" href="#Parametric-Methods-1">Parametric Methods</a></h2><p>Method definitions can optionally have type parameters qualifying the signature:</p><pre><code class="language-julia-repl">julia> same_type(x::T, y::T) where {T} = true
|
||
same_type (generic function with 1 method)
|
||
|
||
julia> same_type(x,y) = false
|
||
same_type (generic function with 2 methods)</code></pre><p>The first method applies whenever both arguments are of the same concrete type, regardless of what type that is, while the second method acts as a catch-all, covering all other cases. Thus, overall, this defines a boolean function that checks whether its two arguments are of the same type:</p><pre><code class="language-julia-repl">julia> same_type(1, 2)
|
||
true
|
||
|
||
julia> same_type(1, 2.0)
|
||
false
|
||
|
||
julia> same_type(1.0, 2.0)
|
||
true
|
||
|
||
julia> same_type("foo", 2.0)
|
||
false
|
||
|
||
julia> same_type("foo", "bar")
|
||
true
|
||
|
||
julia> same_type(Int32(1), Int64(2))
|
||
false</code></pre><p>Such definitions correspond to methods whose type signatures are <code>UnionAll</code> types (see <a href="types.html#UnionAll-Types-1">UnionAll Types</a>).</p><p>This kind of definition of function behavior by dispatch is quite common – idiomatic, even – in Julia. Method type parameters are not restricted to being used as the types of arguments: they can be used anywhere a value would be in the signature of the function or body of the function. Here's an example where the method type parameter <code>T</code> is used as the type parameter to the parametric type <code>Vector{T}</code> in the method signature:</p><pre><code class="language-julia-repl">julia> myappend(v::Vector{T}, x::T) where {T} = [v..., x]
|
||
myappend (generic function with 1 method)
|
||
|
||
julia> myappend([1,2,3],4)
|
||
4-element Array{Int64,1}:
|
||
1
|
||
2
|
||
3
|
||
4
|
||
|
||
julia> myappend([1,2,3],2.5)
|
||
ERROR: MethodError: no method matching myappend(::Array{Int64,1}, ::Float64)
|
||
Closest candidates are:
|
||
myappend(::Array{T,1}, !Matched::T) where T at none:1
|
||
|
||
julia> myappend([1.0,2.0,3.0],4.0)
|
||
4-element Array{Float64,1}:
|
||
1.0
|
||
2.0
|
||
3.0
|
||
4.0
|
||
|
||
julia> myappend([1.0,2.0,3.0],4)
|
||
ERROR: MethodError: no method matching myappend(::Array{Float64,1}, ::Int64)
|
||
Closest candidates are:
|
||
myappend(::Array{T,1}, !Matched::T) where T at none:1</code></pre><p>As you can see, the type of the appended element must match the element type of the vector it is appended to, or else a <a href="../stdlib/base.html#Base.MethodError"><code>MethodError</code></a> is raised. In the following example, the method type parameter <code>T</code> is used as the return value:</p><pre><code class="language-julia-repl">julia> mytypeof(x::T) where {T} = T
|
||
mytypeof (generic function with 1 method)
|
||
|
||
julia> mytypeof(1)
|
||
Int64
|
||
|
||
julia> mytypeof(1.0)
|
||
Float64</code></pre><p>Just as you can put subtype constraints on type parameters in type declarations (see <a href="types.html#Parametric-Types-1">Parametric Types</a>), you can also constrain type parameters of methods:</p><pre><code class="language-julia-repl">julia> same_type_numeric(x::T, y::T) where {T<:Number} = true
|
||
same_type_numeric (generic function with 1 method)
|
||
|
||
julia> same_type_numeric(x::Number, y::Number) = false
|
||
same_type_numeric (generic function with 2 methods)
|
||
|
||
julia> same_type_numeric(1, 2)
|
||
true
|
||
|
||
julia> same_type_numeric(1, 2.0)
|
||
false
|
||
|
||
julia> same_type_numeric(1.0, 2.0)
|
||
true
|
||
|
||
julia> same_type_numeric("foo", 2.0)
|
||
ERROR: MethodError: no method matching same_type_numeric(::String, ::Float64)
|
||
Closest candidates are:
|
||
same_type_numeric(!Matched::T<:Number, ::T<:Number) where T<:Number at none:1
|
||
same_type_numeric(!Matched::Number, ::Number) at none:1
|
||
|
||
julia> same_type_numeric("foo", "bar")
|
||
ERROR: MethodError: no method matching same_type_numeric(::String, ::String)
|
||
|
||
julia> same_type_numeric(Int32(1), Int64(2))
|
||
false</code></pre><p>The <code>same_type_numeric</code> function behaves much like the <code>same_type</code> function defined above, but is only defined for pairs of numbers.</p><p>Parametric methods allow the same syntax as <code>where</code> expressions used to write types (see <a href="types.html#UnionAll-Types-1">UnionAll Types</a>). If there is only a single parameter, the enclosing curly braces (in <code>where {T}</code>) can be omitted, but are often preferred for clarity. Multiple parameters can be separated with commas, e.g. <code>where {T, S<:Real}</code>, or written using nested <code>where</code>, e.g. <code>where S<:Real where T</code>.</p><h2><a class="nav-anchor" id="Redefining-Methods-1" href="#Redefining-Methods-1">Redefining Methods</a></h2><p>When redefining a method or adding new methods, it is important to realize that these changes don't take effect immediately. This is key to Julia's ability to statically infer and compile code to run fast, without the usual JIT tricks and overhead. Indeed, any new method definition won't be visible to the current runtime environment, including Tasks and Threads (and any previously defined <code>@generated</code> functions). Let's start with an example to see what this means:</p><pre><code class="language-julia-repl">julia> function tryeval()
|
||
@eval newfun() = 1
|
||
newfun()
|
||
end
|
||
tryeval (generic function with 1 method)
|
||
|
||
julia> tryeval()
|
||
ERROR: MethodError: no method matching newfun()
|
||
The applicable method may be too new: running in world age xxxx1, while current world is xxxx2.
|
||
Closest candidates are:
|
||
newfun() at none:1 (method too new to be called from this world context.)
|
||
in tryeval() at none:1
|
||
...
|
||
|
||
julia> newfun()
|
||
1</code></pre><p>In this example, observe that the new definition for <code>newfun</code> has been created, but can't be immediately called. The new global is immediately visible to the <code>tryeval</code> function, so you could write <code>return newfun</code> (without parentheses). But neither you, nor any of your callers, nor the functions they call, or etc. can call this new method definition!</p><p>But there's an exception: future calls to <code>newfun</code> <em>from the REPL</em> work as expected, being able to both see and call the new definition of <code>newfun</code>.</p><p>However, future calls to <code>tryeval</code> will continue to see the definition of <code>newfun</code> as it was <em>at the previous statement at the REPL</em>, and thus before that call to <code>tryeval</code>.</p><p>You may want to try this for yourself to see how it works.</p><p>The implementation of this behavior is a "world age counter". This monotonically increasing value tracks each method definition operation. This allows describing "the set of method definitions visible to a given runtime environment" as a single number, or "world age". It also allows comparing the methods available in two worlds just by comparing their ordinal value. In the example above, we see that the "current world" (in which the method <code>newfun()</code> exists), is one greater than the task-local "runtime world" that was fixed when the execution of <code>tryeval</code> started.</p><p>Sometimes it is necessary to get around this (for example, if you are implementing the above REPL). Fortunately, there is an easy solution: call the function using <a href="../stdlib/base.html#Base.invokelatest"><code>Base.invokelatest</code></a>:</p><pre><code class="language-julia-repl">julia> function tryeval2()
|
||
@eval newfun2() = 2
|
||
Base.invokelatest(newfun2)
|
||
end
|
||
tryeval2 (generic function with 1 method)
|
||
|
||
julia> tryeval2()
|
||
2</code></pre><p>Finally, let's take a look at some more complex examples where this rule comes into play. Define a function <code>f(x)</code>, which initially has one method:</p><pre><code class="language-julia-repl">julia> f(x) = "original definition"
|
||
f (generic function with 1 method)</code></pre><p>Start some other operations that use <code>f(x)</code>:</p><pre><code class="language-julia-repl">julia> g(x) = f(x)
|
||
g (generic function with 1 method)
|
||
|
||
julia> t = @async f(wait()); yield();</code></pre><p>Now we add some new methods to <code>f(x)</code>:</p><pre><code class="language-julia-repl">julia> f(x::Int) = "definition for Int"
|
||
f (generic function with 2 methods)
|
||
|
||
julia> f(x::Type{Int}) = "definition for Type{Int}"
|
||
f (generic function with 3 methods)</code></pre><p>Compare how these results differ:</p><pre><code class="language-julia-repl">julia> f(1)
|
||
"definition for Int"
|
||
|
||
julia> g(1)
|
||
"definition for Int"
|
||
|
||
julia> wait(schedule(t, 1))
|
||
"original definition"
|
||
|
||
julia> t = @async f(wait()); yield();
|
||
|
||
julia> wait(schedule(t, 1))
|
||
"definition for Int"</code></pre><h2><a class="nav-anchor" id="Parametrically-constrained-Varargs-methods-1" href="#Parametrically-constrained-Varargs-methods-1">Parametrically-constrained Varargs methods</a></h2><p>Function parameters can also be used to constrain the number of arguments that may be supplied to a "varargs" function (<a href="functions.html#Varargs-Functions-1">Varargs Functions</a>). The notation <code>Vararg{T,N}</code> is used to indicate such a constraint. For example:</p><pre><code class="language-julia-repl">julia> bar(a,b,x::Vararg{Any,2}) = (a,b,x)
|
||
bar (generic function with 1 method)
|
||
|
||
julia> bar(1,2,3)
|
||
ERROR: MethodError: no method matching bar(::Int64, ::Int64, ::Int64)
|
||
Closest candidates are:
|
||
bar(::Any, ::Any, ::Any, !Matched::Any) at none:1
|
||
|
||
julia> bar(1,2,3,4)
|
||
(1, 2, (3, 4))
|
||
|
||
julia> bar(1,2,3,4,5)
|
||
ERROR: MethodError: no method matching bar(::Int64, ::Int64, ::Int64, ::Int64, ::Int64)
|
||
Closest candidates are:
|
||
bar(::Any, ::Any, ::Any, ::Any) at none:1</code></pre><p>More usefully, it is possible to constrain varargs methods by a parameter. For example:</p><pre><code class="language-julia">function getindex(A::AbstractArray{T,N}, indexes::Vararg{Number,N}) where {T,N}</code></pre><p>would be called only when the number of <code>indexes</code> matches the dimensionality of the array.</p><h2><a class="nav-anchor" id="Note-on-Optional-and-keyword-Arguments-1" href="#Note-on-Optional-and-keyword-Arguments-1">Note on Optional and keyword Arguments</a></h2><p>As mentioned briefly in <a href="functions.html#man-functions-1">Functions</a>, optional arguments are implemented as syntax for multiple method definitions. For example, this definition:</p><pre><code class="language-julia">f(a=1,b=2) = a+2b</code></pre><p>translates to the following three methods:</p><pre><code class="language-julia">f(a,b) = a+2b
|
||
f(a) = f(a,2)
|
||
f() = f(1,2)</code></pre><p>This means that calling <code>f()</code> is equivalent to calling <code>f(1,2)</code>. In this case the result is <code>5</code>, because <code>f(1,2)</code> invokes the first method of <code>f</code> above. However, this need not always be the case. If you define a fourth method that is more specialized for integers:</p><pre><code class="language-julia">f(a::Int,b::Int) = a-2b</code></pre><p>then the result of both <code>f()</code> and <code>f(1,2)</code> is <code>-3</code>. In other words, optional arguments are tied to a function, not to any specific method of that function. It depends on the types of the optional arguments which method is invoked. When optional arguments are defined in terms of a global variable, the type of the optional argument may even change at run-time.</p><p>Keyword arguments behave quite differently from ordinary positional arguments. In particular, they do not participate in method dispatch. Methods are dispatched based only on positional arguments, with keyword arguments processed after the matching method is identified.</p><h2><a class="nav-anchor" id="Function-like-objects-1" href="#Function-like-objects-1">Function-like objects</a></h2><p>Methods are associated with types, so it is possible to make any arbitrary Julia object "callable" by adding methods to its type. (Such "callable" objects are sometimes called "functors.")</p><p>For example, you can define a type that stores the coefficients of a polynomial, but behaves like a function evaluating the polynomial:</p><pre><code class="language-julia-repl">julia> struct Polynomial{R}
|
||
coeffs::Vector{R}
|
||
end
|
||
|
||
julia> function (p::Polynomial)(x)
|
||
v = p.coeffs[end]
|
||
for i = (length(p.coeffs)-1):-1:1
|
||
v = v*x + p.coeffs[i]
|
||
end
|
||
return v
|
||
end</code></pre><p>Notice that the function is specified by type instead of by name. In the function body, <code>p</code> will refer to the object that was called. A <code>Polynomial</code> can be used as follows:</p><pre><code class="language-julia-repl">julia> p = Polynomial([1,10,100])
|
||
Polynomial{Int64}([1, 10, 100])
|
||
|
||
julia> p(3)
|
||
931</code></pre><p>This mechanism is also the key to how type constructors and closures (inner functions that refer to their surrounding environment) work in Julia, discussed <a href="constructors.html#constructors-and-conversion-1">later in the manual</a>.</p><h2><a class="nav-anchor" id="Empty-generic-functions-1" href="#Empty-generic-functions-1">Empty generic functions</a></h2><p>Occasionally it is useful to introduce a generic function without yet adding methods. This can be used to separate interface definitions from implementations. It might also be done for the purpose of documentation or code readability. The syntax for this is an empty <code>function</code> block without a tuple of arguments:</p><pre><code class="language-julia">function emptyfunc
|
||
end</code></pre><h2><a class="nav-anchor" id="man-method-design-ambiguities-1" href="#man-method-design-ambiguities-1">Method design and the avoidance of ambiguities</a></h2><p>Julia's method polymorphism is one of its most powerful features, yet exploiting this power can pose design challenges. In particular, in more complex method hierarchies it is not uncommon for <a href="methods.html#man-ambiguities-1">ambiguities</a> to arise.</p><p>Above, it was pointed out that one can resolve ambiguities like</p><pre><code class="language-julia">f(x, y::Int) = 1
|
||
f(x::Int, y) = 2</code></pre><p>by defining a method</p><pre><code class="language-julia">f(x::Int, y::Int) = 3</code></pre><p>This is often the right strategy; however, there are circumstances where following this advice blindly can be counterproductive. In particular, the more methods a generic function has, the more possibilities there are for ambiguities. When your method hierarchies get more complicated than this simple example, it can be worth your while to think carefully about alternative strategies.</p><p>Below we discuss particular challenges and some alternative ways to resolve such issues.</p><h3><a class="nav-anchor" id="Tuple-and-NTuple-arguments-1" href="#Tuple-and-NTuple-arguments-1">Tuple and NTuple arguments</a></h3><p><code>Tuple</code> (and <code>NTuple</code>) arguments present special challenges. For example,</p><pre><code class="language-julia">f(x::NTuple{N,Int}) where {N} = 1
|
||
f(x::NTuple{N,Float64}) where {N} = 2</code></pre><p>are ambiguous because of the possibility that <code>N == 0</code>: there are no elements to determine whether the <code>Int</code> or <code>Float64</code> variant should be called. To resolve the ambiguity, one approach is define a method for the empty tuple:</p><pre><code class="language-julia">f(x::Tuple{}) = 3</code></pre><p>Alternatively, for all methods but one you can insist that there is at least one element in the tuple:</p><pre><code class="language-julia">f(x::NTuple{N,Int}) where {N} = 1 # this is the fallback
|
||
f(x::Tuple{Float64, Vararg{Float64}}) = 2 # this requires at least one Float64</code></pre><h3><a class="nav-anchor" id="man-methods-orthogonalize-1" href="#man-methods-orthogonalize-1">Orthogonalize your design</a></h3><p>When you might be tempted to dispatch on two or more arguments, consider whether a "wrapper" function might make for a simpler design. For example, instead of writing multiple variants:</p><pre><code class="language-julia">f(x::A, y::A) = ...
|
||
f(x::A, y::B) = ...
|
||
f(x::B, y::A) = ...
|
||
f(x::B, y::B) = ...</code></pre><p>you might consider defining</p><pre><code class="language-julia">f(x::A, y::A) = ...
|
||
f(x, y) = f(g(x), g(y))</code></pre><p>where <code>g</code> converts the argument to type <code>A</code>. This is a very specific example of the more general principle of <a href="https://en.wikipedia.org/wiki/Orthogonality_(programming)">orthogonal design</a>, in which separate concepts are assigned to separate methods. Here, <code>g</code> will most likely need a fallback definition</p><pre><code class="language-julia">g(x::A) = x</code></pre><p>A related strategy exploits <code>promote</code> to bring <code>x</code> and <code>y</code> to a common type:</p><pre><code class="language-julia">f(x::T, y::T) where {T} = ...
|
||
f(x, y) = f(promote(x, y)...)</code></pre><p>One risk with this design is the possibility that if there is no suitable promotion method converting <code>x</code> and <code>y</code> to the same type, the second method will recurse on itself infinitely and trigger a stack overflow. The non-exported function <code>Base.promote_noncircular</code> can be used as an alternative; when promotion fails it will still throw an error, but one that fails faster with a more specific error message.</p><h3><a class="nav-anchor" id="Dispatch-on-one-argument-at-a-time-1" href="#Dispatch-on-one-argument-at-a-time-1">Dispatch on one argument at a time</a></h3><p>If you need to dispatch on multiple arguments, and there are many fallbacks with too many combinations to make it practical to define all possible variants, then consider introducing a "name cascade" where (for example) you dispatch on the first argument and then call an internal method:</p><pre><code class="language-julia">f(x::A, y) = _fA(x, y)
|
||
f(x::B, y) = _fB(x, y)</code></pre><p>Then the internal methods <code>_fA</code> and <code>_fB</code> can dispatch on <code>y</code> without concern about ambiguities with each other with respect to <code>x</code>.</p><p>Be aware that this strategy has at least one major disadvantage: in many cases, it is not possible for users to further customize the behavior of <code>f</code> by defining further specializations of your exported function <code>f</code>. Instead, they have to define specializations for your internal methods <code>_fA</code> and <code>_fB</code>, and this blurs the lines between exported and internal methods.</p><h3><a class="nav-anchor" id="Abstract-containers-and-element-types-1" href="#Abstract-containers-and-element-types-1">Abstract containers and element types</a></h3><p>Where possible, try to avoid defining methods that dispatch on specific element types of abstract containers. For example,</p><pre><code class="language-julia">-(A::AbstractArray{T}, b::Date) where {T<:Date}</code></pre><p>generates ambiguities for anyone who defines a method</p><pre><code class="language-julia">-(A::MyArrayType{T}, b::T) where {T}</code></pre><p>The best approach is to avoid defining <em>either</em> of these methods: instead, rely on a generic method <code>-(A::AbstractArray, b)</code> and make sure this method is implemented with generic calls (like <code>similar</code> and <code>-</code>) that do the right thing for each container type and element type <em>separately</em>. This is just a more complex variant of the advice to <a href="methods.html#man-methods-orthogonalize-1">orthogonalize</a> your methods.</p><p>When this approach is not possible, it may be worth starting a discussion with other developers about resolving the ambiguity; just because one method was defined first does not necessarily mean that it can't be modified or eliminated. As a last resort, one developer can define the "band-aid" method</p><pre><code class="language-julia">-(A::MyArrayType{T}, b::Date) where {T<:Date} = ...</code></pre><p>that resolves the ambiguity by brute force.</p><h3><a class="nav-anchor" id="Complex-method-"cascades"-with-default-arguments-1" href="#Complex-method-"cascades"-with-default-arguments-1">Complex method "cascades" with default arguments</a></h3><p>If you are defining a method "cascade" that supplies defaults, be careful about dropping any arguments that correspond to potential defaults. For example, suppose you're writing a digital filtering algorithm and you have a method that handles the edges of the signal by applying padding:</p><pre><code class="language-julia">function myfilter(A, kernel, ::Replicate)
|
||
Apadded = replicate_edges(A, size(kernel))
|
||
myfilter(Apadded, kernel) # now perform the "real" computation
|
||
end</code></pre><p>This will run afoul of a method that supplies default padding:</p><pre><code class="language-julia">myfilter(A, kernel) = myfilter(A, kernel, Replicate()) # replicate the edge by default</code></pre><p>Together, these two methods generate an infinite recursion with <code>A</code> constantly growing bigger.</p><p>The better design would be to define your call hierarchy like this:</p><pre><code class="language-julia">struct NoPad end # indicate that no padding is desired, or that it's already applied
|
||
|
||
myfilter(A, kernel) = myfilter(A, kernel, Replicate()) # default boundary conditions
|
||
|
||
function myfilter(A, kernel, ::Replicate)
|
||
Apadded = replicate_edges(A, size(kernel))
|
||
myfilter(Apadded, kernel, NoPad()) # indicate the new boundary conditions
|
||
end
|
||
|
||
# other padding methods go here
|
||
|
||
function myfilter(A, kernel, ::NoPad)
|
||
# Here's the "real" implementation of the core computation
|
||
end</code></pre><p><code>NoPad</code> is supplied in the same argument position as any other kind of padding, so it keeps the dispatch hierarchy well organized and with reduced likelihood of ambiguities. Moreover, it extends the "public" <code>myfilter</code> interface: a user who wants to control the padding explicitly can call the <code>NoPad</code> variant directly.</p><div class="footnote" id="footnote-Clarke61"><a href="#footnote-Clarke61"><strong>[Clarke61]</strong></a><p>Arthur C. Clarke, <em>Profiles of the Future</em> (1961): Clarke's Third Law.</p></div><footer><hr/><a class="previous" href="types.html"><span class="direction">Previous</span><span class="title">Types</span></a><a class="next" href="constructors.html"><span class="direction">Next</span><span class="title">Constructors</span></a></footer></article></body></html>
|