### [Built-in Exceptions](#Built-in-Exceptions)
For example, the [`sqrt`](../../base/math/#Base.sqrt-Tuple%7BNumber%7D) function throws a [`DomainError`](../../base/base/#Core.DomainError) if applied to a negative real value:
```julia-repl
julia> sqrt(-1)
ERROR: DomainError with -1.0:
sqrt was called with a negative real argument but will only return a complex result if called with a complex argument. Try sqrt(Complex(x)).
Stacktrace:
[...]
```
You may define your own exceptions in the following way:
```julia-repl
julia> struct MyCustomException <: Exception end
```


### [The](#The-%5Bthrow%5D(@ref)-function) [`throw`](../../base/base/#Core.throw) function
Exceptions can be created explicitly with [`throw`](../../base/base/#Core.throw). For example, a function defined only for non-negative numbers could be written to [`throw`](../../base/base/#Core.throw) a [`DomainError`](../../base/base/#Core.DomainError) if the argument is negative:
```julia-repl
julia> f(x) = x>=0 ? exp(-x) : throw(DomainError(x, "argument must be non-negative"))
f (generic function with 1 method)

julia> f(1)
0.36787944117144233

julia> f(-1)
ERROR: DomainError with -1:
argument must be non-negative
Stacktrace:
 [1] f(::Int64) at ./none:1
```
Note that [`DomainError`](../../base/base/#Core.DomainError) without parentheses is not an exception, but a type of exception. It needs to be called to obtain an `Exception` object:
```julia-repl
julia> typeof(DomainError(nothing)) <: Exception
true

julia> typeof(DomainError) <: Exception
false
```
Additionally, some exception types take one or more arguments that are used for error reporting:
```julia-repl
julia> throw(UndefVarError(:x))
ERROR: UndefVarError: `x` not defined
```
This mechanism can be implemented easily by custom exception types following the way [`UndefVarError`](../../base/base/#Core.UndefVarError) is written:
```julia-repl
julia> struct MyUndefVarError <: Exception
           var::Symbol
       end

julia> Base.showerror(io::IO, e::MyUndefVarError) = print(io, e.var, " not defined")
```
When writing an error message, it is preferred to make the first word lowercase. For example,


### [The](#The-%5Bthrow%5D(@ref)-function) [`throw`](../../base/base/#Core.throw) function
`size(A) == size(B) || throw(DimensionMismatch("size of A not equal to size of B"))`
is preferred over
`size(A) == size(B) || throw(DimensionMismatch("Size of A not equal to size of B"))`.
However, sometimes it makes sense to keep the uppercase first letter, for instance if an argument to a function is a capital letter:
`size(A,1) == size(B,2) || throw(DimensionMismatch("A has first dimension..."))`.


### [Errors](#Errors)
The [`error`](../../base/base/#Base.error) function is used to produce an [`ErrorException`](../../base/base/#Core.ErrorException) that interrupts the normal flow of control.
Suppose we want to stop execution immediately if the square root of a negative number is taken. To do this, we can define a fussy version of the [`sqrt`](../../base/math/#Base.sqrt-Tuple%7BNumber%7D) function that raises an error if its argument is negative:
```julia-repl
julia> fussy_sqrt(x) = x >= 0 ? sqrt(x) : error("negative x not allowed")
fussy_sqrt (generic function with 1 method)

julia> fussy_sqrt(2)
1.4142135623730951

julia> fussy_sqrt(-1)
ERROR: negative x not allowed
Stacktrace:
 [1] error at ./error.jl:33 [inlined]
 [2] fussy_sqrt(::Int64) at ./none:1
 [3] top-level scope
```
If `fussy_sqrt` is called with a negative value from another function, instead of trying to continue execution of the calling function, it returns immediately, displaying the error message in the interactive session:
```julia-repl
julia> function verbose_fussy_sqrt(x)
           println("before fussy_sqrt")
           r = fussy_sqrt(x)
           println("after fussy_sqrt")
           return r
       end
verbose_fussy_sqrt (generic function with 1 method)

julia> verbose_fussy_sqrt(2)
before fussy_sqrt
after fussy_sqrt
1.4142135623730951

julia> verbose_fussy_sqrt(-1)
before fussy_sqrt
ERROR: negative x not allowed
Stacktrace:
 [1] error at ./error.jl:33 [inlined]
 [2] fussy_sqrt at ./none:1 [inlined]
 [3] verbose_fussy_sqrt(::Int64) at ./none:3
 [4] top-level scope
```


### [The try/catch statement](#The-try/catch-statement)
The `try/catch` statement allows for `Exception`s to be tested for, and for the graceful handling of things that may ordinarily break your application. For example, in the below code the function for square root would normally throw an exception. By placing a `try/catch` block around it we can mitigate that here. You may choose how you wish to handle this exception, whether logging it, return a placeholder value or as in the case below where we just printed out a statement. One thing to think about when deciding how to handle unexpected situations is that using a `try/catch` block is much slower than using conditional branching to handle those situations. Below there are more examples of handling exceptions with a `try/catch` block:
```julia-repl
julia> try
           sqrt("ten")
       catch e
           println("You should have entered a numeric value")
       end
You should have entered a numeric value
```
`try/catch` statements also allow the `Exception` to be saved in a variable. The following contrived example calculates the square root of the second element of `x` if `x` is indexable, otherwise assumes `x` is a real number and returns its square root:


### [The try/catch statement](#The-try/catch-statement)
```julia-repl
julia> sqrt_second(x) = try
           sqrt(x[2])
       catch y
           if isa(y, DomainError)
               sqrt(complex(x[2], 0))
           elseif isa(y, BoundsError)
               sqrt(x)
           end
       end
sqrt_second (generic function with 1 method)

julia> sqrt_second([1 4])
2.0

julia> sqrt_second([1 -4])
0.0 + 2.0im

julia> sqrt_second(9)
3.0

julia> sqrt_second(-9)
ERROR: DomainError with -9.0:
sqrt was called with a negative real argument but will only return a complex result if called with a complex argument. Try sqrt(Complex(x)).
Stacktrace:
[...]
```
Note that the symbol following `catch` will always be interpreted as a name for the exception, so care is needed when writing `try/catch` expressions on a single line. The following code will *not* work to return the value of `x` in case of an error:
```julia
try bad() catch x end
```
Instead, use a semicolon or insert a line break after `catch`:
```julia
try bad() catch; x end

try bad()
catch
    x
end
```


### [The try/catch statement](#The-try/catch-statement)
The power of the `try/catch` construct lies in the ability to unwind a deeply nested computation immediately to a much higher level in the stack of calling functions. There are situations where no error has occurred, but the ability to unwind the stack and pass a value to a higher level is desirable. Julia provides the [`rethrow`](../../base/base/#Base.rethrow), [`backtrace`](../../base/base/#Base.backtrace), [`catch_backtrace`](../../base/base/#Base.catch_backtrace) and [`current_exceptions`](../../base/base/#Base.current_exceptions) functions for more advanced error handling.


### [else Clauses](#else-Clauses)
This functionality requires at least Julia 1.8.
In some cases, one may not only want to appropriately handle the error case, but also want to run some code only if the `try` block succeeds. For this, an `else` clause can be specified after the `catch` block that is run whenever no error was thrown previously. The advantage over including this code in the `try` block instead is that any further errors don't get silently caught by the `catch` clause.
```julia
local x
try
    x = read("file", String)
catch
    # handle read errors
else
    # do something with x
end
```
The `try`, `catch`, `else`, and `finally` clauses each introduce their own scope blocks, so if a variable is only defined in the `try` block, it can not be accessed by the `else` or `finally` clause:
```julia-repl
julia> try
           foo = 1
       catch
       else
           foo
       end
ERROR: UndefVarError: `foo` not defined in `Main`
Suggestion: check for spelling errors or missing imports.
```
Use the [`local` keyword](../variables-and-scoping/#local-scope) outside the `try` block to make the variable accessible from anywhere within the outer scope.


### [finally Clauses](#finally-Clauses)
In code that performs state changes or uses resources like files, there is typically clean-up work (such as closing files) that needs to be done when the code is finished. Exceptions potentially complicate this task, since they can cause a block of code to exit before reaching its normal end. The `finally` keyword provides a way to run some code when a given block of code exits, regardless of how it exits.
For example, here is how we can guarantee that an opened file is closed:
```julia
f = open("file")
try
    # operate on file f
finally
    close(f)
end
```
When control leaves the `try` block (for example due to a `return`, or just finishing normally), `close(f)` will be executed. If the `try` block exits due to an exception, the exception will continue propagating. A `catch` block may be combined with `try` and `finally` as well. In this case the `finally` block will run after `catch` has handled the error.


## [Tasks (aka Coroutines)](#man-tasks)
Tasks are a control flow feature that allows computations to be suspended and resumed in a flexible manner. We mention them here only for completeness; for a full discussion see [Asynchronous Programming](../asynchronous-programming/#man-asynchronous).
------------------------------------------------------------------------


# Scope of Variables · The Julia Language
Source: https://docs.julialang.org/en/v1/manual/variables-and-scoping/


# [Scope of Variables](#scope-of-variables)
The *scope* of a variable is the region of code within which a variable is accessible. Variable scoping helps avoid variable naming conflicts. The concept is intuitive: two functions can both have arguments called `x` without the two `x`'s referring to the same thing. Similarly, there are many other cases where different blocks of code can use the same name without referring to the same thing. The rules for when the same variable name does or doesn't refer to the same thing are called scope rules; this section spells them out in detail.
Certain constructs in the language introduce *scope blocks*, which are regions of code that are eligible to be the scope of some set of variables. The scope of a variable cannot be an arbitrary set of source lines; instead, it will always line up with one of these blocks. There are two main types of scopes in Julia, *global scope* and *local scope*. The latter can be nested. There is also a distinction in Julia between constructs which introduce a "hard scope" and those which only introduce a "soft scope", which affects whether [shadowing](https://en.wikipedia.org/wiki/Variable_shadowing) a global variable by the same name is allowed or not.


### [Scope constructs](#man-scope-table)
The constructs introducing scope blocks are:
| Construct                                                                                | Scope type   | Allowed within |
|:----------------------------------------------------------------------------------------------------------------|:-------------|:---------------|
| [`module`](../../base/base/#module), [`baremodule`](../../base/base/#baremodule)                                | global       | global         |
| [`struct`](../../base/base/#struct)                                                                             | local (hard) | global         |
| [`for`](../../base/base/#for), [`while`](../../base/base/#while), [`try`](../../base/base/#try)                 | local (soft) | global, local  |
| [`macro`](../../base/base/#macro)                                                                               | local (hard) | global         |
| functions, [`do`](../../base/base/#do) blocks, [`let`](../../base/base/#let) blocks, comprehensions, generators | local (hard) | global, local  |
Notably missing from this table are [begin blocks](../control-flow/#man-compound-expressions) and [if blocks](../control-flow/#man-conditional-evaluation) which do *not* introduce new scopes. The three types of scopes follow somewhat different rules which will be explained below.


### [Scope constructs](#man-scope-table)
Julia uses [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scope_vs._dynamic_scope), meaning that a function's scope does not inherit from its caller's scope, but from the scope in which the function was defined. For example, in the following code the `x` inside `foo` refers to the `x` in the global scope of its module `Bar`:
```julia-repl
julia> module Bar
           x = 1
           foo() = x
       end;
```
and not a `x` in the scope where `foo` is used:
```julia-repl
julia> import .Bar

julia> x = -1;

julia> Bar.foo()
1
```
Thus *lexical scope* means that what a variable in a particular piece of code refers to can be deduced from the code in which it appears alone and does not depend on how the program executes. A scope nested inside another scope can "see" variables in all the outer scopes in which it is contained. Outer scopes, on the other hand, cannot see variables in inner scopes.


## [Global Scope](#Global-Scope)
Each module introduces a new global scope, separate from the global scope of all other modules—there is no all-encompassing global scope. Modules can introduce variables of other modules into their scope through the [using or import](../modules/#modules) statements or through qualified access using the dot-notation, i.e. each module is a so-called *namespace* as well as a first-class data structure associating names with values.
If a top-level expression contains a variable declaration with keyword `local`, then that variable is not accessible outside that expression. The variable inside the expression does not affect global variables of the same name. An example is to declare `local x` in a `begin` or `if` block at the top-level:
```julia-repl
julia> x = 1
       begin
           local x = 0
           @show x
       end
       @show x;
x = 0
x = 1
```
Note that the interactive prompt (aka REPL) is in the global scope of the module `Main`.


## [Local Scope](#local-scope)
A new local scope is introduced by most code blocks (see above [table](#man-scope-table) for a complete list). If such a block is syntactically nested inside of another local scope, the scope it creates is nested inside of all the local scopes that it appears within, which are all ultimately nested inside of the global scope of the module in which the code is evaluated. Variables in outer scopes are visible from any scope they contain — meaning that they can be read and written in inner scopes — unless there is a local variable with the same name that "shadows" the outer variable of the same name. This is true even if the outer local is declared after (in the sense of textually below) an inner block. When we say that a variable "exists" in a given scope, this means that a variable by that name exists in any of the scopes that the current scope is nested inside of, including the current one.


## [Local Scope](#local-scope)
Some programming languages require explicitly declaring new variables before using them. Explicit declaration works in Julia too: in any local scope, writing `local x` declares a new local variable in that scope, regardless of whether there is already a variable named `x` in an outer scope or not. Declaring each new variable like this is somewhat verbose and tedious, however, so Julia, like many other languages, considers assignment to a variable name that doesn't already exist to implicitly declare that variable. If the current scope is global, the new variable is global; if the current scope is local, the new variable is local to the innermost local scope and will be visible inside of that scope but not outside of it. If you assign to an existing local, it *always* updates that existing local: you can only shadow a local by explicitly declaring a new local in a nested scope with the `local` keyword. In particular, this applies to variables assigned in inner functions, which may surprise users coming from Python where assignment in an inner function creates a new local unless the variable is explicitly declared to be non-local.
Mostly this is pretty intuitive, but as with many things that behave intuitively, the details are more subtle than one might naïvely imagine.
When `x = <value>` occurs in a local scope, Julia applies the following rules to decide what the expression means based on where the assignment expression occurs and what `x` already refers to at that location:


## [Local Scope](#local-scope)
1.  **Existing local:** If `x` is *already a local variable*, then the existing local `x` is assigned;
2.  **Hard scope:** If `x` is *not already a local variable* and assignment occurs inside of any hard scope construct (i.e. within a `let` block, function, struct or macro body, comprehension, or generator), a new local named `x` is created in the scope of the assignment;
3.  **Soft scope:** If `x` is *not already a local variable* and all of the scope constructs containing the assignment are soft scopes (loops, `try`/`catch` blocks), the behavior depends on whether the global variable `x` is defined:
    -   if global `x` is *undefined*, a new local named `x` is created in the scope of the assignment;
    -   if global `x` is *defined*, the assignment is considered ambiguous:
        -   in *non-interactive* contexts (files, eval), an ambiguity warning is printed and a new local is created;
        -   in *interactive* contexts (REPL, notebooks), the global variable `x` is assigned.
You may note that in non-interactive contexts the hard and soft scope behaviors are identical except that a warning is printed when an implicitly local variable (i.e. not declared with `local x`) shadows a global. In interactive contexts, the rules follow a more complex heuristic for the sake of convenience. This is covered in depth in examples that follow.


## [Local Scope](#local-scope)
Now that you know the rules, let's look at some examples. Each example is assumed to be evaluated in a fresh REPL session so that the only globals in each snippet are the ones that are assigned in that block of code.
We'll begin with a nice and clear-cut situation—assignment inside of a hard scope, in this case a function body, when no local variable by that name already exists:
```julia-repl
julia> function greet()
           x = "hello" # new local
           println(x)
       end
greet (generic function with 1 method)

julia> greet()
hello

julia> x # global
ERROR: UndefVarError: `x` not defined in `Main`
```
Inside of the `greet` function, the assignment `x = "hello"` causes `x` to be a new local variable in the function's scope. There are two relevant facts: the assignment occurs in local scope and there is no existing local `x` variable. Since `x` is local, it doesn't matter if there is a global named `x` or not. Here for example we define `x = 123` before defining and calling `greet`:
```julia-repl
julia> x = 123 # global
123

julia> function greet()
           x = "hello" # new local
           println(x)
       end
greet (generic function with 1 method)

julia> greet()
hello

julia> x # global
123
```
Since the `x` in `greet` is local, the value (or lack thereof) of the global `x` is unaffected by calling `greet`. The hard scope rule doesn't care whether a global named `x` exists or not: assignment to `x` in a hard scope is local (unless `x` is declared global).


## [Local Scope](#local-scope)
The next clear cut situation we'll consider is when there is already a local variable named `x`, in which case `x = <value>` always assigns to this existing local `x`. This is true whether the assignment occurs in the same local scope, an inner local scope in the same function body, or in the body of a function nested inside of another function, also known as a [closure](https://en.wikipedia.org/wiki/Closure_(computer_programming)).
We'll use the `sum_to` function, which computes the sum of integers from one up to `n`, as an example:
```julia
function sum_to(n)
    s = 0 # new local
    for i = 1:n
        s = s + i # assign existing local
    end
    return s # same local
end
```
As in the previous example, the first assignment to `s` at the top of `sum_to` causes `s` to be a new local variable in the body of the function. The `for` loop has its own inner local scope within the function scope. At the point where `s = s + i` occurs, `s` is already a local variable, so the assignment updates the existing `s` instead of creating a new local. We can test this out by calling `sum_to` in the REPL:
```julia-repl
julia> function sum_to(n)
           s = 0 # new local
           for i = 1:n
               s = s + i # assign existing local
           end
           return s # same local
       end
sum_to (generic function with 1 method)

julia> sum_to(10)
55

julia> s # global
ERROR: UndefVarError: `s` not defined in `Main`
```


## [Local Scope](#local-scope)
Since `s` is local to the function `sum_to`, calling the function has no effect on the global variable `s`. We can also see that the update `s = s + i` in the `for` loop must have updated the same `s` created by the initialization `s = 0` since we get the correct sum of 55 for the integers 1 through 10.
Let's dig into the fact that the `for` loop body has its own scope for a second by writing a slightly more verbose variation which we'll call `sum_to_def`, in which we save the sum `s + i` in a variable `t` before updating `s`:
```julia-repl
julia> function sum_to_def(n)
           s = 0 # new local
           for i = 1:n
               t = s + i # new local `t`
               s = t # assign existing local `s`
           end
           return s, @isdefined(t)
       end
sum_to_def (generic function with 1 method)

julia> sum_to_def(10)
(55, false)
```
This version returns `s` as before but it also uses the `@isdefined` macro to return a boolean indicating whether there is a local variable named `t` defined in the function's outermost local scope. As you can see, there is no `t` defined outside of the `for` loop body. This is because of the hard scope rule again: since the assignment to `t` occurs inside of a function, which introduces a hard scope, the assignment causes `t` to become a new local variable in the local scope where it appears, i.e. inside of the loop body. Even if there were a global named `t`, it would make no difference—the hard scope rule isn't affected by anything in global scope.


## [Local Scope](#local-scope)
Note that the local scope of a for loop body is no different from the local scope of an inner function. This means that we could rewrite this example so that the loop body is implemented as a call to an inner helper function and it behaves the same way:
```julia-repl
julia> function sum_to_def_closure(n)
           function loop_body(i)
               t = s + i # new local `t`
               s = t # assign same local `s` as below
           end
           s = 0 # new local
           for i = 1:n
               loop_body(i)
           end
           return s, @isdefined(t)
       end
sum_to_def_closure (generic function with 1 method)

julia> sum_to_def_closure(10)
(55, false)
```
This example illustrates a couple of key points:
1.  Inner function scopes are just like any other nested local scope. In particular, if a variable is already a local outside of an inner function and you assign to it in the inner function, the outer local variable is updated.
2.  It doesn't matter if the definition of an outer local happens below where it is updated, the rule remains the same. The entire enclosing local scope is parsed and its locals determined before inner local meanings are resolved.
This design means that you can generally move code in or out of an inner function without changing its meaning, which facilitates a number of common idioms in the language using closures (see [do blocks](../functions/#Do-Block-Syntax-for-Function-Arguments)).


## [Local Scope](#local-scope)
Let's move onto some more ambiguous cases covered by the soft scope rule. We'll explore this by extracting the bodies of the `greet` and `sum_to_def` functions into soft scope contexts. First, let's put the body of `greet` in a `for` loop—which is soft, rather than hard—and evaluate it in the REPL:
```julia-repl
julia> for i = 1:3
           x = "hello" # new local
           println(x)
       end
hello
hello
hello

julia> x
ERROR: UndefVarError: `x` not defined in `Main`
```
Since the global `x` is not defined when the `for` loop is evaluated, the first clause of the soft scope rule applies and `x` is created as local to the `for` loop and therefore global `x` remains undefined after the loop executes. Next, let's consider the body of `sum_to_def` extracted into global scope, fixing its argument to `n = 10`
```julia
s = 0
for i = 1:10
    t = s + i
    s = t
end
s
@isdefined(t)
```
What does this code do? Hint: it's a trick question. The answer is "it depends." If this code is entered interactively, it behaves the same way it does in a function body. But if the code appears in a file, it prints an ambiguity warning and throws an undefined variable error. Let's see it working in the REPL first:
```julia-repl
julia> s = 0 # global
0

julia> for i = 1:10
           t = s + i # new local `t`
           s = t # assign global `s`
       end

julia> s # global
55

julia> @isdefined(t) # global
false
```


## [Local Scope](#local-scope)
The REPL approximates being in the body of a function by deciding whether assignment inside the loop assigns to a global or creates new local based on whether a global variable by that name is defined or not. If a global by the name exists, then the assignment updates it. If no global exists, then the assignment creates a new local variable. In this example we see both cases in action:
-   There is no global named `t`, so `t = s + i` creates a new `t` that is local to the `for` loop;
-   There is a global named `s`, so `s = t` assigns to it.
The second fact is why execution of the loop changes the global value of `s` and the first fact is why `t` is still undefined after the loop executes. Now, let's try evaluating this same code as though it were in a file instead:
```julia-repl
julia> code = """
       s = 0 # global
       for i = 1:10
           t = s + i # new local `t`
           s = t # new local `s` with warning
       end
       s, # global
       @isdefined(t) # global
       """;

julia> include_string(Main, code)
┌ Warning: Assignment to `s` in soft scope is ambiguous because a global variable by the same name exists: `s` will be treated as a new local. Disambiguate by using `local s` to suppress this warning or `global s` to assign to the existing global variable.
└ @ string:4
ERROR: LoadError: UndefVarError: `s` not defined in local scope
```


## [Local Scope](#local-scope)
Here we use [`include_string`](../../base/base/#Base.include_string), to evaluate `code` as though it were the contents of a file. We could also save `code` to a file and then call `include` on that file—the result would be the same. As you can see, this behaves quite different from evaluating the same code in the REPL. Let's break down what's happening here:
-   global `s` is defined with the value `0` before the loop is evaluated
-   the assignment `s = t` occurs in a soft scope—a `for` loop outside of any function body or other hard scope construct
-   therefore the second clause of the soft scope rule applies, and the assignment is ambiguous so a warning is emitted
-   execution continues, making `s` local to the `for` loop body
-   since `s` is local to the `for` loop, it is undefined when `t = s + i` is evaluated, causing an error
-   evaluation stops there, but if it got to `s` and `@isdefined(t)`, it would return `0` and `false`.


## [Local Scope](#local-scope)
This demonstrates some important aspects of scope: in a scope, each variable can only have one meaning, and that meaning is determined regardless of the order of expressions. The presence of the expression `s = t` in the loop causes `s` to be local to the loop, which means that it is also local when it appears on the right hand side of `t = s + i`, even though that expression appears first and is evaluated first. One might imagine that the `s` on the first line of the loop could be global while the `s` on the second line of the loop is local, but that's not possible since the two lines are in the same scope block and each variable can only mean one thing in a given scope.


#### [On Soft Scope](#on-soft-scope)
We have now covered all the local scope rules, but before wrapping up this section, perhaps a few words should be said about why the ambiguous soft scope case is handled differently in interactive and non-interactive contexts. There are two obvious questions one could ask:
1.  Why doesn't it just work like the REPL everywhere?
2.  Why doesn't it just work like in files everywhere? And maybe skip the warning?
In Julia ≤ 0.6, all global scopes did work like the current REPL: when `x = <value>` occurred in a loop (or `try`/`catch`, or `struct` body) but outside of a function body (or `let` block or comprehension), it was decided based on whether a global named `x` was defined or not whether `x` should be local to the loop. This behavior has the advantage of being intuitive and convenient since it approximates the behavior inside of a function body as closely as possible. In particular, it makes it easy to move code back and forth between a function body and the REPL when trying to debug the behavior of a function. However, it has some downsides. First, it's quite a complex behavior: many people over the years were confused about this behavior and complained that it was complicated and hard both to explain and understand. Fair point. Second, and arguably worse, is that it's bad for programming "at scale." When you see a small piece of code in one place like this, it's quite clear what's going on:
```julia
s = 0
for i = 1:10
    s += i
end
```


## [Local Scope](#local-scope)
Obviously the intention is to modify the existing global variable `s`. What else could it mean? However, not all real world code is so short or so clear. We found that code like the following often occurs in the wild:
```julia
x = 123

# much later
# maybe in a different file

for i = 1:10
    x = "hello"
    println(x)
end

# much later
# maybe in yet another file
# or maybe back in the first one where `x = 123`

y = x + 234
```
It's far less clear what should happen here. Since `x + "hello"` is a method error, it seems probable that the intention is for `x` to be local to the `for` loop. But runtime values and what methods happen to exist cannot be used to determine the scopes of variables. With the Julia ≤ 0.6 behavior, it's especially concerning that someone might have written the `for` loop first, had it working just fine, but later when someone else adds a new global far away—possibly in a different file—the code suddenly changes meaning and either breaks noisily or, worse still, silently does the wrong thing. This kind of ["spooky action at a distance"](https://en.wikipedia.org/wiki/Action_at_a_distance_(computer_programming)) is something that good programming language designs should prevent.


## [Local Scope](#local-scope)
So in Julia 1.0, we simplified the rules for scope: in any local scope, assignment to a name that wasn't already a local variable created a new local variable. This eliminated the notion of soft scope entirely as well as removing the potential for spooky action. We uncovered and fixed a significant number of bugs due to the removal of soft scope, vindicating the choice to get rid of it. And there was much rejoicing! Well, no, not really. Because some people were angry that they now had to write:
```julia
s = 0
for i = 1:10
    global s += i
end
```
Do you see that `global` annotation in there? Hideous. Obviously this situation could not be tolerated. But seriously, there are two main issues with requiring `global` for this kind of top-level code:
1.  It's no longer convenient to copy and paste the code from inside a function body into the REPL to debug it—you have to add `global` annotations and then remove them again to go back;
2.  Beginners will write this kind of code without the `global` and have no idea why their code doesn't work—the error that they get is that `s` is undefined, which does not seem to enlighten anyone who happens to make this mistake.
As of Julia 1.5, this code works without the `global` annotation in interactive contexts like the REPL or Jupyter notebooks (just like Julia 0.6) and in files and other non-interactive contexts, it prints this very direct warning:


## [Local Scope](#local-scope)
> Assignment to `s` in soft scope is ambiguous because a global variable by the same name exists: `s` will be treated as a new local. Disambiguate by using `local s` to suppress this warning or `global s` to assign to the existing global variable.
This addresses both issues while preserving the "programming at scale" benefits of the 1.0 behavior: global variables have no spooky effect on the meaning of code that may be far away; in the REPL copy-and-paste debugging works and beginners don't have any issues; any time someone either forgets a `global` annotation or accidentally shadows an existing global with a local in a soft scope, which would be confusing anyway, they get a nice clear warning.
An important property of this design is that any code that executes in a file without a warning will behave the same way in a fresh REPL. And on the flip side, if you take a REPL session and save it to file, if it behaves differently than it did in the REPL, then you will get a warning.


### [Let Blocks](#Let-Blocks)
`let` statements create a new *hard scope* block (see above) and introduce new variable bindings each time they run. The variable need not be immediately assigned:
```julia-repl
julia> var1 = let x
           for i in 1:5
               (i == 4) && (x = i; break)
           end
           x
       end
4
```
Whereas assignments might reassign a new value to an existing value location, `let` always creates a new location. This difference is usually not important, and is only detectable in the case of variables that outlive their scope via closures. The `let` syntax accepts a comma-separated series of assignments and variable names:
```julia-repl
julia> x, y, z = -1, -1, -1;

julia> let x = 1, z
           println("x: $x, y: $y") # x is local variable, y the global
           println("z: $z") # errors as z has not been assigned yet but is local
       end
x: 1, y: -1
ERROR: UndefVarError: `z` not defined in local scope
```
The assignments are evaluated in order, with each right-hand side evaluated in the scope before the new variable on the left-hand side has been introduced. Therefore it makes sense to write something like `let x = x` since the two `x` variables are distinct and have separate storage. Here is an example where the behavior of `let` is needed:
```julia-repl
julia> Fs = Vector{Any}(undef, 2); i = 1;

julia> while i <= 2
           Fs[i] = ()->i
           global i += 1
       end

julia> Fs[1]()
3

julia> Fs[2]()
3
```


### [Let Blocks](#Let-Blocks)
Here we create and store two closures that return variable `i`. However, it is always the same variable `i`, so the two closures behave identically. We can use `let` to create a new binding for `i`:
```julia-repl
julia> Fs = Vector{Any}(undef, 2); i = 1;

julia> while i <= 2
           let i = i
               Fs[i] = ()->i
           end
           global i += 1
       end

julia> Fs[1]()
1

julia> Fs[2]()
2
```
Since the `begin` construct does not introduce a new scope, it can be useful to use a zero-argument `let` to just introduce a new scope block without creating any new bindings immediately:
```julia-repl
julia> let
           local x = 1
           let
               local x = 2
           end
           x
       end
1
```
Since `let` introduces a new scope block, the inner local `x` is a different variable than the outer local `x`. This particular example is equivalent to:
```julia-repl
julia> let x = 1
           let x = 2
           end
           x
       end
1
```


### [Loops and Comprehensions](#Loops-and-Comprehensions)
In loops and [comprehensions](../arrays/#man-comprehensions), new variables introduced in their body scopes are freshly allocated for each loop iteration, as if the loop body were surrounded by a `let` block, as demonstrated by this example:
```julia-repl
julia> Fs = Vector{Any}(undef, 2);

julia> for j = 1:2
           Fs[j] = ()->j
       end

julia> Fs[1]()
1

julia> Fs[2]()
2
```
A `for` loop or comprehension iteration variable is always a new variable:
```julia-repl
julia> function f()
           i = 0
           for i = 1:3
               # empty
           end
           return i
       end;

julia> f()
0
```
However, it is occasionally useful to reuse an existing local variable as the iteration variable. This can be done conveniently by adding the keyword `outer`:
```julia-repl
julia> function f()
           i = 0
           for outer i = 1:3
               # empty
           end
           return i
       end;

julia> f()
3
```


## [Constants](#Constants)
A common use of variables is giving names to specific, unchanging values. Such variables are only assigned once. This intent can be conveyed to the compiler using the [`const`](../../base/base/#const) keyword:
```julia-repl
julia> const e  = 2.71828182845904523536;

julia> const pi = 3.14159265358979323846;
```
Multiple variables can be declared in a single `const` statement:
```julia-repl
julia> const a, b = 1, 2
(1, 2)
```
The `const` declaration should only be used in global scope on globals. It is difficult for the compiler to optimize code involving global variables, since their values (or even their types) might change at almost any time. If a global variable will not change, adding a `const` declaration solves this performance problem.
Local constants are quite different. The compiler is able to determine automatically when a local variable is constant, so local constant declarations are not necessary, and in fact are currently not supported.
Special top-level assignments, such as those performed by the `function` and `struct` keywords, are constant by default.
Note that `const` only affects the variable binding; the variable may be bound to a mutable object (such as an array), and that object may still be modified. Additionally when one tries to assign a value to a variable that is declared constant the following scenarios are possible:
-   if a new value has a different type than the type of the constant then an error is thrown:
```julia-repl
julia> const x = 1.0
1.0

julia> x = 1
ERROR: invalid redefinition of constant x
```


## [Constants](#Constants)
-   if a new value has the same type as the constant then a warning is printed:
```julia-repl
julia> const y = 1.0
1.0

julia> y = 2.0
WARNING: redefinition of constant y. This may fail, cause incorrect answers, or produce other errors.
2.0
```
-   if an assignment would not result in the change of variable value no message is given:
```julia-repl
julia> const z = 100
100

julia> z = 100
100
```
The last rule applies to immutable objects even if the variable binding would change, e.g.:
```julia-repl
julia> const s1 = "1"
"1"

julia> s2 = "1"
"1"

julia> pointer.([s1, s2], 1)
2-element Array{Ptr{UInt8},1}:
 Ptr{UInt8} @0x00000000132c9638
 Ptr{UInt8} @0x0000000013dd3d18

julia> s1 = s2
"1"

julia> pointer.([s1, s2], 1)
2-element Array{Ptr{UInt8},1}:
 Ptr{UInt8} @0x0000000013dd3d18
 Ptr{UInt8} @0x0000000013dd3d18
```
However, for mutable objects the warning is printed as expected:
```julia-repl
julia> const a = [1]
1-element Vector{Int64}:
 1

julia> a = [1]
WARNING: redefinition of constant a. This may fail, cause incorrect answers, or produce other errors.
1-element Vector{Int64}:
 1
```
Note that although sometimes possible, changing the value of a `const` variable is strongly discouraged, and is intended only for convenience during interactive use. Changing constants can cause various problems or unexpected behaviors. For instance, if a method references a constant and is already compiled before the constant is changed, then it might keep using the old value:


## [Constants](#Constants)
```julia-repl
julia> const x = 1
1

julia> f() = x
f (generic function with 1 method)

julia> f()
1

julia> x = 2
WARNING: redefinition of constant x. This may fail, cause incorrect answers, or produce other errors.
2

julia> f()
1
```


## [Typed Globals](#man-typed-globals)
Support for typed globals was added in Julia 1.8
Similar to being declared as constants, global bindings can also be declared to always be of a constant type. This can either be done without assigning an actual value using the syntax `global x::T` or upon assignment as `x::T = 123`.
```julia-repl
julia> x::Float64 = 2.718
2.718

julia> f() = x
f (generic function with 1 method)

julia> Base.return_types(f)
1-element Vector{Any}:
 Float64
```
For any assignment to a global, Julia will first try to convert it to the appropriate type using [`convert`](../../base/base/#Base.convert):
```julia-repl
julia> global y::Int

julia> y = 1.0
1.0

julia> y
1

julia> y = 3.14
ERROR: InexactError: Int64(3.14)
Stacktrace:
[...]
```
The type does not need to be concrete, but annotations with abstract types typically have little performance benefit.
Once a global has either been assigned to or its type has been set, the binding type is not allowed to change:
```julia-repl
julia> x = 1
1

julia> global x::Int
ERROR: cannot set type for global x. It already has a value or is already set to a different type.
Stacktrace:
[...]
```
------------------------------------------------------------------------


# Types · The Julia Language
Source: https://docs.julialang.org/en/v1/manual/types/


# [Types](#man-types)
Type systems have traditionally fallen into two quite different camps: static type systems, where every program expression must have a type computable before the execution of the program, and dynamic type systems, where nothing is known about types until run time, when the actual values manipulated by the program are available. Object orientation allows some flexibility in statically typed languages by letting code be written without the precise types of values being known at compile time. The ability to write code that can operate on different types is called polymorphism. All code in classic dynamically typed languages is polymorphic: only by explicitly checking types, or when objects fail to support operations at run-time, are the types of any values ever restricted.
Julia's type system is dynamic, but gains some of the advantages of static type systems by making it possible to indicate that certain values are of specific types. This can be of great assistance in generating efficient code, but even more significantly, it allows method dispatch on the types of function arguments to be deeply integrated with the language. Method dispatch is explored in detail in [Methods](../methods/#Methods), but is rooted in the type system presented here.


# [Types](#man-types)
The default behavior in Julia when types are omitted is to allow values to be of any type. Thus, one can write many useful Julia functions without ever explicitly using types. When additional expressiveness is needed, however, it is easy to gradually introduce explicit type annotations into previously "untyped" code. Adding annotations serves three primary purposes: to take advantage of Julia's powerful multiple-dispatch mechanism, to improve human readability, and to catch programmer errors.
Describing Julia in the lingo of [type systems](https://en.wikipedia.org/wiki/Type_system), it is: dynamic, nominative and parametric. Generic types can be parameterized, and the hierarchical relationships between types are [explicitly declared](https://en.wikipedia.org/wiki/Nominal_type_system), rather than [implied by compatible structure](https://en.wikipedia.org/wiki/Structural_type_system). One particularly distinctive feature of Julia's type system is that concrete types may not subtype each other: all concrete types are final and may only have abstract types as their supertypes. While this might at first seem unduly restrictive, it has many beneficial consequences with surprisingly few drawbacks. It turns out that being able to inherit behavior is much more important than being able to inherit structure, and inheriting both causes significant difficulties in traditional object-oriented languages. Other high-level aspects of Julia's type system that should be mentioned up front are:


# [Types](#man-types)
-   There is no division between object and non-object values: all values in Julia are true objects having a type that belongs to a single, fully connected type graph, all nodes of which are equally first-class as types.
-   There is no meaningful concept of a "compile-time type": the only type a value has is its actual type when the program is running. This is called a "run-time type" in object-oriented languages where the combination of static compilation with polymorphism makes this distinction significant.
-   Only values, not variables, have types – variables are simply names bound to values, although for simplicity we may say "type of a variable" as shorthand for "type of the value to which a variable refers".
-   Both abstract and concrete types can be parameterized by other types. They can also be parameterized by symbols, by values of any type for which [`isbits`](../../base/base/#Base.isbits) returns true (essentially, things like numbers and bools that are stored like C types or `struct`s with no pointers to other objects), and also by tuples thereof. Type parameters may be omitted when they do not need to be referenced or restricted.
Julia's type system is designed to be powerful and expressive, yet clear, intuitive and unobtrusive. Many Julia programmers may never feel the need to write code that explicitly uses types. Some kinds of programming, however, become clearer, simpler, faster and more robust with declared types.


## [Type Declarations](#Type-Declarations)
The `::` operator can be used to attach type annotations to expressions and variables in programs. There are two primary reasons to do this:
1.  As an assertion to help confirm that your program works the way you expect, and
2.  To provide extra type information to the compiler, which can then improve performance in some cases.
When appended to an expression computing a value, the `::` operator is read as "is an instance of". It can be used anywhere to assert that the value of the expression on the left is an instance of the type on the right. When the type on the right is concrete, the value on the left must have that type as its implementation – recall that all concrete types are final, so no implementation is a subtype of any other. When the type is abstract, it suffices for the value to be implemented by a concrete type that is a subtype of the abstract type. If the type assertion is not true, an exception is thrown, otherwise, the left-hand value is returned:
```julia-repl
julia> (1+2)::AbstractFloat
ERROR: TypeError: in typeassert, expected AbstractFloat, got a value of type Int64

julia> (1+2)::Int
3
```
This allows a type assertion to be attached to any expression in-place.


## [Type Declarations](#Type-Declarations)
When appended to a variable on the left-hand side of an assignment, or as part of a `local` declaration, the `::` operator means something a bit different: it declares the variable to always have the specified type, like a type declaration in a statically-typed language such as C. Every value assigned to the variable will be converted to the declared type using [`convert`](../../base/base/#Base.convert):
```julia-repl
julia> function foo()
           x::Int8 = 100
           x
       end
foo (generic function with 1 method)

julia> x = foo()
100

julia> typeof(x)
Int8
```
This feature is useful for avoiding performance "gotchas" that could occur if one of the assignments to a variable changed its type unexpectedly.
This "declaration" behavior only occurs in specific contexts:
```julia
local x::Int8  # in a local declaration
x::Int8 = 10   # as the left-hand side of an assignment
```
and applies to the whole current scope, even before the declaration.
As of Julia 1.8, type declarations can now be used in global scope i.e. type annotations can be added to global variables to make accessing them type stable.
```julia
julia> x::Int = 10
10

julia> x = 3.5
ERROR: InexactError: Int64(3.5)

julia> function foo(y)
           global x = 15.8    # throws an error when foo is called
           return x + y
       end
foo (generic function with 1 method)

julia> foo(10)
ERROR: InexactError: Int64(15.8)
```
Declarations can also be attached to function definitions:


## [Type Declarations](#Type-Declarations)
```julia
function sinc(x)::Float64
    if x == 0
        return 1
    end
    return sin(pi*x)/(pi*x)
end
```
Returning from this function behaves just like an assignment to a variable with a declared type: the value is always converted to `Float64`.


## [Abstract Types](#man-abstract-types)
Abstract types cannot be instantiated, and serve only as nodes in the type graph, thereby describing sets of related concrete types: those concrete types which are their descendants. We begin with abstract types even though they have no instantiation because they are the backbone of the type system: they form the conceptual hierarchy which makes Julia's type system more than just a collection of object implementations.
Recall that in [Integers and Floating-Point Numbers](../integers-and-floating-point-numbers/#Integers-and-Floating-Point-Numbers), we introduced a variety of concrete types of numeric values: [`Int8`](../../base/numbers/#Core.Int8), [`UInt8`](../../base/numbers/#Core.UInt8), [`Int16`](../../base/numbers/#Core.Int16), [`UInt16`](../../base/numbers/#Core.UInt16), [`Int32`](../../base/numbers/#Core.Int32), [`UInt32`](../../base/numbers/#Core.UInt32), [`Int64`](../../base/numbers/#Core.Int64), [`UInt64`](../../base/numbers/#Core.UInt64), [`Int128`](../../base/numbers/#Core.Int128), [`UInt128`](../../base/numbers/#Core.UInt128), [`Float16`](../../base/numbers/#Core.Float16), [`Float32`](../../base/numbers/#Core.Float32), and [`Float64`](../../base/numbers/#Core.Float64). Although they have different representation sizes, `Int8`, `Int16`, `Int32`, `Int64` and `Int128` all have in common that they are signed integer types. Likewise `UInt8`, `UInt16`, `UInt32`, `UInt64` and `UInt128` are all unsigned integer types, while `Float16`, `Float32` and `Float64` are distinct in being floating-point types rather than integers. It is common for a piece of code to make sense, for example, only if its arguments are some kind of integer, but not really depend on what particular *kind* of integer. For example, the greatest common denominator algorithm works for all kinds of integers, but will not work for floating-point numbers. Abstract types allow the construction of a hierarchy of types, providing a context into which concrete types can fit. This allows you, for example, to easily program to any type that is an integer, without restricting an algorithm to a specific type of integer.


## [Abstract Types](#man-abstract-types)
Abstract types are declared using the [`abstract type`](../../base/base/#abstract%20type) keyword. The general syntaxes for declaring an abstract type are:
```julia
abstract type «name» end
abstract type «name» <: «supertype» end
```
The `abstract type` keyword introduces a new abstract type, whose name is given by `«name»`. This name can be optionally followed by [`<:`](../../base/base/#Core.:%3C:) and an already-existing type, indicating that the newly declared abstract type is a subtype of this "parent" type.
When no supertype is given, the default supertype is `Any` – a predefined abstract type that all objects are instances of and all types are subtypes of. In type theory, `Any` is commonly called "top" because it is at the apex of the type graph. Julia also has a predefined abstract "bottom" type, at the nadir of the type graph, which is written as `Union{}`. It is the exact opposite of `Any`: no object is an instance of `Union{}` and all types are supertypes of `Union{}`.
Let's consider some of the abstract types that make up Julia's numerical hierarchy:
```julia
abstract type Number end
abstract type Real          <: Number end
abstract type AbstractFloat <: Real end
abstract type Integer       <: Real end
abstract type Signed        <: Integer end
abstract type Unsigned      <: Integer end
```


## [Abstract Types](#man-abstract-types)
The [`Number`](../../base/numbers/#Core.Number) type is a direct child type of `Any`, and [`Real`](../../base/numbers/#Core.Real) is its child. In turn, `Real` has two children (it has more, but only two are shown here; we'll get to the others later): [`Integer`](../../base/numbers/#Core.Integer) and [`AbstractFloat`](../../base/numbers/#Core.AbstractFloat), separating the world into representations of integers and representations of real numbers. Representations of real numbers include floating-point types, but also include other types, such as rationals. `AbstractFloat` includes only floating-point representations of real numbers. Integers are further subdivided into [`Signed`](../../base/numbers/#Core.Signed) and [`Unsigned`](../../base/numbers/#Core.Unsigned) varieties.
The `<:` operator in general means "is a subtype of", and, used in declarations like those above, declares the right-hand type to be an immediate supertype of the newly declared type. It can also be used in expressions as a subtype operator which returns `true` when its left operand is a subtype of its right operand:
```julia-repl
julia> Integer <: Number
true

julia> Integer <: AbstractFloat
false
```
An important use of abstract types is to provide default implementations for concrete types. To give a simple example, consider:
```julia
function myplus(x,y)
    x+y
end
```


## [Abstract Types](#man-abstract-types)
The first thing to note is that the above argument declarations are equivalent to `x::Any` and `y::Any`. When this function is invoked, say as `myplus(2,5)`, the dispatcher chooses the most specific method named `myplus` that matches the given arguments. (See [Methods](../methods/#Methods) for more information on multiple dispatch.)
Assuming no method more specific than the above is found, Julia next internally defines and compiles a method called `myplus` specifically for two `Int` arguments based on the generic function given above, i.e., it implicitly defines and compiles:
```julia
function myplus(x::Int,y::Int)
    x+y
end
```
and finally, it invokes this specific method.
Thus, abstract types allow programmers to write generic functions that can later be used as the default method by many combinations of concrete types. Thanks to multiple dispatch, the programmer has full control over whether the default or more specific method is used.
An important point to note is that there is no loss in performance if the programmer relies on a function whose arguments are abstract types, because it is recompiled for each tuple of concrete argument types with which it is invoked. (There may be a performance issue, however, in the case of function arguments that are containers of abstract types; see [Performance Tips](../performance-tips/#man-performance-abstract-container).)


## [Primitive Types](#Primitive-Types)
It is almost always preferable to wrap an existing primitive type in a new composite type than to define your own primitive type.
This functionality exists to allow Julia to bootstrap the standard primitive types that LLVM supports. Once they are defined, there is very little reason to define more.
A primitive type is a concrete type whose data consists of plain old bits. Classic examples of primitive types are integers and floating-point values. Unlike most languages, Julia lets you declare your own primitive types, rather than providing only a fixed set of built-in ones. In fact, the standard primitive types are all defined in the language itself:
```julia
primitive type Float16 <: AbstractFloat 16 end
primitive type Float32 <: AbstractFloat 32 end
primitive type Float64 <: AbstractFloat 64 end

primitive type Bool <: Integer 8 end
primitive type Char <: AbstractChar 32 end

primitive type Int8    <: Signed   8 end
primitive type UInt8   <: Unsigned 8 end
primitive type Int16   <: Signed   16 end
primitive type UInt16  <: Unsigned 16 end
primitive type Int32   <: Signed   32 end
primitive type UInt32  <: Unsigned 32 end
primitive type Int64   <: Signed   64 end
primitive type UInt64  <: Unsigned 64 end
primitive type Int128  <: Signed   128 end
primitive type UInt128 <: Unsigned 128 end
```
The general syntaxes for declaring a primitive type are:
```julia
primitive type «name» «bits» end
primitive type «name» <: «supertype» «bits» end
```


## [Primitive Types](#Primitive-Types)
The number of bits indicates how much storage the type requires and the name gives the new type a name. A primitive type can optionally be declared to be a subtype of some supertype. If a supertype is omitted, then the type defaults to having `Any` as its immediate supertype. The declaration of [`Bool`](../../base/numbers/#Core.Bool) above therefore means that a boolean value takes eight bits to store, and has [`Integer`](../../base/numbers/#Core.Integer) as its immediate supertype. Currently, only sizes that are multiples of 8 bits are supported and you are likely to experience LLVM bugs with sizes other than those used above. Therefore, boolean values, although they really need just a single bit, cannot be declared to be any smaller than eight bits.


## [Primitive Types](#Primitive-Types)
The types [`Bool`](../../base/numbers/#Core.Bool), [`Int8`](../../base/numbers/#Core.Int8) and [`UInt8`](../../base/numbers/#Core.UInt8) all have identical representations: they are eight-bit chunks of memory. Since Julia's type system is nominative, however, they are not interchangeable despite having identical structure. A fundamental difference between them is that they have different supertypes: [`Bool`](../../base/numbers/#Core.Bool)'s direct supertype is [`Integer`](../../base/numbers/#Core.Integer), [`Int8`](../../base/numbers/#Core.Int8)'s is [`Signed`](../../base/numbers/#Core.Signed), and [`UInt8`](../../base/numbers/#Core.UInt8)'s is [`Unsigned`](../../base/numbers/#Core.Unsigned). All other differences between [`Bool`](../../base/numbers/#Core.Bool), [`Int8`](../../base/numbers/#Core.Int8), and [`UInt8`](../../base/numbers/#Core.UInt8) are matters of behavior – the way functions are defined to act when given objects of these types as arguments. This is why a nominative type system is necessary: if structure determined type, which in turn dictates behavior, then it would be impossible to make [`Bool`](../../base/numbers/#Core.Bool) behave any differently than [`Int8`](../../base/numbers/#Core.Int8) or [`UInt8`](../../base/numbers/#Core.UInt8).


## [Composite Types](#Composite-Types)
[Composite types](https://en.wikipedia.org/wiki/Composite_data_type) are called records, structs, or objects in various languages. A composite type is a collection of named fields, an instance of which can be treated as a single value. In many languages, composite types are the only kind of user-definable type, and they are by far the most commonly used user-defined type in Julia as well.
In mainstream object oriented languages, such as C++, Java, Python and Ruby, composite types also have named functions associated with them, and the combination is called an "object". In purer object-oriented languages, such as Ruby or Smalltalk, all values are objects whether they are composites or not. In less pure object oriented languages, including C++ and Java, some values, such as integers and floating-point values, are not objects, while instances of user-defined composite types are true objects with associated methods. In Julia, all values are objects, but functions are not bundled with the objects they operate on. This is necessary since Julia chooses which method of a function to use by multiple dispatch, meaning that the types of *all* of a function's arguments are considered when selecting a method, rather than just the first one (see [Methods](../methods/#Methods) for more information on methods and dispatch). Thus, it would be inappropriate for functions to "belong" to only their first argument. Organizing methods into function objects rather than having named bags of methods "inside" each object ends up being a highly beneficial aspect of the language design.


## [Composite Types](#Composite-Types)
Composite types are introduced with the [`struct`](../../base/base/#struct) keyword followed by a block of field names, optionally annotated with types using the `::` operator:
```julia-repl
julia> struct Foo
           bar
           baz::Int
           qux::Float64
       end
```
Fields with no type annotation default to `Any`, and can accordingly hold any type of value.
New objects of type `Foo` are created by applying the `Foo` type object like a function to values for its fields:
```julia-repl
julia> foo = Foo("Hello, world.", 23, 1.5)
Foo("Hello, world.", 23, 1.5)

julia> typeof(foo)
Foo
```
When a type is applied like a function it is called a *constructor*. Two constructors are generated automatically (these are called *default constructors*). One accepts any arguments and calls [`convert`](../../base/base/#Base.convert) to convert them to the types of the fields, and the other accepts arguments that match the field types exactly. The reason both of these are generated is that this makes it easier to add new definitions without inadvertently replacing a default constructor.
Since the `bar` field is unconstrained in type, any value will do. However, the value for `baz` must be convertible to `Int`:
```julia-repl
julia> Foo((), 23.5, 1)
ERROR: InexactError: Int64(23.5)
Stacktrace:
[...]
```
You may find a list of field names using the [`fieldnames`](../../base/base/#Base.fieldnames) function.
```julia-repl
julia> fieldnames(Foo)
(:bar, :baz, :qux)
```


## [Composite Types](#Composite-Types)
You can access the field values of a composite object using the traditional `foo.bar` notation:
```julia-repl
julia> foo.bar
"Hello, world."

julia> foo.baz
23

julia> foo.qux
1.5
```
Composite objects declared with `struct` are *immutable*; they cannot be modified after construction. This may seem odd at first, but it has several advantages:
-   It can be more efficient. Some structs can be packed efficiently into arrays, and in some cases the compiler is able to avoid allocating immutable objects entirely.
-   It is not possible to violate the invariants provided by the type's constructors.
-   Code using immutable objects can be easier to reason about.
An immutable object might contain mutable objects, such as arrays, as fields. Those contained objects will remain mutable; only the fields of the immutable object itself cannot be changed to point to different objects.
Where required, mutable composite objects can be declared with the keyword [`mutable struct`](../../base/base/#mutable%20struct), to be discussed in the next section.
If all the fields of an immutable structure are indistinguishable (`===`) then two immutable values containing those fields are also indistinguishable:
```julia-repl
julia> struct X
           a::Int
           b::Float64
       end

julia> X(1, 2) === X(1, 2)
true
```


## [Composite Types](#Composite-Types)
There is much more to say about how instances of composite types are created, but that discussion depends on both [Parametric Types](#Parametric-Types) and on [Methods](../methods/#Methods), and is sufficiently important to be addressed in its own section: [Constructors](../constructors/#man-constructors).
For many user-defined types `X`, you may want to define a method [`Base.broadcastable(x::X) = Ref(x)`](../interfaces/#man-interfaces-broadcasting) so that instances of that type act as 0-dimensional "scalars" for [broadcasting](../arrays/#Broadcasting).


## [Mutable Composite Types](#Mutable-Composite-Types)
If a composite type is declared with `mutable struct` instead of `struct`, then instances of it can be modified:
```julia-repl
julia> mutable struct Bar
           baz
           qux::Float64
       end

julia> bar = Bar("Hello", 1.5);

julia> bar.qux = 2.0
2.0

julia> bar.baz = 1//2
1//2
```
An extra interface between the fields and the user can be provided through [Instance Properties](../interfaces/#man-instance-properties). This grants more control on what can be accessed and modified using the `bar.baz` notation.
In order to support mutation, such objects are generally allocated on the heap, and have stable memory addresses. A mutable object is like a little container that might hold different values over time, and so can only be reliably identified with its address. In contrast, an instance of an immutable type is associated with specific field values –- the field values alone tell you everything about the object. In deciding whether to make a type mutable, ask whether two instances with the same field values would be considered identical, or if they might need to change independently over time. If they would be considered identical, the type should probably be immutable.
To recap, two essential properties define immutability in Julia:
-   It is not permitted to modify the value of an immutable type.
    -   For bits types this means that the bit pattern of a value once set will never change and that value is the identity of a bits type.


## [Mutable Composite Types](#Mutable-Composite-Types)
    -   For composite types, this means that the identity of the values of its fields will never change. When the fields are bits types, that means their bits will never change, for fields whose values are mutable types like arrays, that means the fields will always refer to the same mutable value even though that mutable value's content may itself be modified.
-   An object with an immutable type may be copied freely by the compiler since its immutability makes it impossible to programmatically distinguish between the original object and a copy.
    -   In particular, this means that small enough immutable values like integers and floats are typically passed to functions in registers (or stack allocated).
    -   Mutable values, on the other hand are heap-allocated and passed to functions as pointers to heap-allocated values except in cases where the compiler is sure that there's no way to tell that this is not what is happening.
In cases where one or more fields of an otherwise mutable struct is known to be immutable, one can declare these fields as such using `const` as shown below. This enables some, but not all of the optimizations of immutable structs, and can be used to enforce invariants on the particular fields marked as `const`.
`const` annotating fields of mutable structs requires at least Julia 1.8.


## [Mutable Composite Types](#Mutable-Composite-Types)
```julia-repl
julia> mutable struct Baz
           a::Int
           const b::Float64
       end

julia> baz = Baz(1, 1.5);

julia> baz.a = 2
2

julia> baz.b = 2.0
ERROR: setfield!: const field .b of type Baz cannot be changed
[...]
```


## [Declared Types](#man-declared-types)
The three kinds of types (abstract, primitive, composite) discussed in the previous sections are actually all closely related. They share the same key properties:
-   They are explicitly declared.
-   They have names.
-   They have explicitly declared supertypes.
-   They may have parameters.
Because of these shared properties, these types are internally represented as instances of the same concept, `DataType`, which is the type of any of these types:
```julia-repl
julia> typeof(Real)
DataType

julia> typeof(Int)
DataType
```
A `DataType` may be abstract or concrete. If it is concrete, it has a specified size, storage layout, and (optionally) field names. Thus a primitive type is a `DataType` with nonzero size, but no field names. A composite type is a `DataType` that has field names or is empty (zero size).
Every concrete value in the system is an instance of some `DataType`.


## [Type Unions](#Type-Unions)
A type union is a special abstract type which includes as objects all instances of any of its argument types, constructed using the special [`Union`](../../base/base/#Core.Union) keyword:
```julia-repl
julia> IntOrString = Union{Int,AbstractString}
Union{Int64, AbstractString}

julia> 1 :: IntOrString
1

julia> "Hello!" :: IntOrString
"Hello!"

julia> 1.0 :: IntOrString
ERROR: TypeError: in typeassert, expected Union{Int64, AbstractString}, got a value of type Float64
```
The compilers for many languages have an internal union construct for reasoning about types; Julia simply exposes it to the programmer. The Julia compiler is able to generate efficient code in the presence of `Union` types with a small number of types <sup>[[1\]](#footnote-1)</sup>, by generating specialized code in separate branches for each possible type.
A particularly useful case of a `Union` type is `Union{T, Nothing}`, where `T` can be any type and [`Nothing`](../../base/base/#Core.Nothing) is the singleton type whose only instance is the object [`nothing`](../../base/constants/#Core.nothing). This pattern is the Julia equivalent of [`Nullable`, `Option` or `Maybe`](https://en.wikipedia.org/wiki/Nullable_type) types in other languages. Declaring a function argument or a field as `Union{T, Nothing}` allows setting it either to a value of type `T`, or to `nothing` to indicate that there is no value. See [this FAQ entry](../faq/#faq-nothing) for more information.


## [Parametric Types](#Parametric-Types)
An important and powerful feature of Julia's type system is that it is parametric: types can take parameters, so that type declarations actually introduce a whole family of new types – one for each possible combination of parameter values. There are many languages that support some version of [generic programming](https://en.wikipedia.org/wiki/Generic_programming), wherein data structures and algorithms to manipulate them may be specified without specifying the exact types involved. For example, some form of generic programming exists in ML, Haskell, Ada, Eiffel, C++, Java, C#, F#, and Scala, just to name a few. Some of these languages support true parametric polymorphism (e.g. ML, Haskell, Scala), while others support ad-hoc, template-based styles of generic programming (e.g. C++, Java). With so many different varieties of generic programming and parametric types in various languages, we won't even attempt to compare Julia's parametric types to other languages, but will instead focus on explaining Julia's system in its own right. We will note, however, that because Julia is a dynamically typed language and doesn't need to make all type decisions at compile time, many traditional difficulties encountered in static parametric type systems can be relatively easily handled.
All declared types (the `DataType` variety) can be parameterized, with the same syntax in each case. We will discuss them in the following order: first, parametric composite types, then parametric abstract types, and finally parametric primitive types.


### [Parametric Composite Types](#man-parametric-composite-types)
Type parameters are introduced immediately after the type name, surrounded by curly braces:
```julia-repl
julia> struct Point{T}
           x::T
           y::T
       end
```
This declaration defines a new parametric type, `Point{T}`, holding two "coordinates" of type `T`. What, one may ask, is `T`? Well, that's precisely the point of parametric types: it can be any type at all (or a value of any bits type, actually, although here it's clearly used as a type). `Point{Float64}` is a concrete type equivalent to the type defined by replacing `T` in the definition of `Point` with [`Float64`](../../base/numbers/#Core.Float64). Thus, this single declaration actually declares an unlimited number of types: `Point{Float64}`, `Point{AbstractString}`, `Point{Int64}`, etc. Each of these is now a usable concrete type:
```julia-repl
julia> Point{Float64}
Point{Float64}

julia> Point{AbstractString}
Point{AbstractString}
```
The type `Point{Float64}` is a point whose coordinates are 64-bit floating-point values, while the type `Point{AbstractString}` is a "point" whose "coordinates" are string objects (see [Strings](../../devdocs/ast/#Strings)).
`Point` itself is also a valid type object, containing all instances `Point{Float64}`, `Point{AbstractString}`, etc. as subtypes:
```julia-repl
julia> Point{Float64} <: Point
true

julia> Point{AbstractString} <: Point
true
```
Other types, of course, are not subtypes of it:
```julia-repl
julia> Float64 <: Point
false

julia> AbstractString <: Point
false
```


### [Parametric Composite Types](#man-parametric-composite-types)
Concrete `Point` types with different values of `T` are never subtypes of each other:
```julia-repl
julia> Point{Float64} <: Point{Int64}
false

julia> Point{Float64} <: Point{Real}
false
```
This last point is *very* important: even though `Float64 <: Real` we **DO NOT** have `Point{Float64} <: Point{Real}`.
In other words, in the parlance of type theory, Julia's type parameters are *invariant*, rather than being [covariant (or even contravariant)](https://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29). This is for practical reasons: while any instance of `Point{Float64}` may conceptually be like an instance of `Point{Real}` as well, the two types have different representations in memory:
-   An instance of `Point{Float64}` can be represented compactly and efficiently as an immediate pair of 64-bit values;
-   An instance of `Point{Real}` must be able to hold any pair of instances of [`Real`](../../base/numbers/#Core.Real). Since objects that are instances of `Real` can be of arbitrary size and structure, in practice an instance of `Point{Real}` must be represented as a pair of pointers to individually allocated `Real` objects.


### [Parametric Composite Types](#man-parametric-composite-types)
The efficiency gained by being able to store `Point{Float64}` objects with immediate values is magnified enormously in the case of arrays: an `Array{Float64}` can be stored as a contiguous memory block of 64-bit floating-point values, whereas an `Array{Real}` must be an array of pointers to individually allocated [`Real`](../../base/numbers/#Core.Real) objects – which may well be [boxed](https://en.wikipedia.org/wiki/Object_type_%28object-oriented_programming%29#Boxing) 64-bit floating-point values, but also might be arbitrarily large, complex objects, which are declared to be implementations of the `Real` abstract type.
Since `Point{Float64}` is not a subtype of `Point{Real}`, the following method can't be applied to arguments of type `Point{Float64}`:
```julia
function norm(p::Point{Real})
    sqrt(p.x^2 + p.y^2)
end
```
A correct way to define a method that accepts all arguments of type `Point{T}` where `T` is a subtype of [`Real`](../../base/numbers/#Core.Real) is:
```julia
function norm(p::Point{<:Real})
    sqrt(p.x^2 + p.y^2)
end
```
(Equivalently, one could define `function norm(p::Point{T} where T<:Real)` or `function norm(p::Point{T}) where T<:Real`; see [UnionAll Types](#UnionAll-Types).)
More examples will be discussed later in [Methods](../methods/#Methods).


### [Parametric Composite Types](#man-parametric-composite-types)
How does one construct a `Point` object? It is possible to define custom constructors for composite types, which will be discussed in detail in [Constructors](../constructors/#man-constructors), but in the absence of any special constructor declarations, there are two default ways of creating new composite objects, one in which the type parameters are explicitly given and the other in which they are implied by the arguments to the object constructor.
Since the type `Point{Float64}` is a concrete type equivalent to `Point` declared with [`Float64`](../../base/numbers/#Core.Float64) in place of `T`, it can be applied as a constructor accordingly:
```julia-repl
julia> p = Point{Float64}(1.0, 2.0)
Point{Float64}(1.0, 2.0)

julia> typeof(p)
Point{Float64}
```
For the default constructor, exactly one argument must be supplied for each field:
```julia-repl
julia> Point{Float64}(1.0)
ERROR: MethodError: no method matching Point{Float64}(::Float64)
The type `Point{Float64}` exists, but no method is defined for this combination of argument types when trying to construct it.
[...]

julia> Point{Float64}(1.0, 2.0, 3.0)
ERROR: MethodError: no method matching Point{Float64}(::Float64, ::Float64, ::Float64)
The type `Point{Float64}` exists, but no method is defined for this combination of argument types when trying to construct it.
[...]
```
Only one default constructor is generated for parametric types, since overriding it is not possible. This constructor accepts any arguments and converts them to the field types.


### [Parametric Composite Types](#man-parametric-composite-types)
In many cases, it is redundant to provide the type of `Point` object one wants to construct, since the types of arguments to the constructor call already implicitly provide type information. For that reason, you can also apply `Point` itself as a constructor, provided that the implied value of the parameter type `T` is unambiguous:
```julia-repl
julia> p1 = Point(1.0,2.0)
Point{Float64}(1.0, 2.0)

julia> typeof(p1)
Point{Float64}

julia> p2 = Point(1,2)
Point{Int64}(1, 2)

julia> typeof(p2)
Point{Int64}
```
In the case of `Point`, the type of `T` is unambiguously implied if and only if the two arguments to `Point` have the same type. When this isn't the case, the constructor will fail with a [`MethodError`](../../base/base/#Core.MethodError):
```julia-repl
julia> Point(1,2.5)
ERROR: MethodError: no method matching Point(::Int64, ::Float64)
The type `Point` exists, but no method is defined for this combination of argument types when trying to construct it.

Closest candidates are:
  Point(::T, !Matched::T) where T
   @ Main none:2

Stacktrace:
[...]
```
Constructor methods to appropriately handle such mixed cases can be defined, but that will not be discussed until later on in [Constructors](../constructors/#man-constructors).


### [Parametric Abstract Types](#Parametric-Abstract-Types)
Parametric abstract type declarations declare a collection of abstract types, in much the same way:
```julia-repl
julia> abstract type Pointy{T} end
```
With this declaration, `Pointy{T}` is a distinct abstract type for each type or integer value of `T`. As with parametric composite types, each such instance is a subtype of `Pointy`:
```julia-repl
julia> Pointy{Int64} <: Pointy
true

julia> Pointy{1} <: Pointy
true
```
Parametric abstract types are invariant, much as parametric composite types are:
```julia-repl
julia> Pointy{Float64} <: Pointy{Real}
false

julia> Pointy{Real} <: Pointy{Float64}
false
```
The notation `Pointy{<:Real}` can be used to express the Julia analogue of a *covariant* type, while `Pointy{>:Int}` the analogue of a *contravariant* type, but technically these represent *sets* of types (see [UnionAll Types](#UnionAll-Types)).
```julia-repl
julia> Pointy{Float64} <: Pointy{<:Real}
true

julia> Pointy{Real} <: Pointy{>:Int}
true
```
Much as plain old abstract types serve to create a useful hierarchy of types over concrete types, parametric abstract types serve the same purpose with respect to parametric composite types. We could, for example, have declared `Point{T}` to be a subtype of `Pointy{T}` as follows:
```julia-repl
julia> struct Point{T} <: Pointy{T}
           x::T
           y::T
       end
```
Given such a declaration, for each choice of `T`, we have `Point{T}` as a subtype of `Pointy{T}`:


### [Parametric Abstract Types](#Parametric-Abstract-Types)
```julia-repl
julia> Point{Float64} <: Pointy{Float64}
true

julia> Point{Real} <: Pointy{Real}
true

julia> Point{AbstractString} <: Pointy{AbstractString}
true
```
This relationship is also invariant:
```julia-repl
julia> Point{Float64} <: Pointy{Real}
false

julia> Point{Float64} <: Pointy{<:Real}
true
```
What purpose do parametric abstract types like `Pointy` serve? Consider if we create a point-like implementation that only requires a single coordinate because the point is on the diagonal line *x = y*:
```julia-repl
julia> struct DiagPoint{T} <: Pointy{T}
           x::T
       end
```
Now both `Point{Float64}` and `DiagPoint{Float64}` are implementations of the `Pointy{Float64}` abstraction, and similarly for every other possible choice of type `T`. This allows programming to a common interface shared by all `Pointy` objects, implemented for both `Point` and `DiagPoint`. This cannot be fully demonstrated, however, until we have introduced methods and dispatch in the next section, [Methods](../methods/#Methods).
There are situations where it may not make sense for type parameters to range freely over all possible types. In such situations, one can constrain the range of `T` like so:
```julia-repl
julia> abstract type Pointy{T<:Real} end
```
With such a declaration, it is acceptable to use any type that is a subtype of [`Real`](../../base/numbers/#Core.Real) in place of `T`, but not types that are not subtypes of `Real`:


### [Parametric Abstract Types](#Parametric-Abstract-Types)
```julia-repl
julia> Pointy{Float64}
Pointy{Float64}

julia> Pointy{Real}
Pointy{Real}

julia> Pointy{AbstractString}
ERROR: TypeError: in Pointy, in T, expected T<:Real, got Type{AbstractString}

julia> Pointy{1}
ERROR: TypeError: in Pointy, in T, expected T<:Real, got a value of type Int64
```
Type parameters for parametric composite types can be restricted in the same manner:
```julia
struct Point{T<:Real} <: Pointy{T}
    x::T
    y::T
end
```
To give a real-world example of how all this parametric type machinery can be useful, here is the actual definition of Julia's [`Rational`](../../base/numbers/#Base.Rational) immutable type (except that we omit the constructor here for simplicity), representing an exact ratio of integers:
```julia
struct Rational{T<:Integer} <: Real
    num::T
    den::T
end
```
It only makes sense to take ratios of integer values, so the parameter type `T` is restricted to being a subtype of [`Integer`](../../base/numbers/#Core.Integer), and a ratio of integers represents a value on the real number line, so any [`Rational`](../../base/numbers/#Base.Rational) is an instance of the [`Real`](../../base/numbers/#Core.Real) abstraction.


### [Tuple Types](#Tuple-Types)
Tuples are an abstraction of the arguments of a function – without the function itself. The salient aspects of a function's arguments are their order and their types. Therefore a tuple type is similar to a parameterized immutable type where each parameter is the type of one field. For example, a 2-element tuple type resembles the following immutable type:
```julia
struct Tuple2{A,B}
    a::A
    b::B
end
```
However, there are three key differences:
-   Tuple types may have any number of parameters.
-   Tuple types are *covariant* in their parameters: `Tuple{Int}` is a subtype of `Tuple{Any}`. Therefore `Tuple{Any}` is considered an abstract type, and tuple types are only concrete if their parameters are.
-   Tuples do not have field names; fields are only accessed by index.
Tuple values are written with parentheses and commas. When a tuple is constructed, an appropriate tuple type is generated on demand:
```julia-repl
julia> typeof((1,"foo",2.5))
Tuple{Int64, String, Float64}
```
Note the implications of covariance:
```julia-repl
julia> Tuple{Int,AbstractString} <: Tuple{Real,Any}
true

julia> Tuple{Int,AbstractString} <: Tuple{Real,Real}
false

julia> Tuple{Int,AbstractString} <: Tuple{Real,}
false
```
Intuitively, this corresponds to the type of a function's arguments being a subtype of the function's signature (when the signature matches).


### [Vararg Tuple Types](#Vararg-Tuple-Types)
The last parameter of a tuple type can be the special value [`Vararg`](../../base/base/#Core.Vararg), which denotes any number of trailing elements:
```julia-repl
julia> mytupletype = Tuple{AbstractString,Vararg{Int}}
Tuple{AbstractString, Vararg{Int64}}

julia> isa(("1",), mytupletype)
true

julia> isa(("1",1), mytupletype)
true

julia> isa(("1",1,2), mytupletype)
true

julia> isa(("1",1,2,3.0), mytupletype)
false
```
Moreover `Vararg{T}` corresponds to zero or more elements of type `T`. Vararg tuple types are used to represent the arguments accepted by varargs methods (see [Varargs Functions](../functions/#Varargs-Functions)).
The special value `Vararg{T,N}` (when used as the last parameter of a tuple type) corresponds to exactly `N` elements of type `T`. `NTuple{N,T}` is a convenient alias for `Tuple{Vararg{T,N}}`, i.e. a tuple type containing exactly `N` elements of type `T`.


### [Named Tuple Types](#Named-Tuple-Types)
Named tuples are instances of the [`NamedTuple`](../../base/base/#Core.NamedTuple) type, which has two parameters: a tuple of symbols giving the field names, and a tuple type giving the field types. For convenience, `NamedTuple` types are printed using the [`@NamedTuple`](../../base/base/#Base.@NamedTuple) macro which provides a convenient `struct`-like syntax for declaring these types via `key::Type` declarations, where an omitted `::Type` corresponds to `::Any`.
```julia-repl
julia> typeof((a=1,b="hello")) # prints in macro form
@NamedTuple{a::Int64, b::String}

julia> NamedTuple{(:a, :b), Tuple{Int64, String}} # long form of the type
@NamedTuple{a::Int64, b::String}
```
The `begin ... end` form of the `@NamedTuple` macro allows the declarations to be split across multiple lines (similar to a struct declaration), but is otherwise equivalent:
```julia-repl
julia> @NamedTuple begin
           a::Int
           b::String
       end
@NamedTuple{a::Int64, b::String}
```
A `NamedTuple` type can be used as a constructor, accepting a single tuple argument. The constructed `NamedTuple` type can be either a concrete type, with both parameters specified, or a type that specifies only field names:
```julia-repl
julia> @NamedTuple{a::Float32,b::String}((1, ""))
(a = 1.0f0, b = "")

julia> NamedTuple{(:a, :b)}((1, ""))
(a = 1, b = "")
```
If field types are specified, the arguments are converted. Otherwise the types of the arguments are used directly.


### [Parametric Primitive Types](#Parametric-Primitive-Types)
Primitive types can also be declared parametrically. For example, pointers are represented as primitive types which would be declared in Julia like this:
```julia
# 32-bit system:
primitive type Ptr{T} 32 end

# 64-bit system:
primitive type Ptr{T} 64 end
```
The slightly odd feature of these declarations as compared to typical parametric composite types, is that the type parameter `T` is not used in the definition of the type itself – it is just an abstract tag, essentially defining an entire family of types with identical structure, differentiated only by their type parameter. Thus, `Ptr{Float64}` and `Ptr{Int64}` are distinct types, even though they have identical representations. And of course, all specific pointer types are subtypes of the umbrella [`Ptr`](../../base/c/#Core.Ptr) type:
```julia-repl
julia> Ptr{Float64} <: Ptr
true

julia> Ptr{Int64} <: Ptr
true
```


## [UnionAll Types](#UnionAll-Types)
We have said that a parametric type like `Ptr` acts as a supertype of all its instances (`Ptr{Int64}` etc.). How does this work? `Ptr` itself cannot be a normal data type, since without knowing the type of the referenced data the type clearly cannot be used for memory operations. The answer is that `Ptr` (or other parametric types like `Array`) is a different kind of type called a [`UnionAll`](../../base/base/#Core.UnionAll) type. Such a type expresses the *iterated union* of types for all values of some parameter.
`UnionAll` types are usually written using the keyword `where`. For example `Ptr` could be more accurately written as `Ptr{T} where T`, meaning all values whose type is `Ptr{T}` for some value of `T`. In this context, the parameter `T` is also often called a "type variable" since it is like a variable that ranges over types. Each `where` introduces a single type variable, so these expressions are nested for types with multiple parameters, for example `Array{T,N} where N where T`.


## [UnionAll Types](#UnionAll-Types)
The type application syntax `A{B,C}` requires `A` to be a `UnionAll` type, and first substitutes `B` for the outermost type variable in `A`. The result is expected to be another `UnionAll` type, into which `C` is then substituted. So `A{B,C}` is equivalent to `A{B}{C}`. This explains why it is possible to partially instantiate a type, as in `Array{Float64}`: the first parameter value has been fixed, but the second still ranges over all possible values. Using explicit `where` syntax, any subset of parameters can be fixed. For example, the type of all 1-dimensional arrays can be written as `Array{T,1} where T`.
Type variables can be restricted with subtype relations. `Array{T} where T<:Integer` refers to all arrays whose element type is some kind of [`Integer`](../../base/numbers/#Core.Integer). The syntax `Array{<:Integer}` is a convenient shorthand for `Array{T} where T<:Integer`. Type variables can have both lower and upper bounds. `Array{T} where Int<:T<:Number` refers to all arrays of [`Number`](../../base/numbers/#Core.Number)s that are able to contain `Int`s (since `T` must be at least as big as `Int`). The syntax `where T>:Int` also works to specify only the lower bound of a type variable, and `Array{>:Int}` is equivalent to `Array{T} where T>:Int`.


## [UnionAll Types](#UnionAll-Types)
Since `where` expressions nest, type variable bounds can refer to outer type variables. For example `Tuple{T,Array{S}} where S<:AbstractArray{T} where T<:Real` refers to 2-tuples whose first element is some [`Real`](../../base/numbers/#Core.Real), and whose second element is an `Array` of any kind of array whose element type contains the type of the first tuple element.
The `where` keyword itself can be nested inside a more complex declaration. For example, consider the two types created by the following declarations:
```julia-repl
julia> const T1 = Array{Array{T, 1} where T, 1}
Vector{Vector} (alias for Array{Array{T, 1} where T, 1})

julia> const T2 = Array{Array{T, 1}, 1} where T
Array{Vector{T}, 1} where T
```
Type `T1` defines a 1-dimensional array of 1-dimensional arrays; each of the inner arrays consists of objects of the same type, but this type may vary from one inner array to the next. On the other hand, type `T2` defines a 1-dimensional array of 1-dimensional arrays all of whose inner arrays must have the same type. Note that `T2` is an abstract type, e.g., `Array{Array{Int,1},1} <: T2`, whereas `T1` is a concrete type. As a consequence, `T1` can be constructed with a zero-argument constructor `a=T1()` but `T2` cannot.
There is a convenient syntax for naming such types, similar to the short form of function definition syntax:
```julia
Vector{T} = Array{T, 1}
```


## [UnionAll Types](#UnionAll-Types)
This is equivalent to `const Vector = Array{T,1} where T`. Writing `Vector{Float64}` is equivalent to writing `Array{Float64,1}`, and the umbrella type `Vector` has as instances all `Array` objects where the second parameter – the number of array dimensions – is 1, regardless of what the element type is. In languages where parametric types must always be specified in full, this is not especially helpful, but in Julia, this allows one to write just `Vector` for the abstract type including all one-dimensional dense arrays of any element type.


## [Singleton types](#man-singleton-types)
Immutable composite types with no fields are called *singletons*. Formally, if
1.  `T` is an immutable composite type (i.e. defined with `struct`),
2.  `a isa T && b isa T` implies `a === b`,
then `T` is a singleton type.<sup>[[2\]](#footnote-2)</sup> [`Base.issingletontype`](../../base/base/#Base.issingletontype) can be used to check if a type is a singleton type. [Abstract types](#man-abstract-types) cannot be singleton types by construction.
From the definition, it follows that there can be only one instance of such types:
```julia-repl
julia> struct NoFields
       end

julia> NoFields() === NoFields()
true

julia> Base.issingletontype(NoFields)
true
```
The [`===`](../../base/base/#Core.:===) function confirms that the constructed instances of `NoFields` are actually one and the same.
Parametric types can be singleton types when the above condition holds. For example,
```julia-repl
julia> struct NoFieldsParam{T}
       end

julia> Base.issingletontype(NoFieldsParam) # Can't be a singleton type ...
false

julia> NoFieldsParam{Int}() isa NoFieldsParam # ... because it has ...
true

julia> NoFieldsParam{Bool}() isa NoFieldsParam # ... multiple instances.
true

julia> Base.issingletontype(NoFieldsParam{Int}) # Parametrized, it is a singleton.
true

julia> NoFieldsParam{Int}() === NoFieldsParam{Int}()
true
```


## [Types of functions](#Types-of-functions)
Each function has its own type, which is a subtype of `Function`.
```julia-repl
julia> foo41(x) = x + 1
foo41 (generic function with 1 method)

julia> typeof(foo41)
typeof(foo41) (singleton type of function foo41, subtype of Function)
```
Note how `typeof(foo41)` prints as itself. This is merely a convention for printing, as it is a first-class object that can be used like any other value:
```julia-repl
julia> T = typeof(foo41)
typeof(foo41) (singleton type of function foo41, subtype of Function)

julia> T <: Function
true
```
Types of functions defined at top-level are singletons. When necessary, you can compare them with [`===`](../../base/base/#Core.:===).
[Closures](../functions/#man-anonymous-functions) also have their own type, which is usually printed with names that end in `#<number>`. Names and types for functions defined at different locations are distinct, but not guaranteed to be printed the same way across sessions.
```julia-repl
julia> typeof(x -> x + 1)
var"#9#10"
```
Types of closures are not necessarily singletons.
```julia-repl
julia> addy(y) = x -> x + y
addy (generic function with 1 method)

julia> typeof(addy(1)) === typeof(addy(2))
true

julia> addy(1) === addy(2)
false

julia> Base.issingletontype(typeof(addy(1)))
false
```


## [Type{T} type selectors](#man-typet-type)
For each type `T`, `Type{T}` is an abstract parametric type whose only instance is the object `T`. Until we discuss [Parametric Methods](../methods/#Parametric-Methods) and [conversions](../conversion-and-promotion/#conversion-and-promotion), it is difficult to explain the utility of this construct, but in short, it allows one to specialize function behavior on specific types as *values*. This is useful for writing methods (especially parametric ones) whose behavior depends on a type that is given as an explicit argument rather than implied by the type of one of its arguments.
Since the definition is a little difficult to parse, let's look at some examples:
```julia-repl
julia> isa(Float64, Type{Float64})
true

julia> isa(Real, Type{Float64})
false

julia> isa(Real, Type{Real})
true

julia> isa(Float64, Type{Real})
false
```
In other words, [`isa(A, Type{B})`](../../base/base/#Core.isa) is true if and only if `A` and `B` are the same object and that object is a type.
In particular, since parametric types are [invariant](#man-parametric-composite-types), we have
```julia-repl
julia> struct TypeParamExample{T}
           x::T
       end

julia> TypeParamExample isa Type{TypeParamExample}
true

julia> TypeParamExample{Int} isa Type{TypeParamExample}
false

julia> TypeParamExample{Int} isa Type{TypeParamExample{Int}}
true
```
Without the parameter, `Type` is simply an abstract type which has all type objects as its instances:


## [Type{T} type selectors](#man-typet-type)
```julia-repl
julia> isa(Type{Float64}, Type)
true

julia> isa(Float64, Type)
true

julia> isa(Real, Type)
true
```
Any object that is not a type is not an instance of `Type`:
```julia-repl
julia> isa(1, Type)
false

julia> isa("foo", Type)
false
```
While `Type` is part of Julia's type hierarchy like any other abstract parametric type, it is not commonly used outside method signatures except in some special cases. Another important use case for `Type` is sharpening field types which would otherwise be captured less precisely, e.g. as [`DataType`](#man-declared-types) in the example below where the default constructor could lead to performance problems in code relying on the precise wrapped type (similarly to [abstract type parameters](../performance-tips/#man-performance-abstract-container)).
```julia-repl
julia> struct WrapType{T}
       value::T
       end

julia> WrapType(Float64) # default constructor, note DataType
WrapType{DataType}(Float64)

julia> WrapType(::Type{T}) where T = WrapType{Type{T}}(T)
WrapType

julia> WrapType(Float64) # sharpened constructor, note more precise Type{Float64}
WrapType{Type{Float64}}(Float64)
```


## [Type Aliases](#Type-Aliases)
Sometimes it is convenient to introduce a new name for an already expressible type. This can be done with a simple assignment statement. For example, `UInt` is aliased to either [`UInt32`](../../base/numbers/#Core.UInt32) or [`UInt64`](../../base/numbers/#Core.UInt64) as is appropriate for the size of pointers on the system:
```julia-repl
# 32-bit system:
julia> UInt
UInt32

# 64-bit system:
julia> UInt
UInt64
```
This is accomplished via the following code in `base/boot.jl`:
```julia
if Int === Int64
    const UInt = UInt64
else
    const UInt = UInt32
end
```
Of course, this depends on what `Int` is aliased to – but that is predefined to be the correct type – either [`Int32`](../../base/numbers/#Core.Int32) or [`Int64`](../../base/numbers/#Core.Int64).
(Note that unlike `Int`, `Float` does not exist as a type alias for a specific sized [`AbstractFloat`](../../base/numbers/#Core.AbstractFloat). Unlike with integer registers, where the size of `Int` reflects the size of a native pointer on that machine, the floating point register sizes are specified by the IEEE-754 standard.)
Type aliases may be parametrized:
```julia-repl
julia> const Family{T} = Set{T}
Set

julia> Family{Char} === Set{Char}
true
```