english translation started

This commit is contained in:
2026-02-22 18:22:46 +01:00
parent 19c67a3c9b
commit 9e418381ca
17 changed files with 1915 additions and 1907 deletions

View File

@@ -15,22 +15,22 @@ Base.active_module() = myactive_module()
```
# Das Typsystem von Julia
# The Julia Type System
Man kann umfangreiche Programme in Julia schreiben, ohne auch nur eine einzige Typdeklaration verwenden zu müssen. Das ist natürlich Absicht und soll die Arbeit der Anwender vereinfachen.
One can write extensive programs in Julia without using a single type declaration. This is, of course, intentional and should simplify the work of users.
Wir blicken jetzt trotzdem mal unter die Motorhaube.
However, we will now take a look under the hood.
## Die Typhierarchie am Beispiel der numerischen Typen
## The Type Hierarchy with the Example of Numeric Types
Das Typsystem hat die Struktur eines Baums, dessen Wurzel der Typ `Any` ist. Mit den Funktionen `subtypes()` und `supertype()` kann man den Baum erforschen. Sie zeigen alle Kinder bzw. die Mutter eines Knotens an.
The type system has the structure of a tree whose root is the type `Any`. The functions `subtypes()` and `supertype()` can be used to explore the tree. They show all children or the parent of a node.
```{julia}
subtypes(Int64)
```
Das Ergebnis ist eine leere Liste von Typen. `Int64` ist ein sogenannter **konkreter Typ** und hat keine Untertypen.
The result is an empty list of types. `Int64` is a so-called **concrete type** and has no subtypes.
Wir klettern jetzt mal die Typhierarchie auf diesem Ast nach oben bis zur Wurzel (Informatiker-Bäume stehen bekanntlich immer auf dem Kopf).
Let's now climb the type hierarchy on this branch to the root (in computer science, trees are always standing on their heads).
```{julia}
supertype(Int64)
```
@@ -46,72 +46,69 @@ supertype(Real)
```{julia}
supertype(Number)
```
Das wäre übrigens auch schneller gegangen: Die Funktion `supertypes()` (mit Plural-s) zeigt alle Vorfahren an.
This would have been faster, by the way: The function `supertypes()` (with plural-s) shows all ancestors.
```{julia}
supertypes(Int64)
```
Nun kann man sich die Knoten angucken:
Now one can look at the nodes:
{{< embed ../notebooks/nb-types.ipynb#nb3 >}}
Mit einer kleinen rekursiven Funktion kann man schnell einen ganzen (Unter-)Baum ausdrucken:
With a small recursive function, one can quickly print out an entire (sub-)tree:
{{< embed ../notebooks/nb-types.ipynb#nb1 >}}
::::{.content-hidden unless-format="xxx"}
...und natürlich gibt es da auch ein Julia-Paket:
...and of course, there is also a Julia package for this:
{{< embed ../notebooks/nb-types.ipynb#nb2 >}}
:::
::::
Hier das Ganze nochmal als Bild (gemacht mit LaTeX/[TikZ](https://tikz.dev/tikz-trees))
Here again as an image (made with LaTeX/[TikZ](https://tikz.dev/tikz-trees))
::: {.content-visible when-format="html"}
![](../images/TypeTree2.png){width=80%}
:::
::: {.content-visible when-format="pdf"}
![Die Hierarchie der numerischen Typen](../images/TypeTree2.png){width=60%}
![The hierarchy of numeric types](../images/TypeTree2.png){width=60%}
:::
Natürlich hat Julia nicht nur numerische Typen. Die Anzahl der direkten Abkömmlinge (Kinder) von `Any` ist
Of course, Julia has not only numeric types. The number of direct descendants (children) of `Any` is
```{julia}
length(subtypes(Any))
```
und mit (fast) jedem Paket, das man mit `using ...` lädt, werden es mehr.
and with (almost) every package loaded with `using ...`, this increases.
## Abstract and Concrete Types
## Abstrakte und Konkrete Typen
- Ein Objekt hat immer einen **konkreten** Typ.
- Konkrete Typen haben keine Untertypen mehr, sie sind immer „Blätter“ des Baumes.
- Konkrete Typen spezifizieren eine konkrete Datenstruktur.
- An object always has a **concrete** type.
- Concrete types have no more subtypes, they are always the "leaves" of the tree.
- Concrete types specify a concrete data structure.
:::{.xxx}
:::
- Abstrakte Typen können nicht instanziiert werden, d.h., es gibt keine Objekte mit diesem Typ.
- Sie definieren eine Menge von konkreten Typen und gemeinsame Methoden für diese Typen.
- Sie können daher in der Definition von Funktionstypen, Argumenttypen, Elementtypen von zusammengesetzten Typen u.ä. verwendet werden.
- Abstract types cannot be instantiated, i.e., there are no objects of this type.
- They define a set of concrete types and common methods for these types.
- They can therefore be used in the definition of function types, argument types, element types of composite types, etc.
Zum **Deklarieren** *und* **Testen** der "Abstammung" innerhalb der Typhierarchie gibt es einen eigenen Operator:
For **declaring** *and* **testing** the "descent" within the type hierarchy, there is a special operator:
```{julia}
Int64 <: Number
```
Zum Testen, ob ein Objekt einen bestimmten Typ (oder einen abstrakten Supertyp davon) hat, dient `isa(object, typ)`. Es wird meist in der Infix-Form verwendet und sollte als Frage `x is a T?` gelesen werden.
To test whether an object has a certain type (or an abstract supertype of it), `isa(object, typ)` is used. It is usually used in infix form and should be read as the question `x is a T?`.
```{julia}
x = 17.2
@@ -120,7 +117,7 @@ x = 17.2
```
Da abstrakte Typen keine Datenstrukturen definieren, ist ihre Definition recht schlicht. Entweder sie stammen direkt von `Any` ab:
Since abstract types do not define data structures, their definition is quite simple. Either they are derived directly from `Any`:
```{julia}
abstract type MySuperType end
@@ -128,7 +125,7 @@ abstract type MySuperType end
supertype(MySuperType)
```
oder von einem anderen abstrakten Typ:
or from another abstract type:
```{julia}
abstract type MySpecialNumber <: Integer end
@@ -136,24 +133,24 @@ abstract type MySpecialNumber <: Integer end
supertypes(MySpecialNumber)
```
Mit der Definition werden die abstrakten Typen an einer Stelle des Typ-Baums "eingehängt".
With the definition, the abstract types are "hung" at a point in the type tree.
## Die numerischen Typen `Bool` und `Irrational`
## The Numeric Types `Bool` and `Irrational`
Da sie im Baum der numerischen Typen zu sehen sind, seien sie kurz erklärt:
Since they are seen in the numeric type tree, they should be briefly explained:
`Bool` ist numerisch im Sinne von `true=1, false=0`:
`Bool` is numeric in the sense of `true=1, false=0`:
```{julia}
true + true + true, false - true, sqrt(true), true/4
```
`Irrational` ist der Typ einiger vordefinierter Konstanten wie `π` und ``.
Laut [Dokumentation](https://docs.julialang.org/en/v1/base/numbers/#Base.AbstractIrrational) ist `Irrational` ein *"Number type representing an exact irrational value, which is automatically rounded to the correct precision in arithmetic operations with other numeric quantities".*
`Irrational` is the type of some predefined constants such as `π` and ``.
According to the [documentation](https://docs.julialang.org/en/v1/base/numbers/#Base.AbstractIrrational), `Irrational` is a *"Number type representing an exact irrational value, which is automatically rounded to the correct precision in arithmetic operations with other numeric quantities".*
## Union-Typen
## Union Types
Falls die Baum-Hierarchie nicht ausreicht, kann man auch abstrakte Typen als Vereinigung beliebiger (abstrakter und konkreter) Typen definieren.
If the tree hierarchy is not sufficient, one can also define abstract types as a union of arbitrary (abstract and concrete) types.
```{julia}
IntOrString = Union{Int64,String}
@@ -161,17 +158,17 @@ IntOrString = Union{Int64,String}
:::{.callout-note .titlenormal}
## Beispiel
Das Kommando `methods(<)` zeigt, dass unter den über 70 Methoden, die für den Vergleichsoperator definiert sind, einige auch *union types* verwenden, z.B. ist
## Example
The command `methods(<)` shows that among the over 70 methods defined for the comparison operator, some also use *union types*, e.g., there is
```julia
<(x::Union{Float16, Float32, Float64}, y::BigFloat)
```
eine Methode für den Vergleich einer Maschinenzahl fester Länge mit einer Maschinenzahl beliebiger Länge.
a method for comparing a machine number of fixed length with a machine number of arbitrary length.
:::
## Zusammengesetzte (_composite_) Typen: `struct`
## Composite (_composite_) Types: `struct`
Eine `struct` ist eine Zusammenstellung von mehreren benannten Feldern und definiert einen konkreten Typ.
A `struct` is a collection of several named fields and defines a concrete type.
```{julia}
abstract type Point end
@@ -188,7 +185,7 @@ mutable struct Point3D <: Point
end
```
Wie wir schon bei Ausdrücken der Form `x = Int8(33)` gesehen haben, kann man Typnamen direkt als Konstruktoren einsetzen:
As we have already seen with expressions of the form `x = Int8(33)`, type names can be used directly as constructors:
```{julia}
p1 = Point2D(1.4, 3.5)
@@ -200,20 +197,20 @@ p1 isa Point3D, p1 isa Point2D, p1 isa Point
```
Die Felder einer `struct` können über ihren Namen mit dem `.`-Operator adressiert werden.
The fields of a `struct` can be addressed by their name with the `.` operator.
```{julia}
p1.y
```
Da wir unsere `struct` als `mutable` deklariert haben, können wir das Objekt `p1` modifizieren, indem wir den Feldern neue Werte zuweisen.
Since we declared our `struct` as `mutable`, we can modify the object `p1` by assigning new values to the fields.
```{julia}
p1.x = 3333.4
p1
```
Informationen über den Aufbau eines Typs oder eines Objekts von diesem Typ liefert `dump()`.
Information about the structure of a type or an object of that type is provided by `dump()`.
```{julia}
dump(Point3D)
@@ -224,28 +221,28 @@ dump(Point3D)
dump(p1)
```
## Funktionen und *Multiple dispatch*
## Functions and *Multiple Dispatch*
:::{.callout-note .titlenormal}
## Objekte, Funktionen, Methoden
## Objects, Functions, Methods
In klassischen objektorientierten Sprachen wie C++/Java haben Objekte üblicherweise mit ihnen assoziierte Funktionen, die Methoden des Objekts.
In classical object-oriented languages like C++/Java, objects usually have functions associated with them, which are the methods of the object.
In Julia gehören Methoden zu einer Funktion und nicht zu einem Objekt.
(Eine Ausnahme sind die Konstruktoren, also Funktionen, die genauso heißen wie ein Typ und ein Objekt dieses Typs erzeugen.)
In Julia, methods belong to a function and not to an object.
(An exception is the constructors, i.e., functions that have the same name as a type and create an object of that type.)
Sobald man einen neuen Typ definiert hat, kann man sowohl neue als auch bestehende Funktionen um neue Methoden für diesen Typ ergänzen.
Once one has defined a new type, one can add new or existing functions around new methods for this type.
- Eine Funktion kann mehrfach für verschiedene Argumentlisten (Typ und Anzahl) definiert werden.
- Die Funktion hat dann mehrere Methoden.
- Beim Aufruf wird an Hand der konkreten Argumente entschieden, welche Methode genutzt wird *(multiple dispatch)*.
- Es ist typisch für Julia, dass für Standardfunktionen viele Methoden definiert sind. Diese können problemlos um weitere Methoden für eigene Typen erweitert werden.
- A function can be defined multiple times for different argument lists (type and number).
- The function then has multiple methods.
- When calling, it is decided based on the concrete arguments which method is used *(multiple dispatch)*.
- It is typical for Julia that for standard functions, many methods are defined. These can be easily extended by further methods for own types.
:::
Den Abstand zwischen zwei Punkten implementieren wir als Funktion mit zwei Methoden:
We implement the distance between two points as a function with two methods:
```{julia}
function distance(p1::Point2D, p2::Point2D)
@@ -256,35 +253,34 @@ function distance(p1::Point3D, p2::Point3D)
sqrt((p1.x-p2.x)^2 + (p1.y-p2.y)^2 + (p1.z-p2.z)^2)
end
```
```{julia}
distance(p1, Point2D(2200, -300))
```
Wie schon erwähnt, zeigt `methods()` die Methodentabelle einer Funktion an:
As mentioned earlier, `methods()` shows the method table of a function:
```{julia}
methods(distance)
```
Das Macro `@which`, angewendet auf einen vollen Funktionsaufruf mmit konkreter Argumentliste, zeigt an, welche Methode zu diesen konkreten Argumenten ausgewählt wird:
The macro `@which`, applied to a full function call with concrete argument list, shows which method is selected for these concrete arguments:
```{julia}
@which sqrt(3.3)
```
```{julia}
z = "Hallo" * '!'
z = "Hello" * '!'
println(z)
@which "Hallo" * '!'
@which "Hello" * '!'
```
Methoden können auch abstrakte Typen als Argument haben:
Methods can also have abstract types as arguments:
```{julia}
"""
Berechnet den Winkel ϕ (in Grad) der Polarkoordinaten (2D) bzw.
Kugelkoordinaten (3D) eines Punktes
Calculates the angle ϕ (in degrees) of the polar coordinates (2D) or
spherical coordinates (3D) of a point
"""
function phi_winkel(p::Point)
atand(p.y, p.x)
@@ -294,8 +290,8 @@ phi_winkel(p1)
```
:::{.callout-tip collapse="true"}
Ein in *triple quotes* eingeschlossene Text unmittelbat vor der Funktionsdefinition
wird automatisch in die Hilfe-Datenbank von Julia integriert:
A text enclosed in *triple quotes* immediately before the function definition
is automatically integrated into Julia's help database:
```{julia}
?phi_winkel
@@ -304,8 +300,8 @@ wird automatisch in die Hilfe-Datenbank von Julia integriert:
Beim *multiple dispatch* wird die Methode angewendet, die unter allen passenden die spezifischste ist. Hier eine Funktion mit mehreren Methoden
(alle bis auf die letzte in der kurzen [*assignment form*](https://docs.julialang.org/en/v1/manual/functions/#man-functions) geschrieben):
With *multiple dispatch*, the method is applied that is the most specific among all matching ones. Here is a function with several methods
(all but the last in the short [*assignment form*](https://docs.julialang.org/en/v1/manual/functions/#man-functions) written):
```{julia}
f(x::String, y::Number) = "Args: String + Zahl"
@@ -318,35 +314,35 @@ function f(x::Number, y::Number, z::String)
return "Arg: 2 x Zahl + String"
end
```
Hier passen die ersten beiden Methoden. Gewählt wird die zweite, da sie spezifischer ist, `Int64 <: Number`.
Here, the first two methods match. The second is chosen since it is more specific, `Int64 <: Number`.
```{julia}
f("Hallo", 42)
f("Hello", 42)
```
Es kann sein, dass diese Vorschrift zu keinem eindeutigen Ergebnis führt, wenn man seine Methoden schlecht gewählt hat.
It may happen that this rule does not lead to a unique result if one chooses one's methods badly.
```{julia}
f(42, 42)
```
## Parametrisierte numerische Typen: `Rational` und `Complex`
## Parametric Numeric Types: `Rational` and `Complex`
- Für rationale Zahlen (Brüche) verwendet Julia `//` als Infix-Konstruktor:
- For rational numbers (fractions), Julia uses `//` as an infix constructor:
```{julia}
@show Rational(23, 17) 4//16 + 1//3;
```
- Die imaginäre Einheit $\sqrt{-1}$ heißt `im`
- The imaginary unit $\sqrt{-1}$ is called `im`
```{julia}
@show Complex(0.4) 23 + 0.5im/(1-2im);
```
`Rational` und `Complex` bestehen, ähnlich wie unser `Point2D`, aus 2 Feldern: Zähler und Nenner bzw. Real- und Imaginärteil.
`Rational` and `Complex` consist, similar to our `Point2D`, of 2 fields: numerator and denominator or real and imaginary part.
Der Typ dieser Felder ist allerdings nicht vollständig festgelegt. `Rational` und `Complex` sind _parametrisierte_ Typen.
However, the type of these fields is not completely fixed. `Rational` and `Complex` are _parametric_ types.
```{julia}
x = 2//7
@@ -367,14 +363,14 @@ y = 1.0 + 2.0im
typeof(y)
```
Die konkreten Typen `Rational{Int64}`, `Rational{BigInt}`,..., `Complex{Int64}`, `Complex{Float64}}`, ... sind Subtypen von `Rational` bzw. `Complex`.
The concrete types `Rational{Int64}`, `Rational{BigInt}`,..., `Complex{Int64}`, `Complex{Float64}}`, ... are subtypes of `Rational` resp. `Complex`.
```{julia}
Rational{BigInt} <: Rational
```
Die Definitionen [sehen etwa so aus](https://github.com/JuliaLang/julia/blob/master/base/rational.jl#L6-L15):
The definitions [look roughly like this](https://github.com/JuliaLang/julia/blob/master/base/rational.jl#L6-L15):
```{julia}
struct MyComplex{T<:Real} <: Number
@@ -388,19 +384,19 @@ struct MyRational{T<:Integer} <: Real
end
```
Die erste Definition besagt:
The first definition says:
- `MyComplex` hat zwei Felder `re` und `im`, beide vom gleichen Typ `T`.
- Dieser Typ `T` muss ein Untertyp von `Real` sein.
- `MyComplex` und alle seine Varianten wie `MyComplex{Float64}` sind Untertypen von `Number`.
- `MyComplex` has two fields `re` and `im`, both of the same type `T`.
- This type `T` must be a subtype of `Real`.
- `MyComplex` and all its variants like `MyComplex{Float64}` are subtypes of `Number`.
und die zweite besagt analog:
and the second says analogously:
- `MyRational` hat zwei Felder `num` und `den`, beide vom gleichen Typ `T`.
- Dieser Typ `T` muss ein Untertyp von `Integer` sein.
- `MyRational` und seine Varianten sind Untertypen von `Real`.
- `MyRational` has two fields `num` and `den`, both of the same type `T`.
- This type `T` must be a subtype of `Integer`.
- `MyRational` and its variants are subtypes of `Real`.
Nun ist $\subset$ , oder auf julianisch `Rational <: Real`. Also können die Komponenten einer komplexen Zahl auch rational sein:
Now $\subset$ , or in Julia notation `Rational <: Real`. Thus, the components of a complex number can also be rational:
```{julia}
z = 3//4 + 5im
@@ -409,23 +405,24 @@ dump(z)
Diese Strukturen sind ohne das `mutable`-Attribut definiert, also *immutable*:
These structures are defined without the `mutable` attribute, therefore *immutable*:
```{julia}
x = 2.2 + 3.3im
println("Der Realteil ist: $(x.re)")
println("The real part is: $(x.re)")
x.re = 4.4
```
Das ist so üblich. Wir betrachten das Objekt `9` vom Typ `Int64` ja auch als unveränderlich.
Das Folgende geht natürlich trotzdem:
This is common. We also consider the object `9` of type `Int64` as immutable.
The following, of course, still works:
```{julia}
x += 2.2
```
Hier wird ein neues Objekt vom Typ `Complex{Float64}` erzeugt und `x` zur Referenz auf dieses neue Objekt gemacht.
Here, a new object of type `Complex{Float64}` is created and `x` is made a reference to this new object.
Die Möglichkeiten des Typsystems verleiten leicht zum Spielen. Hier definieren wir eine `struct`, die wahlweise eine Maschinenzahl oder ein Paar von Ganzzahlen enthalten kann:
The possibilities of the type system easily invite to play. Here we define a `struct` that can contain either a machine number or a pair of integers:
```{julia}
struct MyParms{T <: Union{Float64, Tuple{Int64, Int64}}}
@@ -441,13 +438,13 @@ p2 = MyParms( (2, 4) )
## Typen als Objekte
## Types as Objects
(1) Typen sind ebenfalls Objekte. Sie sind Objekte einer der drei "Meta-Typen"
(1) Types are also objects. They are objects of one of the three "meta-types"
- `Union` (Union-Typen)
- `UnionAll` (parametrisierte Typen)
- `DataType` (alle konkreten und sonstige abstrakte Typen)
- `Union` (union types)
- `UnionAll` (parametric types)
- `DataType` (all concrete and other abstract types)
```{julia}
@show 23779 isa Int64 Int64 isa DataType;
@@ -463,7 +460,7 @@ p2 = MyParms( (2, 4) )
```
Diese 3 konkreten "Meta-Typen" sind übrigens Subtypen des abstrakten "Meta-Typen" `Type`.
These 3 concrete "meta-types" are, by the way, subtypes of the abstract "meta-type" `Type`.
```{julia}
subtypes(Type)
@@ -471,7 +468,7 @@ subtypes(Type)
-----------------
(2) Damit können Typen auch einfach Variablen zugewiesen werden:
(2) Thus, types can also be easily assigned to variables:
```{julia}
x3 = Float64
@@ -480,15 +477,15 @@ x3 = Float64
:::{.callout-note collapse="true"}
Dies zeigt auch, dass die [Style-Vorgaben in Julia](https://docs.julialang.org/en/v1/manual/style-guide/#Use-naming-conventions-consistent-with-Julia-base/) wie „Typen und Typvariablen starten mit Großbuchstaben, sonstige Variablen und Funktionen werden klein geschrieben.“ nur Konventionen sind und von der Sprache nicht erzwungen werden.
This also shows that the [style guidelines in Julia](https://docs.julialang.org/en/v1/manual/style-guide/#Use-naming-conventions-consistent-with-Julia-base/) such as "Types and type variables start with uppercase letters, other variables and functions are written in lowercase." are only conventions and are not enforced by the language.
Man sollte sie trotzdem einhalten, um den Code lesbar zu halten.
One should still follow them to keep the code readable.
:::
Wenn man solche Zuweisungen mit `const` für dauerhaft erklärt, entsteht ein
*type alias*.
If such assignments are declared permanent with `const`, a
*type alias* is created.
```{julia}
@@ -500,7 +497,7 @@ typeof(z)
--------
(3) Typen können Argumente von Funktionen sein.
(3) Types can be arguments of functions.
```{julia}
@@ -516,74 +513,76 @@ z = myf(43, UInt16, Real)
@show z typeof(z);
```
Wenn man diese Funktion mit Typsignaturen definieren möchte, kann man natürlich
If one wants to define this function with type signatures, of course one can write
```julia
function myf(x, S::Type, T::Type) ... end
```
schreiben. Üblicher ist hier die (dazu äquivalente) spezielle Syntax
However, the (equivalent) special syntax
```julia
function myf(x, ::Type{S}, ::Type{T}) where {S,T} ... end
```
bei der man in der `where`-Klausel auch noch Einschränkungen an die zulässigen Werte der Typvariablen `S` und `T` stellen kann.
is more common here, where one can also impose restrictions on the permissible values of the type variables `S` and `T` in the `where` clause.
Wie definiere ich eine spezielle Methode von `myf`, die nur aufgerufen werden soll, wenn `S` und `T` gleich `Int64` sind? Das ist folgendermaßen möglich:
How do I define a special method of `myf` that should only be called when `S` and `T` are equal to `Int64`? This is possible as follows:
```julia
function myf(x, ::Type{Int64}, ::Type{Int64}) ... end
```
`Type{Int64}` wirkt wie ein "Meta-Typ", dessen einzige Instanz der Typ `Int64` ist.
`Type{Int64}` acts like a "meta-type", whose only instance is the type `Int64`.
-----------------
(4) Es gibt zahlreiche Operationen mit Typen als Argumenten. Wir haben schon `<:(T1, T2)`, `supertype(T)`, `supertypes(T)`, `subtypes(T)` gesehen. Erwähnt seien noch `typejoin(T1,T2)` (nächster gemeinsamer Vorfahre im Typbaum) und Tests wie `isconcretetype(T)`, `isabstracttype(T)`, `isstructtype(T)`.
(4) There are numerous operations with types as arguments. We have already seen `<:(T1, T2)`, `supertype(T)`, `supertypes(T)`, `subtypes(T)`. Mentioned should be still `typejoin(T1,T2)` (next common ancestor in the type tree) and tests like `isconcretetype(T)`, `isabstracttype(T)`, `isstructtype(T)`.
## Invarianz parametrisierter Typen {#sec-invariance}
## Invariance of Parametric Types {#sec-invariance}
Kann man in parametrisierten Typen auch nicht-konkrete Typen einsetzen? Gibt es `Complex{AbstractFloat}` oder `Complex{Union{Float32, Int16}}`?
Can non-concrete types also be substituted into parametric types? Are there `Complex{AbstractFloat}` or `Complex{Union{Float32, Int16}}`?
Ja, die gibt es; und es sind konkrete Typen, man kann also Objekte von diesem Typ erzeugen.
Yes, they exist; and they are concrete types, one can therefore create objects of this type.
```{julia}
z5 = Complex{Integer}(2, 0x33)
dump(z5)
```
Das ist eine heterogene Struktur. Jede Komponente hat einen individuellen Typ `T`, für den `T<:Integer` gilt.
This is a heterogeneous structure. Each component has an individual type `T`, for which `T<:Integer` holds.
Nun gilt zwar
Now it holds that
```{julia}
Int64 <: Integer
```
aber es gilt nicht, dass
but it does not hold that
```{julia}
Complex{Int64} <: Complex{Integer}
```
Diese Typen sind beide konkret. Damit können sie in der Typhierarchie von Julia nicht in einer Sub/Supertype-Relation zueinander stehen. Julias parametrisierte Typen sind [in der Sprache der theoretischen Informatik](https://de.wikipedia.org/wiki/Kovarianz_und_Kontravarianz) **invariant**.
(Wenn aus `S<:T` folgen würde, dass auch `ParamType{S} <: ParamType{T}` gilt, würde man von **Kovarianz** sprechen.)
These types are both concrete. Therefore, they cannot stand in a sub/supertype relation to each other in Julia's type hierarchy. Julia's parametric types are [in the language of theoretical computer science](https://en.wikipedia.org/wiki/Covariance_and_contravariance) **invariant**.
(If from `S<:T` it would follow that also `ParamType{S} <: ParamType{T}`, one would speak of **covariance**.)
## Generische Funktionen
Der übliche (und in vielen Fällen empfohlene!) Programmierstil in Julia ist das Schreiben generischer Funktionen:
## Generic Functions
The usual (and in many cases recommended!) programming style in Julia is writing generic functions:
```{julia}
function fsinnfrei1(x, y)
return x * x * y
end
```
Diese Funktion funktioniert sofort mit allen Typen, für die die verwendeten Operationen definiert sind.
This function works immediately with all types for which the used operations are defined.
```{julia}
fsinnfrei1( Complex(2,3), 10), fsinnfrei1("Hallo", '!')
fsinnfrei1( Complex(2,3), 10), fsinnfrei1("Hello", '!')
```
Man kann natürlich Typ-Annotationen benutzen, um die Verwendbarkeit einzuschränken oder um unterschiedliche Methoden für unterschiedliche Typen zu implementieren:
One can of course use type annotations to restrict usability or to implement different methods for different types:
```{julia}
function fsinnfrei2(x::Number, y::AbstractFloat)
@@ -600,15 +599,15 @@ end
:::{.callout-important}
**Explizite Typannotationen sind fast immer irrelevent für die Geschwindigkeit des Codes!**
**Explicit type annotations are almost always irrelevant for the speed of the code!**
Dies ist einer der wichtigsten *selling points* von Julia.
This is one of the most important *selling points* of Julia.
Sobald eine Funktion zum ersten Mal mit bestimmten Typen aufgerufen wird, wird eine auf diese Argumenttypen spezialisierte Form der Funktion generiert und compiliert. Damit sind generische Funktionen in der Regel genauso schnell, wie die spezialisierten Funktionen, die man in anderen Sprachen schreibt.
Once a function is called for the first time with certain types, a specialized form of the function is generated and compiled for these argument types. Thus, generic functions are usually just as fast as the specialized functions one writes in other languages.
Generische Funktionen erlauben die Zusammenarbeit unterschiedlichster Pakete und eine hohe Abstraktion.
Generic functions enable the cooperation of the most diverse packages and a high level of abstraction.
Ein einfaches Beispiel: Das Paket `Measurements.jl` definiert einen neuen Datentyp `Measurement`, einen Wert mit Fehler, und die Arithmetik dieses Typs. Damit funktionieren generische Funktionen automatisch:
A simple example: The `Measurements.jl` package defines a new data type `Measurement`, a value with error, and the arithmetic of this type. Thus, generic functions work automatically:
```{julia}
#| echo: false
@@ -652,18 +651,18 @@ fsinnfrei1(x, y)
```
:::
## Typ-Parameter in Funktionsdefinitionen: die `where`-Klausel
## Type Parameters in Function Definitions: the `where` Clause
Wir wollen eine Funktion schreiben, die für **alle komplexen Integer** (und nur diese) funktioniert, z.B. eine [in [i] mögliche Primfaktorzerlegung](https://de.wikipedia.org/wiki/Gau%C3%9Fsche_Zahl). Die Definition
We want to write a function that works for **all complex integers** (and only these), e.g., a [possible prime factorization in [i](https://en.wikipedia.org/wiki/Gaussian_integer). The definition
```{julia}
#| eval: false
function isprime(x::Complex{Integer}) ... end
```
liefert nun nicht das Gewünschte, wie wir in @sec-invariance gesehen haben. Die Funktion würde für ein Argument vom Typ `Complex{Int64}` nicht funktionieren, da letzteres kein Subtyp von `Complex{Integer}` ist.
does not give the desired result, as we saw in @sec-invariance. The function would not work for an argument of type `Complex{Int64}`, since the latter is not a subtype of `Complex{Integer}`.
Wir müssen eine Typ-Variable einführen. Dazu dient die `where`-Klausel.
We must introduce a type variable. The `where` clause serves this purpose.
```{julia}
#| eval: false
@@ -672,11 +671,11 @@ function isprime(x::Complex{T}) where {T<:Integer}
end
```
Das ist zu lesen als:
This is to be read as:
> „Das Argument x soll von einem der Typen `Complex{T}` sein, wobei die Typvariable `T` irgendein Untertyp von `Integer` sein kann.“
> "The argument x should be one of the types `Complex{T}`, where the type variable `T` can be any subtype of `Integer`."
Noch ein Beispiel:
Another example:
```{julia}
#| eval: false
function kgV(x::Complex{T}, y::Complex{S}) where {T<:Integer, S<:Integer}
@@ -684,10 +683,11 @@ function kgV(x::Complex{T}, y::Complex{S}) where {T<:Integer, S<:Integer}
end
```
> Die Argumente x und y können verschiedene Typen haben und beide müssen Subtypen von `Integer` sein.
> The arguments x and y can have different types and both must be subtypes of `Integer`.
Wenn es nur eine `where`-Klausel wie im vorletzten Beispiel gibt, kann man die geschweiften Klammern weglassen und
If there is only one `where` clause as in the last example, one can omit the curly braces and
write
```{julia}
#| eval: false
@@ -695,7 +695,7 @@ function isprime(x::Complex{T}) where T<:Integer
...
end
```
schreiben. Das lässt sich noch weiter kürzen zu
This can still be shortened to
```{julia}
#| eval: false
function isprime(x::Complex{<:Integer})
@@ -703,7 +703,7 @@ function isprime(x::Complex{<:Integer})
end
```
Diese verschiedenen Varianten können verwirrend sein, aber das ist nur Syntax.
These different variants can be confusing, but it is only syntax.
```{julia}
C1 = Complex{T} where {T<:Integer}
@@ -713,24 +713,24 @@ C3 = Complex{<:Integer}
C1 == C2 == C3
```
Kurze Syntax für einfache Fälle, ausführliche Syntax für komplexe Varianten.
Short syntax for simple cases, extended syntax for complex variants.
Als letztes sein bemerkt, dass `where T` die Kurzform von `where T<:Any` ist, also eine völlig unbeschränkte Typvariable einführt. Damit ist sowas möglich:
Last, it should be noted that `where T` is the short form of `where T<:Any`, which introduces a completely unrestricted type variable. Thus, something like this is possible:
```{julia}
function fgl(x::T, y::T) where T
println("Glückwunsch! x und y sind vom gleichen Typ!")
println("Congratulations! x and y are of the same type!")
end
```
Diese Methode erfordert, dass die Argumente genau den gleichen, aber ansonsten beliebigen Typ haben.
This method requires that the arguments are exactly the same type, but otherwise arbitrary.
```{julia}
fgl(33, 44)
```
```{julia}
fgl(33, 44.0)
```