From 936e84f7d28766bf9eea0c4576050efe0d201bbd Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 25 Feb 2021 17:37:21 -0500 Subject: [PATCH] add basic overview of when to use type declarations (#39812) --- doc/src/manual/functions.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/doc/src/manual/functions.md b/doc/src/manual/functions.md index b6e362eb72c73..5d1d649b75062 100644 --- a/doc/src/manual/functions.md +++ b/doc/src/manual/functions.md @@ -63,6 +63,23 @@ are identical to the passed values. Modifications to mutable values (such as `Ar a function will be visible to the caller. This is the same behavior found in Scheme, most Lisps, Python, Ruby and Perl, among other dynamic languages. +## Argument-type declarations + +You can declare the types of function arguments by appending `::TypeName` to the argument name, as usual for [Type Declarations](@ref) in Julia. +For example, the following function computes [Fibonacci numbers](https://en.wikipedia.org/wiki/Fibonacci_number) recursively: +``` +fib(n::Integer) = n ≤ 2 ? one(n) : fib(n-1) + fib(n-2) +``` +and the `::Integer` specification means that it will only be callable when `n` is a subtype of the [abstract](@ref man-abstract-types) `Integer` type. + +Argument-type declarations **normally have no impact on performance**: regardless of what argument types (if any) are declared, Julia compiles a specialized version of the function for the actual argument types passed by the caller. For example, calling `fib(1)` will trigger the compilation of specialized version of `fib` optimized specifically for `Int` arguments, which is then re-used if `fib(7)` or `fib(15)` are called. (There are rare exceptions when an argument-type declaration can trigger additional compiler specializations; see: [Be aware of when Julia avoids specializing](@ref).) The most common reasons to declare argument types in Julia are, instead: + +* **Dispatch:** As explained in [Methods](@ref), you can have different versions ("methods") of a function for different argument types, in which case the argument types are used to determine which implementation is called for which arguments. For example, you might implement a completely different algorithm `fib(x::Number) = ...` that works for any `Number` type by using [Binet's formula](https://en.wikipedia.org/wiki/Fibonacci_number#Binet's_formula) to extend it to non-integer values. +* **Correctness:** Type declarations can be useful if your function only returns correct results for certain argument types. For example, if we omitted argument types and wrote `fib(n) = n ≤ 2 ? one(n) : fib(n-1) + fib(n-2)`, then `fib(1.5)` would silently give us the nonsensical answer `1.0`. +* **Clarity:** Type declarations can serve as a form of documentation about the expected arguments. + +However, it is a **common mistake to overly restrict the argument types**, which can unnecessarily limit the applicability of the function and prevent it from being re-used in circumstances you did not anticipate. For example, the `fib(n::Integer)` function above works equally well for `Int` arguments (machine integers) and `BigInt` arbitrary-precision integers (see [BigFloats and BigInts](@ref)), which is especially useful because Fibonacci numbers grow exponentially rapidly and will quickly overflow any fixed-precision type like `Int` (see [Overflow behavior](@ref)). If we had declared our function as `fib(n::Int)`, however, the application to `BigInt` would have been prevented for no reason. In general, you should use the most general applicable abstract types for arguments, and **when in doubt, omit the argument types**. You can always add argument-type specifications later if they become necessary, and you don't sacrifice performance or functionality by omitting them. + ## The `return` Keyword The value returned by a function is the value of the last expression evaluated, which, by default, @@ -146,6 +163,10 @@ Int8 This function will always return an `Int8` regardless of the types of `x` and `y`. See [Type Declarations](@ref) for more on return types. +Return type declarations are **rarely used** in Julia: in general, you should +instead write "type-stable" functions in which Julia's compiler can automatically +infer the return type. For more information, see the [Performance Tips](@ref man-performance-tips) chapter. + ### Returning nothing For functions that do not need to return a value (functions used only for some side effects),