"# Ein Fallbeispiel: Der parametrisierte Datentyp PComplex\n",
"\n",
"Wir wollen als neuen numerischen Typen **komplexe Zahlen in Polardarstellung $z=r e^{i\\phi}=(r,ϕ)$** einführen. \n",
"\n",
"- Der Typ soll sich in die Typhierarchie einfügen als Subtyp von 'Number'.\n",
"- $r$ und $\\phi$ sollen Gleitkommazahlen sein. (Im Unterschied zu komplexen Zahlen in 'kartesischen' Koordinaten hat eine Einschränkung auf ganzzahlige Werte von r oder ϕ mathematisch wenig Sinn.)\n",
"Für die explizite Angabe eines *inner constructors* müssen wir allerdings einen Preis zahlen: Die sonst von Julia bereitgestellten *default constructors* fehlen. \n",
"\n",
"Den Konstruktor, der ohne explizite Typangabe in geschweiften Klammern auskommt und den Typ der Argumente übernimmt, wollen wir gerne auch haben:\n"
"Julia verwendet `//` als Infix-Konstruktor für den Typ `Rational`. Sowas Schickes wollen wir auch. \n",
"\n",
"In der Elektronik/Elektrotechnik werden [Wechselstromgrößen durch komplexe Zahlen beschrieben.](https://de.wikipedia.org/wiki/Komplexe_Wechselstromrechnung). Dabei ist eine Darstellung komplexer Zahlen durch \"Betrag\" und \"Phase\" üblich und sie wird gerne in der sogenannten [Versor-Form](https://de.wikipedia.org/wiki/Versor) (engl. *phasor*) dargestellt:\n",
"In Julia ist eine große Anzahl von Unicode-Zeichen reserviert für die Verwendung als Operatoren. Die definitive Liste ist im [Quellcode des Parsers.](https://github.com/JuliaLang/julia/blob/eaa2c58aeb12f27c1d8c116ab111773a4fc4495f/src/julia-parser.scm#L13-L31)\n",
"\n",
"Auf Details werden wir in einem späteren Kapitel noch eingehen. \n",
"\n",
"Und ja, der Julia-Parser ist in einem Lisp(genauer: Scheme)-Dialekt geschrieben. In Julia ist ein kleiner Scheme-Interpreter namens [femtolisp](https://github.com/JeffBezanson/femtolisp) integriert. Geschrieben hat ihn einer der \"Väter\" von Julia bevor er mit der Arbeit an Julia begann. \n",
"\n",
":::\n",
"\n",
"Das Winkel-Zeichen `∠` steht leider nicht als Operatorsymbol zur Verfügung. Wir weichen aus auf `⋖`. Das kann in Julia als als `\\lessdot<tab>` eingegeben werden.\n"
"(Die Typ-Annotation -- `Real` statt `AbstractFloat` -- ist ein Vorgriff auf kommende weitere Konstruktoren. Im Moment funktioniert der Operator `⋖` erstmal nur mit `Float`s.)\n",
"\n",
"\n",
"Natürlich wollen wir auch die Ausgabe so schön haben. Details dazu findet man in der [Dokumentation](https://docs.julialang.org/en/v1/manual/types/#man-custom-pretty-printing).\n"
"Damit unser Typ ein anständiges Mitglied der von `Number` abstammenden Typfamilie wird, brauchen wir allerdings noch eine ganze Menge mehr. Es müssen Arithmetik, Vergleichsoperatoren, Konvertierungen usw. definiert werden. \n",
"\n",
"\n",
"Wir beschränken uns auf Multiplikation und Quadratwurzeln.\n",
"\n",
"\n",
":::{.callout-note collapse=\"true\"}\n",
"## Module \n",
"\n",
"- Um die `methods` der existierenden Funktionen und Operationen zu ergänzen, muss man diese mit ihrem 'vollen Namen' ansprechen.\n",
"- Alle Objekte gehören zu einem Namensraum oder `module`.\n",
"- Die meisten Basisfunktionen gehören zum Modul `Base`, welches standardmäßig immer ohne explizites `using ...` geladen wird. \n",
"- Solange man keine eigenen Module definiert, sind die eigenen Definitionen im Modul `Main`.\n",
"- Das Macro `@which`, angewendet auf einen Namen, zeigt an, in welchem Modul der Name definiert wurde. \n"
"(Da das Operatorsymbol kein normaler Name ist, muss der Doppelpunkt bei der Zusammensetzung mit `Base.` sein.)\n",
"\n",
"Wir können allerdings noch nicht mit anderen numerischen Typen multiplizieren. Dazu könnte man nun eine Vielzahl von entsprechenden Methoden definieren. Julia stellt *für numerische Typen* noch einen weiteren Mechanismus zur Verfügung, der das etwas vereinfacht.\n",
"\n",
"\n",
"## Typ-Promotion und Konversion \n",
"\n",
"In Julia kann man bekanntlich die verschiedensten numerischen Typen nebeneinander verwenden.\n"
"Die Funktion `promote()` verwendet dazu zwei Helfer, die Funktionen\n",
" `promote_type(T1, T2)` und `convert(T, x)`\n",
"\n",
"Wie üblich in Julia, kann man diesen Mechanismus durch [eigene *promotion rules* und `convert(T,x)`-Methoden erweitern.](https://docs.julialang.org/en/v1/manual/conversion-and-promotion/) \n",
"\n",
"\n",
"### Die Funktion `promote_type(T1, T2,...)` \n",
"\n",
"Sie ermittelt, zu welchem Typ umgewandelt werden soll. Argumente sind Typen, nicht Werte.\n"
"Die spezielle Rolle von `convert()` liegt darin, dass es an verschiedenen Stellen _implizit_ und automatisch eingesetzt wird: \n",
"\n",
"> [The following language constructs call convert](https://docs.julialang.org/en/v1/manual/conversion-and-promotion/#When-is-convert-called?):\n",
"> \n",
" - Assigning to an array converts to the array's element type.\n",
" - Assigning to a field of an object converts to the declared type of the field.\n",
" - Constructing an object with new converts to the object's declared field types.\n",
" - Assigning to a variable with a declared type (e.g. local x::T) converts to that type.\n",
" - A function with a declared return type converts its return value to that type.\n",
"\n",
"-- und natürlich in `promote()`\n",
"\n",
"Für selbstdefinierte Datentypen kann man convert() um weitere Methoden ergänzen.\n",
"\n",
"Für Datentypen innerhalb der Number-Hierarchie gibt es wieder eine 'catch-all-Definition'\n",
"```julia\n",
"convert(::Type{T}, x::Number) where {T<:Number} = T(x)\n",
"```\n",
"\n",
"Also: Wenn für einen Typen `T` aus der Hierarchie `T<:Number` ein Konstruktor `T(x)` mit einem numerischen Argument `x` existiert, dann wird dieser Konstruktor `T(x)` automatisch für Konvertierungen benutzt. (Natürlich können auch speziellere Methoden für `convert()` definiert werden, die dann Vorrang haben.)\n",
"Wir brauchen nun noch *promotion rules*, die festlegen, welcher Typ bei `promote(x::T1, y::T2)` herauskommen soll. Damit wird `promote_type()` intern um die nötigen weiteren Methoden erweitert. \n",
": Wenn ein `PComplex{T}` und ein `S<:Real` zusammentreffen, dann sollen beide zu `PComplex{U}` umgewandelt werden, wobei `U` der Typ ist, zu dem `S` und `T` beide umgewandelt (_promoted_) werden können. \n",
"\n",
"2. Regel\n",
": Wenn ein `PComplex{T}` und ein `Complex{S}` zusammentreffen, dann sollen beide zu `PComplex{U}` umgewandelt werden, wobei `U` der Typ ist, zu dem `S` und `T` beide umgewandelt werden können. \n",
"\n",
"\n",
"\n",
"Damit klappt nun die Multiplikation mit beliebigen numerischen Typen.\n"
"Jetzt geht sowas wie `PComplex(1, 0)` noch nicht. Wir wollen auch andere reelle Typen für `r` und `ϕ` zulassen. Der Einfachheit halber wandeln wir hier alles nach `Float64` um. Analog verfahren wir auch, wenn nur ein reelles oder komplexes Argument verwendet wird.\n",