Ja, hallo, liebe Hörerinnen und Hörer, willkommen beim Python-Podcast, heute Episode 54.
Wir reden heute über Types, Typings und Typescript.
Hallihallo.
Hallihallo, willkommen Dominik und...
Hallo Johannes.
Hallo zusammen.
Und hallo Stefan.
Genau.
Ja, hallo Stefan.
Auch ein Gast heute.
Genau.
Ja, wir freuen uns sehr, dass ihr alle da seid und fangen einfach mit News an, wie sonst auch immer.
Ja, würde ich schon sagen.
Was gab es denn Schönes?
Wer möchte anfangen, soll ich?
Ja, fang du mal an.
Na gut, ja, also ehrlich gesagt, das letzte Mal war nicht so lange her, daher habe ich
ja nicht so viel gesammelt.
Python 3.12.1.
Genau, das ist natürlich irgendwie, es stand irgendwie dabei, so 400 Bugfixes und so, also
ja, sollte man wahrscheinlich mal installieren und meine Frage dazu wäre halt, bist du jetzt
schon umgestiegen auf 3.12., weil du wolltest ja immer nur die erste meiner Version abwarten,
aber dann auch, ja?
Ja, also noch nicht mit allen Sachen, aber mit vielen Sachen.
Okay.
Also 3.12.1 ist draufgekommen und jetzt in meinem Systeminterpreter zum Beispiel ist das
Auftritt.
3.12.1.
Hervorragend.
Ja, aber ich glaube, da war nichts, also außer Bugs ist da nichts irgendwie passiert.
Ja, mein Hauptproblem im Moment ist halt PyTorch oder sowas, wo es auch im Dezember einen Release
gab, dass es jetzt endlich mit 3.11.1 funktioniert.
Ah, ja, ja.
Ja, aber.
Ja, ansonsten genau neue Releases, es gab einen neuen Ruby on Rails Release, ich gucke
da ab und zu mal so rüber, weil ich halt interessant finde, wie viel tolles Zeug da
passiert und das ist jetzt deutlich.
Also es ist jetzt schneller geworden, hat irgendwie einen eingebauten Dustin-Time-Compiler
und verwendet jetzt einen ähnlichen Ansatz für den Parser, wie Python halt auch.
Python ist ja jetzt mit irgendwie 3.9 auf dem Pack-Parser umgestiegen und das ist, Ruby
verwendet da jetzt was ganz ähnliches, ist auch so ein rekursives Dings da, Parsen, ich
weiß nicht, ich habe es wieder vergessen, wie das genau heißt, so ähnlich und sie
sind auch umgestiegen von Bison, also dem Parser-Generator auf den Ansatz.
Ich finde das schon ganz oft.
Rup.
Lob für Ruby gehört, muss ich ehrlich sagen.
Ja, und auch bei dieser ganzen Justin-Time-Compile-Geschichte, da ist, glaube ich, einer der Hauptsponsoren
auch Shopify, ja, genau, also die basieren, also es gibt ja viele, viele große Unternehmen,
die halt hauptsächlich auf Ruby on Rails Moduliten basieren und da kommt halt auch
eine Menge Geld rein.
Okay, okay.
Und ja, Python ist ja jetzt auch dran mit diesem Justin-Time-Compiler-Thema, da in
drei, drei Sekunden.
Das wäre meine News gewesen, ja.
Ach so, sorry.
Dann schieb's mal los.
Ja, im Dezember, Ende Dezember ist wohl ein Patch in den 3.13-Branch reingekommen, wo ein
JIT-Compiler drin ist für Python.
Also es ist jetzt richtig im Plan drin, dass Python in 3.13 einen JIT-Compiler hat, einen
Copy-and-Patch-JIT-Compiler, was auch immer das bedeuten mag.
Da gab es auch angeblich eine wundervolle Diskussion auf Reddit zu…
Ja, also es gibt einmal die, ich kann empfehlen, es gibt Core-PUI, das ist so ein Podcast, wo
zwei der Core-Entwickler irgendwie drüber reden.
Da gibt es eine Episode zum Justin-Time-Compiler und dann…
Wer sind dabei?
Shannon und Sean oder wie ist das?
Nee, das sind Pablo Galindo Salgado, der Release-Manager auch für 3.13 und Lukas Schlanger.
Ah ja.
Genau.
Und genau, die haben da einmal drüber geredet und daher weiß ich auch, dass das basiert
hauptsächlich, also warum man das jetzt nochmal in Angriff nimmt, auf Geschichten, die in
Lua passiert sind.
Da gab es jetzt auch irgendwelche, ich habe jetzt wieder die Details vergessen, aber so
Papers, die sehr, sehr interessant aussahen und die halt vermuten lassen, dass man es
relativ leicht irgendwie auch für Python verwenden kann.
Und ja, das ist halt ein InPython in Ruby, da werden überall diese Dinger jetzt gerade
eingebaut.
Und hier der…
Der wegen dem PyPy auch hier war schon, der meinte auch so, oh, er muss jetzt mal sein
Commit-Bit wieder hochfahren.
Wieder quasi aus…
Ja, es gab ja immer, dass die neuen Versionen deutlich einfacher zu implementieren sind
bei PyPy, wenn das drin ist, was da in C-Extensions irgendwie dazukommen sollte, wollte, wenn
ich das richtig verstanden hatte damals.
Ja, also jedenfalls, der macht da jetzt auch mit und das wird auf jeden Fall spannend.
Ja, schnelles Python.
Ja, genau.
Ja, ansonsten hätte ich jetzt noch das, ich habe ja jetzt mich auch hier an dieser Stelle
schon irgendwie ein paar Mal beschwert über irgendwie sehr holprige Updates von Python.
Über mich, nein.
Ach so, Entschuldigung.
Ja, gut.
Über Pydantic 2, über den Umstieg von Pydantic 1 auf 2.
Und jetzt haben das andere Leute auch gemacht, irgendwie Simon Willison hat da in letzter
Zeit relativ viel zu gepostet, dass er das irgendwie ungünstig fand.
Habe ich dann auch so Sachen gesehen, wie es gab da ein Issue zu, wo dann einer von Netflix
oder so in relativ freundlichem Ton zunächst schrieb, irgendwie so, ja, also das macht bei
uns sehr viel Arbeit und irgendwie, das war jetzt alles irgendwie nicht so günstig.
Und ich habe dann geguckt, der hat dann auch irgendwann beschrieben, welche Issues, die
da reingelaufen sind.
Und das war halt zum Beispiel auch einer von den Dingern, in die ich da reingerannt bin.
Und ja, genau, der meinte dann so, tja, also sauberer wäre es gewesen, wenn man das Paket
irgendwie umbenannt hätte oder so.
Mhm.
Und das wollten sie aber nicht machen.
Und dann haben sie gesagt, nee, das geht auch so oder geht so.
Aber es ging alles nicht.
Und es war relativ furchtbar.
Also gerade, wenn man eine Library ist, hat man damit halt ein großes Problem, weil man
nicht kontrollieren kann, was in der Applikation, die einen benutzt, halt irgendwie für eine
Pydantic-Version ist.
Und zum Beispiel FastAPI hat damit auch ein Riesenproblem gehabt.
Und wie die das dann letztendlich gemacht haben, ist, sie haben halt so einen Flag
eingeführt, Pydantic V2, und machen dann jetzt gerade sowas wie, if Pydantic V2, 500
Zeilen eingerückt, irgendwie Kompatibilitäts-Layer.
Und dann Else und dann sonst.
Und das haben sie an mehreren Stellen.
Das ist wirklich absolut schrecklich.
Naja, aber so sieht es halt aus.
Also das war, dieses Update war nicht, nicht wirklich reibungslos, sondern da haben viele
Leute irgendwie eine Menge Schweiß gelassen.
Naja, also nicht durch.
Okay.
Ja.
Also gut, wenn man es nicht zu sehr drin hat.
Ich habe da tatsächlich auch zwischendurch noch auf eins dependen müssen, weil da so
bei zwei Sachen nicht so ging, was jetzt irgendwie geht.
Und ja, ein bisschen nervig.
Johannes, hast du noch was?
Nee, also in der TypeScript-Welt gibt es sowas nicht.
Aber jetzt können wir uns über TypeScript-Versionen unterhalten.
Apropos, ich habe gehört, dass ein neues Buch erschienen in der TypeScript-Welt.
Echt, was?
Ja, das ist mir ganz neu.
Ja, ich glaube, das muss der Stefan mal so ein bisschen erzählen.
Ist das jetzt mein Intro, oder was?
Ja, du darfst das gerne antworten.
Das war jetzt die softeste Überleitung.
Ja, genau.
Das war der softeste Übergang, den der Dominik macht.
Ich habe das jetzt total spannend gefunden, weil...
Also, es wird ja immer so gemunkert, dass im Web-Bereich gibt es ja quasi alle drei Minuten
irgendeinen neuen Fachbegriff oder irgendeinen neuen Bibliothekennamen oder irgendein neues
JavaScript-Framework, das irgendeinen abstrusen Namen hat, mit dem sich alle nachher irgendwie
auseinandersetzen müssen.
Und das ist jetzt das zweite Mal, dass ich so in diese Python-Ecke reinschaue und denke
mir, hey, komm, das ist ja da genau das Gleiche.
Den ersten Namen, den ich...
Den ich erkannt habe, das war fast API, weil tatsächlich ist es, das ist spannend, ich
bin jetzt mit, seit vier, fünf Monaten bei uns in der Firma mit einer Gruppe Python-Developern
unterwegs, Data-Scientists, ganz klassisch, du hast einen Data-Scientist und das Tool
der Wahl ist Python, die Bibliotheken sind da, das Übliche mit Langchain, Pipapo für
diese ganze LLM-Sache, und das war so mein erstes Intro in dieser Python-Welt und ich
bin schon massiv gescheitert daran, dass ich den Package-Manager auswähle, der passt,
weil da gibt es ja dann Conda, Anaconda, Pipo, da sind das ganz andere, keine Ahnung, also
wie gesagt, das ist ja schon wieder vorbei, aber ich weiß, dass ich fast API nach langen
langen Gesprächen mit unseren Python-Devs in unser Architektur-Diagramm eingetragen
habe für irgendeinen Server, den wir gemacht haben, genau.
Also das war eben das Erste, wo ich mir gedacht habe, so, da kenne ich mich jetzt aus, bei
fast API, da kann ich mitreden.
Also es funktioniert multithreaded, aber nicht async, ist das richtig?
Nee, also kann man wahrscheinlich so betreiben, wenn man wirklich will, aber nee, es ist tatsächlich
async, also unter fast API liegt normalerweise, so würde ich jetzt mal sagen, wenn man das
so betreibt, wie es gedacht ist, Stalett, beziehungsweise UV-Corn, und das ist halt
sozusagen die LibUV, also das ist halt eine Adaption von LinUV, was halt auch unter der
Event-Loop bei Node.js liegt.
Das ist halt für Python, ja, und also ist halt quasi genauso schnell dann auch und ist
async, ja.
Cool.
Das war die, die zweite war dann, dass ich, dass ich versucht habe, über PyO3 eine Brücke
zwischen async-rust und async-python einmal zu schreiben.
Das war spannend.
Das war richtig cool.
Ja.
Also was mich da beeindruckt hat, und mit dem habe ich nicht gerechnet, ist, dass das
vollen Frankischen Interface von Python ja fantastisch ist, also du hast da, du hast
dort dein, dein kompiliertes SO-Modul dazu und du kannst auf die, auf die, auf die Objektliste
zugreifen und kannst die Identifier rauslesen, also das war, also das Kompilieren vom Rust-Code
war auf jeden Fall anspruchsvoller, als wir nachher die Symbole in, in Python zu loben
und zu verwenden, das war richtig, richtig beeindruckend, also, ähm, coole Sache, möchte
ich mir auf jeden Fall mehr anschauen.
Aber das, das ist es, also das, das sind meine Python-Kenntnisse und.
Ja, weil, also ich finde auch, das Benutzen von Python ist halt das, was so Spaß macht,
dann, ne?
Ja.
Also High-Level-Interface.
High-Level-Interface, ich glaube, ist sehr gut geeignet.
PS3 auch ein cooles Beispiel, da gibt es die meisten Sachen, die es irgendwie kann.
Ich glaube, was es nicht kann, ist irgendwie, äh, Iteratoren ausspucken richtig oder so
oder Generatoren ergeben muss, aber.
Ja, das wird wahrscheinlich schwierig sein, ja.
Das kann man kurz vorstellen.
Keine Ahnung.
Da ist dieses Typsystem von Rust halt doch sehr, sehr eigen und, und sehr schwierig,
in andere, ähm, ähm, Sprachen zu, zu integrieren, nehme ich mal an.
Ja, aber, ähm, ich hatte ja als News ja auch quasi noch ein bisschen Werbung gemacht für
dein Buch, vielleicht willst du dazu noch irgendwie kurz was sagen?
Mhm.
Ähm, ja, dankeschön.
Danke für, für diese Überleitung.
Ähm, ich habe tatsächlich in den letzten Jahren, ähm, TypeScript-Bücher geschrieben.
Ähm, das erste Buch, das ich geschrieben habe, war, war in 2020, äh, TypeScript in
50 Lessons, das beim Smashing Magazine Verlag rausgekommen ist, das als, ähm, angenehmer,
unaufgeregter Einstieg in TypeScript als Typsystem auf JavaScript gedacht ist.
Das heißt, die, die, das Zielpublikum waren Entwicklerinnen und Entwickler, die, ähm,
ähm, JavaScript.
schon kennen, sich dort auch schon, schon wohlfühlen und jetzt merken, jetzt, jetzt
müssen sie TypeScript verwenden, äh, brauchen die Info, warum man das erstens überhaupt
haben will und zweitens, wie das jetzt so richtig funktioniert und warum es da so viel
Syntax gibt und warum die so, so kompliziert ausschaut und versucht, das, das grundlegende
System, Typsystem runterzubrechen auf einfach zu verdauende, ähm, Lektionen.
Ähm, genau, das war, das war das Ziel von, von dem Buch, das war, habe ich dann zufälligerweise
in 50, ähm.
Ähm, Lektionen, ähm, geschafft, das war, äh, Riesenspaß, das war quasi mein, mein Corona-Projekt,
mein, mein erstes Lockdown-Projekt, wobei, stimmt nicht, ich habe es zwischen dem ersten
und dem zweiten Lockdown, habe ich tatsächlich das meiste geschrieben, dazwischen war ich
einfach ein totales Chaos.
Das sieht auch super aus, also von außen, wenn du in den Schrank stellen und, ähm.
Wir, wir wissen das, weil wir, weil wir das, weil wir dieses Buch auch alle haben.
Ja, tatsächlich.
Zwar als Python-Entwickler, was natürlich auch schon was heißt, wenn wir schon mal
in die Kamera gehalten, das macht mich, das macht mich irrsinnig happy, ihr kennt sich
das nicht vorstellen, das ist, das sind diese, diese unglaublich schönen Momente,
wo man sieht, ähm, dass das Buch tatsächlich auch an Leute kommt und Leute das verwenden,
also die Lesebändchen gesehen herrlich spitze, oder?
Und, ähm, die Optik ist wirklich ganz, ganz besonders, weil, ähm, das, das war eigentlich,
ähm, auch ein Grund, warum ich mit Smashing Magazine zusammenarbeiten wollte, die, äh,
haben einfach irrsinnig viel Liebe zum Detail und versuchen wirklich, äh, sehr individuelle
Bücher zu machen, haben eine wunderschöne Typografie, das sauber zu lesen ist und, und
verzetteln sie dann in kleinen Finessten so stark und, ähm, ihr habt zum Beispiel,
das Cover wurde gestaltet von, von Rob Draper, den habe ich tatsächlich in Düsseldorf
kennengelernt, auf der, auf der Björn Tellerrand, ähm, der als, als Künstler, ähm, die Intro-Grafiken
zu den Golden Globes gemacht hat, äh, für, für Nike und BMX, äh, Fahrräder, beziehungsweise
Schuhe designt hat, äh, und der mich gefragt hat, hey, so, ne, E-Mail geschrieben, hey,
der Arbeit ist cool, möchtest, möchtest du mein Buch designen?
Der, ja, passt, und ich war doch, hey, wow, cool, ne, ich hoffe, er kostet jetzt nicht
eine Million oder so, ne, und er hat tatsächlich dann, ähm, dann unser Buch, also das Buch
gestaltet war, war, ähm, also, also versucht er mit diesem Kapitel, ähm, äh, in Lays
ein bisschen das Ganze zugänglich zu machen, freundlich zu machen, ne, das ist, war, war
uns ganz, ganz wichtig und die Person, die nachher, also die, die Ari, die nachher das
Ganze gesetzt hat und versucht hat, aus, ein Produkt rauszumachen, hat dann auch recherchiert
und hat zum Beispiel das Lesebändchen, das rote Lesebändchen in der Farbe bestellt von
den roten Unterlinien in Visual Studio Code, das heißt, ähm, das ist, das ist der gleiche
Farbton.
Und, und so tief ins Detail geht's dort und das ist halt einfach absolut herrlich für,
für mich, der praktisch nur den Text beigetragen hat, dass du siehst, wie andere Leute sich
so, so investieren in dieses Projekt und versuchen, gemeinsam da irgendwas Cooles draus zu machen,
das, also da krieg ich heute noch Gänsehaut, das ist ja für mich so, ähm, ich weiß, es
ist mein Buch, aber es ist nicht nur mein Buch, also da sind so viele Leute dran beteiligt
gewesen, es war ein irrsinnig cooler Effort von, von so vielen Menschen und das macht
mich einfach jedes Mal wieder glücklich.
Wenn ich, wenn ich dann sehe, dass das, ähm, Leute auch so sehen, sie das in die Bücherregale
stellen oder, ähm, das Beste, was du machen kannst, hast du einen Zoom-Call, stell's in
den Hintergrund und schick mir ein Foto von dem Zoom-Call, das ist das allerbeste, das
macht mir die größte, größte Freude und es hat eine Freundin für mich gemacht, wie
er, wie er ein Interview für sein, sein Startup gemacht hat, auf einmal sehe ich, hey, da
ist mein Buch im Hintergrund, äh, herrlich, also, macht Riesenspaß, genau.
Das war das erste TypeScript-Buch, ich hab dann noch ein zweites TypeScript-Buch geschrieben,
das ist erst im September rausgekommen.
Also, das ist noch ganz, ganz frisch, ähm, das ist das TypeScript-Cookbook, ähm, das
jetzt mit O'Reilly veröffentlicht worden ist, ähm, das war, das war praktisch eine
Auftragsarbeit, also, O'Reilly hat, hat so diese Acquisition-Editors, die suchen halt
noch potenziellen Autoren, schlagen denen Buchprojekte vor oder schlagen vor, hey, möchtest
du zu dem Thema was schreiben, ähm, und sie haben gesagt, sie wollen ein TypeScript-Buch
veröffentlichen in diesem Stil wie TypeScript in Fifty Lessons, ob ich mir vorstellen kann,
noch ein solches zu schreiben, ähm, und am Anfang haben wir gedacht, nein, das geht
nicht, ich hab jetzt schon ein Buch geschrieben.
Ich, ich hab halt einfach, ja, eine gewisse Ansicht, eine gewisse, eine gewisse Stimme,
eine gewisse Idee zu dem Ganzen, hab aber gedacht, naja, meine Saison O'Reilly, probierst
du da mal, dass du kurz ein Inhaltsverzeichnis unterschreibst, wie du dir ein zweites Buch
vorstellen kannst, und, äh, grad hab ich, hab ich 100 weitere Einträge gehabt innerhalb
von drei Stunden, also, ich bin mir am Nachmittag hingesetzt und hab gedacht, ups, wow, da ist
ein Buch da, ähm, und bin mit denen in einen Vertrag gegangen und hab halt dann versucht,
den, den Nachfolger vom, vom TypeScript in Fifty Lessons Buch zu schreiben.
Wenn du in TypeScript in Fifty Lessons lernst, wie das Typsystem funktioniert, lernst du
im TypeScript-Quick-Book alle Dinge, die schiefgehen können, Dinge, die, wenn du wirklich Programme
damit schreibst, wo du merkst, hey, da passt grad was nicht, da, da, du spielst das Typsystem
nicht so mit, wie ich mir das denke, beziehungsweise brauche ich halt irgendeine Technik oder irgendein
Prinzip oder irgendein, äh, Pattern, das ich anwenden kann, um einem gewissen Problem
in den Gegenzug zu kommen.
Im Moment, äh, ich dachte jetzt, äh, so Typen hast du damit nichts mehr schiefgehen
kann, und mit Typen, äh, mit sicheren statischen Typen sind die Sprachen immer besser.
Ganz besonders gut.
Genau, da beschreibt man nur mehr Programme, die nur mehr funktionieren, und man hat keine
Bugs mehr, und es ist super-happy, genau.
Ja, ja, genau, ja.
Ja, also ich, ich bin jetzt eine super Überleitung tatsächlich auf das Thema, was wir heute
machen wollen, oder?
Ja.
Weil, also, ähm, ganz herzlichen Dank, dass du heute da bist, Stefan, das, äh, freut
uns sehr, und von, in Peißen haben wir ja von Typen auch schon das eine, andere Mal
so am Rande gehört.
Ja, wir haben diese Episode immer lange vor uns hergeschoben, weil wir uns nie ausreichend
vorbereitet gefühlt haben dafür, aber das ist halt so großartig.
Das ist ein großes Thema, es gibt auch eine ganz große, ähm, Anti-Fan-Gemeinde in Peißen
für Types, irgendwie.
Ja, es gibt auch viele, die das nicht so mögen, ja.
Ähm, wie, wie seid ihr da so, so drauf?
Ähm, also, also habt ihr jetzt Typen schon in eurem Python-Code drinnen, oder ist das
eher nur so das Buch mit, mit sieben Siegeln, das ihr nicht, nicht öffnen wollt?
Annotationen nutze ich persönlich sehr gerne, insbesondere dann, wenn ich mit anderen Menschen
arbeite, um einfach so zu dokumentieren, was macht das denn überhaupt.
Ähm, das heißt aber nicht, dass das immer stimmt, was da steht.
Ist aber gefährlich damit.
Ja.
Das ist ein gefährliches Spiel.
Ja.
Ja, also ich, ich verwende sie auch schon und ich habe halt ein, ein, ein Projekt mal so
komplett irgendwie annotiert, einfach nur auch, weil ich wissen wollte, wie schwierig
ist es denn nun, wie, wie, wie weh tut es denn, äh, irgendwie.
Äh, äh, und, ähm, ja, das, das ging schon, aber es war auch, also, ich, ich habe da jetzt
nicht so wahnsinnig viel, äh, irgendwie Nutzen rausziehen können, aber ich hatte auch vorher
neben Dingen halt auch schon 100% Test-Coverage, insofern, ähm, war da einfach wahrscheinlich
nicht mehr so viel zu, zu, äh, ja, auch das habe ich ja gemacht, um, um, um es mal gemacht
zu haben, als, äh, dass es halt wirklich Nutzen hat.
Ich habe tatsächlich in den Projekten bei mir jetzt Enforced, äh, MyPi, äh, Precommit
Hook, das ist auch schon, das, damit gehe ich ziemlich vielen Leuten auf die Nerven, ähm,
aber.
Ja, da kannst du überall Objekt hinschreiben, oder?
Das, das zählt doch auch.
Ja, okay, also, da sind die meisten übrigens noch nicht draufgekommen.
Oh, da habe ich jetzt ein Geheimnis verraten, hups, Entschuldigung.
Also, ich bin, ich bin beeindruckt von der 100% Test-Coverage, also, äh, ich bin, ich
bin, leider Gottes, der faulste Tester, äh, diesseits der Donau, also, das ist, ähm,
ähm, ich sage immer, also, ich habe diesen, diesen, diesen Spruch gehabt, wie nur in einer
Agentur gearbeitet, da habe ich gesagt, ja, getestet wird beim Kunden, wenn es in Production
ist, es soll reichen, aber, aber, ähm, aber ich, ich, keine Ahnung, liegt, liegt es an
der Software, die ich schreibe, oder an der Rolle, in der ich bin?
Ähm, ich bin sehr, sehr selten jemand, der, der wirklich ausgiebige Tests sweet schreibt
und da, da, also, das ist, glaube ich, ähm, also, Arsch auf den Haupt, das ist, das ist,
glaube ich, meine größte Schwäche.
Ja, das ist ja, 100% Coverage ist auch schon sehr, äh, ja, aber, aber, ich finde es beeindruckend,
wirklich.
Ja, das haben wir in dem Projekt, in dem ich bin, auch 100%, also, 100% Branch Coverage
sogar.
Mhm.
Und TypeScript.
Oh.
Also, es, es ist ja oft so, dass man das so ein bisschen als gegensätzliche Pole ansieht,
sag ich mal.
Ja.
Die einen machen Typen.
Die anderen machen Testing.
Und das ist schön, Stefan, dass du das jetzt bestätigt hast, aber.
Ich bin quasi das, das, das Typen-Paradebeispiel, ne?
Ja.
Ich meine, das Ding ist, das, ähm, äh, JavaScript und Python haben ja, haben ja, ähm, die gleiche
Eigenschaft, dass beides, äh, in, in der Praxis dynamisch typisierte Sprachen sind.
Sprich, natürlich gibt's Typen, sonst könntest du mit den ganzen Werten nichts anfangen.
Es ist aber relativ egal, was das für ein ist.
Du kannst dann, ähm, ähm, ähm, ein Number oder ein Integer einer, einer Variable zuweisen
und im nächsten String, äh, Schritt dann String und keiner regt sie auf und keiner
beschwert sie.
Und das funktioniert halt einfach, ne?
Ähm, oder du kannst verschiedene Typen mischen.
Du kannst ein String mit einer Zahl kombinieren.
Nee, das geht in Python nicht.
Oder umgekehrt.
Das geht nicht.
Okay, cool.
Wow, dann setzt ihr schon mal einen Riesenschritt weiter als, als in, in JavaScript.
Ja, gut, das ist halt das große Problem an JavaScript, oder?
Dass du solche implizite Konversionen drin hast.
Ja, also.
Genau, das gibt's ja.
Das heißt dann immer weak getyped versus strong getyped, aber ich meine, ich weiß
nicht, ob das irgendeinen Sinn da gibt.
Aber diese ganzen Bezeichnungen sind ja auch alle nur so ein bisschen, ja.
Also die schöneren Bezeichnungen sind meiner Meinung nach statisch und dynamisch,
wo du einfach weißt, okay, ähm, definierst du den Typen im Vorhinein oder wird er
definiert durch die Verwendung?
Ähm, ähm, weak und strong, also schwach und stark ist, ist, sind, sind sehr schwache
Bezeichnungen, meiner Meinung nach.
Ja.
C zum Beispiel hat ein, ein statisches Typsystem, aber ein sehr, sehr schwaches.
Ob du jetzt einen Character hast oder ein Integer oder irgendwas anderes, es ist
einfach komplett wurscht.
Also.
Kannst alles damit machen.
Und kannst auch hin und her wechseln und kannst auch Bitfiddling mit allem möglichen
Scheiß machen.
Genau, genau, genau.
Wundervolle Bezeichnung.
Und vor dem, vor dem ist das, das Statische eigentlich das Wichtige, weil es ja ein bisschen
eine andere, ähm, Herangehensweise ans Programmieren, ähm, voraussetzt.
Du, du machst dir einfach mehr Gedanken über, ähm, über die, die mögliche Wertemenge
einer Variable, bevor du einen Wert zuweist.
Das schließt jetzt nicht aus, dass du sagen kannst, hey, du, du bekommst einen Typen
durch Typ-Inferenz.
Das mache ich sehr sehr gern.
Aber ich sage, hey, diese Variable X ist 3 und dann weißt du, dass das ein Number oder
Integer oder was auch immer ist.
Das geht natürlich auch.
Das machen auch moderne Programmiersprachen sehr, sehr, sehr gerne, ähm, ähm, grundlegend,
dass du halt mit wenig Annotationen machen kannst.
Nichtsdestotrotz, ähm, definierst du Verträge zwischen Bausteinen deines Codes, zwischen
den Functions, zwischen Methoden in deinen Klassen etc., in denen du sagst, du erwartest
aber jetzt diesen Wertebereich und nichts anderes.
Und du lässt auch nichts anderes zu, ne.
Und, ähm, TypeScript ist meiner Meinung nach doch sehr, sehr gut.
Sehr, sehr spannend.
Ähm, weil TypeScript halt versucht, eine sehr dynamische, dynamisch typisierte Programmiersprache
und generell sehr dynamische Programmiersprache wie JavaScript in einer gewissen Art und Weise
zu formalisieren.
Also das Ziel ist ja jetzt nicht, dort eine komplett neue Programmiersprache zu definieren
oder zu entwickeln, sondern auf Basis einer bestehenden irgendwie ein Regelwerk zu finden,
damit alle beteiligten Programmierinnen und Programmierer irgendwas zu diskutieren haben
und wissen, was sie tun.
Was da eigentlich vor sich geht, ne.
Nichts ist schlimmer, wenn du ein halbes Jahr, nachdem du dein Programm geschrieben hast,
wieder zurückgehst und dich fragst, hey, was war denn diese Variable X nochmal, ne.
Also was habe ich mir da eigentlich gedacht?
Und dann hast du irgend so eine fette Wurst an Code und musst herauslesen, was du eigentlich
damit machst.
Und, also das ist was, was mich immer komplett verwirrt hat, wie, also ich habe da nie ein
funktionales Programmierparadigmen nochmal ausprobiert, viel mit asynchronen Promises
gearbeitet in Node, irgendwelche dynamischen Objekte gehabt.
Ich habe ständig neue Keys hinzugefügt und wieder entfernt und das war kurz und effektiv
und schnell und schön und drei Monate später habe ich nicht mehr gewusst, was ich dort
mache.
Wenn ich das Programm heute lese, ich kenne mich nicht aus und ich würde es auch heute
nicht mehr so machen, einfach weil, weil, also gut österreichisch, sorry, Graut und
Ruben einfach zusammenschmeißen und hoffe, dass am Ende alles funktioniert und das ist
gut für kleine Skripte.
Das ist gut, wenn du irgendwie geschwind was runterheckst, aber wenn ihr wirklich mit einem
Team versucht, realisieren.
Wenn du wirklich mit einem Team gearbeitet hast, dann ist das, glaube ich, die niedrigste
Schwelle, die du hast, um zu dokumentieren, was jetzt von deiner Software und von den
Nutzern deiner Software überhaupt erwartet wird.
Und ich glaube, deswegen sind Typen so wichtig und deswegen, glaube ich, sind Typen auch
in Python sehr, sehr interessant, weil du ja da die gleichen Voraussetzungen hast.
Also du musst ja wissen, was erwartet jetzt deine Funktion dort und je mehr Bibliotheken
das du hast.
Wie gesagt, ich wäre schon gesagt, ich bin jetzt in diesem Lunchen und LLM-Wahnsinn irgendwie
angefangen.
Da hast du sehr, sehr komplexe Objekte, die du hin und her schickst, nicht?
Woher sollst du wissen oder woher soll ich wissen, der keinen Python schreibt, sondern
nur lest, was da jetzt eigentlich erwartet wird und was ich da rauskriege?
Ja, das finde ich auch.
Also das ist tatsächlich so als Dokumentationstyp für Python ist das, was ich auch am häufigsten
gerne mag.
Also in kleinen Closures, in kleinen Methoden oder Funktionen irgendwie die Argumente so
zu annotieren, dass beim Lesen klar wird, was denn der Mensch da haben möchte, der
diese API bereitstellt, dass ich das irgendwie zusammengebaut kriege oder so oder verstehe,
was er denn da braucht oder sowas.
Das sehe ich anders.
Ja, ich weiß nicht so.
Aber das hätte ich ja heute hier.
Ja, genau.
Lieber Johannes.
Also ich finde, dass das Argument, was der Dominik bringt, das stimmt, wenn man nur
simple Funktionen hat.
Also wenn man wissen will, was, ob da jetzt ein Integer oder ein String rein muss.
Und man hat vier Parameter und die haben alle simple Typen.
Aber für mich fällt es auseinander, wenn die Typen komplex werden, weil dann hast du
einen Typen, die irgendwelche interne Struktur haben, die völlig opak ist.
Und dann hast du 17 verschiedene Varianten davon.
Und am Ende weißt du genauso wenig, was da rein muss und was nicht.
Und das hat alles so seine Grenzen.
Ja, ich habe letzte Woche mit einem Freund telefoniert.
Da haben wir uns auch kurz über Type Annotations unterhalten.
Und er meinte so, ja, schön und gut.
Und ich sehe die Vorteile.
Und zwar, ich finde es einfach irgendwie hässlich, weil irgendwie.
Die IDE kann ja mit was anfangen oder so.
Wenn man das jetzt anguckt.
Er meinte so, ja, dann hat man bei den ganzen modernen Libraries heutzutage, dann hast du
da irgendwie Funktionen und da hast du pro Zeile einen Parameter, weil da kommt immer
der Name des Parameters.
Dann kommt halt die Type Annotation, die einem nicht viel sagt.
Und dann kommt nochmal ein Kommentar, der erklärt, was die Type Annotation einem eigentlich
sagen will und das dann so untereinander.
Und dann kommt halt die Type Annotation, die einem nicht viel sagt.
Und dann kommt nochmal ein Kommentar, der erklärt, was die Type Annotation einem eigentlich sagen will.
Und das dann so untereinander.
Und dann kommt nochmal ein Kommentar, der erklärt, was die Type Annotation einem eigentlich sagen will.
Und das dann so untereinander.
Und dann meinte er so, das erinnert mich total an C-Code aus den 80ern.
Ich weiß nicht, ich mag das nicht.
Das ist nicht Python.
Ja, aber ich muss, also dieses Argument höre ich auch häufig, wenn man jetzt so bei den
alten, zum Beispiel übernächste Woche wieder oder ist das nächste?
Ich weiß nicht.
Übernächste, ja.
Auch hier aufs Python-User.
Oder das nächste, nächste Mittwoch.
Das nächste Woche, ja.
Ja, ja.
Genau, gehen.
Da gibt es dann die alteingesessenen, die sagen auch immer so, ah.
Immer nur achs, quaks.
Sternchen, Sternchen.
Nein, das nicht, aber.
Ja, ja.
Aber das ist schon so ein Punkt, mit dem viele Probleme haben, ja.
Ich kenne genug Python, dass ich den Witz mit achs, quaks verstanden habe.
Dann gehörst du schon zu den Oberen.
Ja, nein, also gut.
Ich bin immer sehr kritisch, wenn es darum geht, dass man die Ästhetik bewertet, weil
ich schreibe sehr viel in Rust.
Rust ist eine sehr unästhetische Sprache.
Du hast quasi, hey, wie viel sind da?
Ich habe übrigens keine Programmiersprache vertragen und Rust sagt ja, also her damit.
Aber dafür ist mir halt in Rust zu jedem Zeitpunkt absolut hundertprozentig klar, was passiert.
Und das ist halt auch ein Vorteil, den man nicht leugnen kann.
Gerade, wenn man in Teams arbeitet.
Und da nehme ich halt die paar Annotationen, die ich da habe, einfach hin.
Und irgendwas hat jetzt da geblinkt, ich bin kurz rausgekommen.
Und verstehe zumindest, was passiert.
Und ich muss ganz ehrlich sagen, was ich rein jetzt sehe von dem, was.
Wie Python Typings umsetzt, ist es sehr minimalistisch.
Also da kann man sich noch weit, weit mehr verausgaben.
Also ich finde das gar nicht einmal so hässlich.
Ja, ich finde das Problem ist halt, dass diese Typsysteme oft zu weit getrieben werden.
Und das treibt dann Blüten.
Ja, also für alles und jedes und nochmal die letzte.
Genau, für alles und jedes.
Und das muss dann alles ein benannter Typ sein.
Und das geht dann.
Also ich habe hier.
Ich habe hier ein Beispiel aus der Apple-Dokumentation.
Da gibt es einen Typen, der heißt CN Label Contact Relation Younger Cousin Mother Siblings Daughter or Father Sisters Daughter.
Ja.
Das ist ein Typ.
Wunderschön.
Ja, ob man den jetzt braucht oder nicht.
Auf der anderen Seite hier ein Beispiel aus der .NET-Bibliothek.
Die Links sind dann alle in den Show Notes.
Da gibt es eine Methode, die heißt Run.
Die hat 30 Argumente.
31.
Das erste ist ein Object.
Das heißt Makro.
Das zweite ist ein Object.
Das heißt Arc1.
Das dritte ist ein Object.
Das heißt Arc2.
Und so geht es weiter bis Arc30.
Grandios.
Da bringen mir die Typen nichts.
Da sagen mir die Typen nicht, was das macht.
Und zwar in beide Richtungen nicht.
Ja, besser wird man dadurch nicht mehr.
Und es gibt halt da diese Fetischisten, die sagen, du musst aber überall benannte Typen drin haben.
Und du musst das Typsystem vollständig aufbauen.
ausnutzen. Und
das geht mir genauso auf den Senkel wie die alten Herren,
die dann sagen, nein,
Typ-Annotationen ist ekelhaft
und hässlich und wie in den 80ern.
Irgendwo in der Mitte ist dieses Maß.
Du hast mich gerade
an etwas Wunderbares erinnert. Ich bin ja
in einer Java-Welt
groß geworden.
Und habe gelitten dort.
Ich auch.
Ich komme aus der
Nähe von Linz und direkt
auf dem anderen Bergerl
gegenüber wohnt
einer der Erfinder vom Spring-Framework.
Also wir kennen uns, unsere Kinder gehen
gemeinsam in die Schule und solche Sachen.
Und da gibt es eine Klasse, das ist die
Has this type pattern tried to sneak in
some generic or parameterized type pattern
matching stuff anywhere visitor.
Und das ist fantastisch.
Also bei uns in der Firma
haben die Java-Developer
diese Weißkin-Monitore,
aber nicht, dass sie irgendwie
dort mehrere Fenster nebeneinander
hinkriegen, sondern ich sage immer, das ist, damit sie die
Klassennamen ohne Zeilen-Humbuch
durchstehen können.
Und das ist natürlich, das ist
Mumpitz, das ist ganz klar. Also ich denke, man
muss die ganzen Sachen immer ein bisschen
pragmatischer sehen
und halt auch einen richtigen Nutzen
oder wissen, welchen Nutzen man daraus
zieht. Das gilt für alles in der Softwareentwicklung
meiner Meinung nach.
Das ist der Anzug.
Genau. Was ich am liebsten habe,
zum Beispiel, also ich schreibe immer noch sehr, sehr viel
Node und wenn ich starte damit,
dann habe ich
einmal keine Typen zu Beginn, weil ich ja selbst
nicht weiß, wie meine Software am Ende ausschaut.
Aber wenn ich dann fertig bin, wenn ich dann ein paar Funktionen
extrahiert habe, dann lasse ich mir ein paar Typen
einführen, einfach nur, dass ich festgelegt habe,
was ich hier an dieser Stelle
erwarte, damit, wenn ich zurückgehe
oder wer von meinen Kolleginnen oder Kollegen
zurückgeht, auch weiß,
was dort zu erwarten war.
Und um das geht es eigentlich. Und gerade in
TypeScript, also gerade auch in meinem Buch gibt es
ein paar Beispiele drinnen,
da kann man sich schon
richtig, richtig vorausgucken.
Da gibt es irrsinnig mächtige Werkzeuge,
wie String-Template-Literal-Types oder
Tappel-Types, Variadic-Tappel-Types.
Das sind Dinge, wo ich mir denke,
Wahnsinn, dass das überhaupt geht.
Also beeindruckend technisch, dass das geht.
Aber die Use-Cases dafür
sind halt stark limitiert und man muss wirklich überlegen,
ob das dafür steht. Und ich habe dort
drei Lektionen, wo wir
ein funktionales
Programmiertool wie
Currying versuchen, auf drei
sehr, sehr komplexe Orten umzusetzen und sagen,
aber überlegt, ob diese
Typen jetzt das gerechtfertigen, was du
da als Funktion schreibst, oder ob du nicht
lieber eine Funktion nimmst, die viel, viel weniger
kann, aber
genauso einfach zu verwenden ist und die
einfach zu typisieren ist, mit der du
besser die
Typinformation rauskriegst.
Und das stelle ich dann auch so zur Diskussion.
Man muss halt immer abwägen können,
wie rechtfertigst du
den Einsatz dieses Werkzeugs und wie weit
treibst du das?
Ja, ich glaube, das ist tatsächlich
gar nicht so einfach herauszufinden.
Ja, und
...
Ich glaube aber, dass da noch ein anderes Problem dahinter ist.
Ich bin sehr froh, Stefan, dass du
da gesagt hast, dass du gegen
Tests bist oder
gegen große Testcoverage. Und du hast jetzt
gerade eben so ein bisschen deinen Ansatz beschrieben,
wie du Programme schreibst und
du hast was gesagt, was meiner Meinung nach
ganz wichtig ist. Du hast gesagt, du fängst
an, ein Programm zu schreiben und weißt noch nicht, wie es am Ende
ausschauen wird.
Und das geht mir genauso.
Und das ist einer der Gründe, warum
ich
Python mag,
weil mir das die Freiheit gibt,
da so explorativ rumzugehen, ohne
mir groß Gedanken machen zu müssen,
wie es denn jetzt sauber zusammenpasst.
Und ich glaube, dass das ein Programmierstil ist.
Das ist, ich nenne das, exploratives Programmieren.
Ich muss so ein bisschen diesen Space
erkunden, muss so ein bisschen sehen,
was kann ich, was schaffe ich, was mache ich.
Und das ist halt mein Stil.
Ich gehe da rein und sage, jetzt machen wir erstmal irgendwas.
Es gibt aber auch Leute, die da
vielleicht mathematischer rangehen
und die sagen, okay, ich habe hier einen Plan
und eigentlich auf dem Papier habe ich es ja schon hingeschrieben.
Und dann kann ich
auch die Tests zuerst schreiben, weil ich weiß ja schon, was
das machen soll. Ich weiß ja schon, wie es am Ende ausschaut.
Oder ich kann gleich die richtigen Typen reinschreiben.
Ja, da muss man sich ja das Akronym ändern
und einfach Type-Driven-Development
von einem neuen Stand starten.
Ja, das sind, glaube ich,
einfach zwei verschiedene Arten zu
programmieren, die eben verschiedene Dinge
eher bevorzugen.
Es ist tatsächlich,
glaube ich, auch ein bisschen von der Sprache abhängig, weil
es ist ja auch ein bisschen von der Sprache abhängig, weil
ich glaube, Python und JavaScript unterstützen
ja diese Art des explorativen Programmierens
sehr, sehr stark. Einfach eben in
Gensity-Typen auch entfernen. Also du kannst
wirklich einfach einmal herumprobieren und schauen, was
rauskommt. Und schauen, wie
weit du mit deinen Ideen
kommst.
Was ich spannend finde, also ich bin jetzt
seit einigen Jahren auch sehr, sehr stark in Rust drin.
Ich habe das jetzt schon erwähnt. Und sorry, das ist der klassische
Rust-Nutzer. Nicht, dass ich ständig sage, dass
er Rust verwendet, aber es ist halt
wirklich so. Die Sprache macht einiges sehr, sehr richtig.
Du hast in Rust eine sehr
stark typisierte Programmiersprache
mit einem fantastischen Typsystem.
Das ist grandios, dass das
so funktioniert, wie es funktioniert.
Das heißt, du kannst dir gar nicht leisten, dass
du einfach nur mal schaust,
wo kommst
du mit deinen Wertebereichen
überhaupt am Ende hin. Aber trotzdem schafft
es Rust, dass du explorativ
arbeiten kannst, weil einfach die Mittel, die
zur Verfügung gestellt werden, so zugänglich
sein können, weil die Typen, die
vor Hause schon drinnen sind, so
entgegenkommen sind. Das heißt, du kommst mit ein paar
Basistypen aus der Standardbibliothek
und den üblichen Wertebereichen schon
sehr, sehr weit, bevor du dir selbst deine eigenen
Strukturen und
Interfaces, nenne ich es jetzt einmal,
heißen, anders überlegen musst.
Und das ist sehr, sehr spannend. Es ist
aber trotzdem ein anderer Wert des Programmierens.
Das
Endresultat schaut auch dann
anders aus. Also ich mag das aber auch gerne.
So Models oder so Dataclasses
irgendwie zu bauen und dann zu gucken, hey, was sind
das denn überhaupt für Objekte? Und dir halt dann direkt
zur Annotation zu verwenden, das ist
so strukturell sehr klar.
Nein, es schließt sich auch nicht aus.
Also es ist,
ich denke mir,
die
Gefahr liegt wahrscheinlich, dass man sich auf eines
komplett verschreibt.
Auch das, was der Johannes
wieder gesagt hat, ganz richtig ist,
es ist am Ende des Tages, es ist
ein Werkzeug, nicht? Und es muss irgendeinen Job
erfüllen.
Und wenn das gut funktioniert, dass du dir
vorher Gedanken darüber machst, welche Daten, das du
benötigst, dann ist das absolut legitim,
meiner Meinung nach, nicht? Also ich finde
gerade so
angenehm. Wichtig ist, dass die
Sprache, mit der du arbeitest, das unterstützt.
Ja, ich glaube auch eben.
Aber ich meine,
das hat natürlich mal einen Preis, weil es halt zusätzliche
Komplexität unter Umständen halt
auch einführt und es dann halt vielleicht auch
unzugänglicher macht. Also ich, worauf ich
eigentlich hinaus will, ist,
naja, je nach Use Case kann es
halt unterschiedlich
nützlich sein. Also wenn man halt zum Beispiel
eben in einem Team
einer großen Software schreibt, dann kann es halt sein,
dass es sehr viel bringt, auch
irgendwie möglichst viele Hürden
zu errichten, bevor irgendwie
Code... Du willst quasi gar nicht, dass die Entwickler
irgendwas machen. Ja, das ist leider
halt eine Strategie, das ist schwer
zu unterscheiden von, irgendwie man
versucht, Fehler zu vermeiden, bevor sie dann
produktiv gehen, zu bloß nicht
deployen, was dann halt auch viele Leute machen,
was halt irgendwie auch blöde
Konsequenzen hat unter Umständen dann, weil
also viele Sachen, also man hat dann zum Beispiel nicht nur ein
Staging-System, sondern noch so drei andere
oder vier andere und führt immer mehr hinzu,
sodass man bloß nicht nach Produktionen
deployen muss. Ich habe zwei
Abteilungen bei uns kennengelernt, die haben
Employment-Release-Zyklus so alle sechs
Monate. Ja, genau,
in den sechs Monaten passiert halt auch nichts, aber
ja, ist halt vielleicht auch irgendwie
ein Konflikt mit anderen Zielen.
Hast du etwa diesen Artikel über Move
Fast-and-Break-Things gelesen, der kürzlich
rausgekommen ist? Ja, genau.
Ja, richtig. Und
genau, also an der Stelle
macht es ja vielleicht, wenn man
in so einer Situation ist, sehr viel Sinn
da, von den Leuten
zu verlangen, dass sie sich erst Gedanken machen und dass halt man
versucht, möglichst viele Fehler
zu fangen, bevor sie halt in der Produktion
aufschlagen, weil da ist es halt viel
teurer, sie halt zu entfernen als
aber es kann halt total anders
sein, wenn jetzt jemand zum Beispiel, und das ist
halt eine der Stärken bei Python,
bei JavaScript auch
ein bisschen anders,
dass es halt vor allen Dingen auch von
Leuten benutzt wird, die sich selber gar nicht als professionelle
Programmierer sehen würden,
sondern die eher sagen würden, naja, ich bin eigentlich eher so
so Data Scientist oder
Analyst oder sowas,
oder halt irgendwie jemand, der
irgendwie, keine Ahnung, Roboter irgendwie
dazu bringt, irgendwie lustige Dinge zu machen oder sowas
und ich will mich jetzt eigentlich... Oder Teilchenbeschleuniger.
Oder Teilchenbeschleuniger, also solche
Sachen, genau. Und ich will gar nicht
jetzt irgendwie Typ,
Typtheorie verstehen oder
irgendwie, keine Ahnung, auch
Unitests und da sind wir auch bei einem
Ding, wo die Sachen so ein bisschen ähnlich sind.
Also ich dachte auch lange Zeit irgendwie
Unitests sind was ganz anderes
und Typisierung
ist ein
anderen Ende von einem Spektrum und ich bin eher
so Team Test, aber inzwischen denke ich so,
naja, das ist schon relativ ähnlich auch. Ich meine,
auch Unitesting ist halt so eine Sache, die man sich
erst irgendwie erschließen muss und für
manche Leute macht das einfach, wenn man
halt irgendwie so in einem Jupyter Notebook
Sachen macht,
ja, manchmal macht es schon Sinn, das zu testen,
aber für viele Leute ist es auch nicht, lohnt es sich,
weiß ich nicht, ob es sich wirklich lohnt, da so
tief einzusteigen und
aber ich meine, klar, wenn man jetzt große Software
im Team entwickelt, dann klar hat man
Tests und eine CI-Pipeline
und weiß ich nicht, diesen ganzen Kram halt, ne?
Ja. Das Stichwort
da ist doch Programming by Contract,
oder? Und das ist halt eine Möglichkeit,
so einen Contract zu schreiben, aber
also ich bin da in so einem Zwiespalt, ja,
einerseits bin ich total genervt von solchen Typsystemen,
die dann sehr präzise sind und die dann,
ich habe auch, ich bin auch mit Java
aufgewachsen, Stefan, also ich zähle deinen Schmerz
und dann
wirkt man sich ab
und macht ein Pair aus Int
und String und dann weißt du aber nicht,
wie du an den zweiten drankommst und
ach, das ist alles ganz schön.
Aber auf der anderen Seite
gibt es nicht die Möglichkeit zu sagen,
ganz triviale Sachen zu sagen, ja, zum Beispiel
hier muss eine Zahl rauskommen, die immer größer ist als 0
oder immer kleiner als 0, größer als 0
geht ja tatsächlich noch, aber kleiner als 0
ist 0, kannst du nicht sagen. Du kannst nicht sagen,
hier muss was rauskommen, was zwischen 0 und 1 liegt.
Und das ist irgendwie
so eine weirde Sache,
ich bin da in so einem Zwiespalt, ich hätte gerne
keine Typsysteme, aber wenn ich Typsysteme
hätte, hätte ich gerne so welche, die so exakt
sind, dass ich sowas
sagen kann und dass mir dann auch der Compiler sagen kann,
ah, Moment, hier rufst du eine Funktion aus
mit einer Zahl, die zwischen 0 und 1
sein kann, aber die muss zwischen
0 und minus 1 sein.
Nee, das ist nicht Validierung, sondern das ist,
nee, das sind sogenannte Value Types, das kannst
du auch in ein Typsystem reingießen, wenn du das
möchtest, das hat nur
quasi keine Sprache.
Ich glaube, ich habe eine mal gesehen.
Eitris, ja genau.
Also grundsätzlich im funktionalen Programmierbereich
hast du das
relativ häufig, aber
ich würde mal
annehmen, nicht in den
populären oder weit
verbreiteten Programmierungen. Nee, nicht in denen, die man benutzt
irgendwo. Oder die man schon mal gehört hat.
Aus einem ganz
einfachen Grund, weil du halt unter den
Wertebereichen von einer Zahl
halt auch tatsächlich irgendwelche Bytes
liegen, die von einer CPU ausgelesen
werden. Also das,
wenn dir
das Typsystem das erlaubt, dass du dort
irgendwas zwischen minus 1 und plus 1
zum Beispiel definierst, oder mein Gott,
eine ganze Zahl zwischen 25, was auch
immer, dann schiebst du
die Validierungen nur an eine andere Stelle.
Ja gut,
aber das machst du mit einem Typsystem generell, oder?
Also ich meine, sobald der TypeScript-Compiler
durch ist, ist er weg.
Das ist,
das ist sehr richtig, genau. Das ist sehr richtig.
Ich glaube, in Python genauso, ne? Wenn ich mich nicht täusche.
Ja, generell. In Python sind die
Annotationen erstmal gar nichts. Genau, das ist
in Python auch so leicht anders. Das wissen
auch viele Leute. Ja, das ist ganz, das ist halt,
wenn man zu den praktischen Dingen kommt, die halt
damit problematisch sind, also ich bin ja häufiger auch
irgendwie in unterschiedlichen
Firmenkontexten da so unterwegs
und das, was
ich in letzter Zeit halt häufig sehe, ist halt
sowas wie, Leute annotieren
halt irgendwie ganz viel,
sie haben keinen statischen Type-Checker,
der da drüberlaufen würde und in Python ist halt auch keiner
eingebaut, was vielleicht auch nicht so gut ist
und man müsste da, und dann
mache ich dann sowas wie, ich lasse mal
MyPy drüberlaufen und die Leute verwenden
alle TypeDict und sowas und denken halt,
ja, das überprüft jetzt, ob meine ganzen Werte
da so, also meine ganzen
Werte in dem Dict halt so den
richtigen Typ haben und so
und Python selber macht
da gar nichts. Das überprüft überhaupt nichts. Das
ignoriert die Annotationen einfach.
Ich lasse dann MyPy drüberlaufen und kriege dann so
414.
Fehler. Und dann denke ich mir so, okay,
ja, also, du hast da schön deine Annahmen darüber,
wie die ganzen Typen aussehen, dokumentiert, aber die sind halt
leider alle falsch. Das stimmt überhaupt gar nicht.
Und, ähm, ja, da ist dann,
denkt man sich, warum hat man sich denn überhaupt die Mühe gemacht?
Also, das ist halt irgendwie...
Ja, wie ist denn der Zustand der
Entwicklungsumgebungen in Python?
Äh, kommt drauf an.
Also, auch die hängen halt daran, ob du
MyPy zum Beispiel als
Static Type Shaper Extension
richtig konfiguriert hast, ja, und
ob dann auch MyPy die Stubs
halt lesen kann, die halt
da notwendig sind oder halt auch nicht.
Die müssen halt alle dann... Aber viele IDEs behandeln das doch
auch selber schon. Ja, ja, genau.
Da gibt's ja auch dann, ähm, ich weiß gar nicht
jetzt, äh, was bei VS Code
dann normalerweise verwendet wird.
MyPy ist Extension. Ja, aber
ich meine, wenn jetzt zum Beispiel einfach nur das...
Also, IntelliJ macht das selber. Ja, die machen das,
die haben einen proprietären, dann gibt's irgendwie
MyPy, klar, äh, und es gibt
halt noch PyWrite und, ähm,
der Language Server
für Python, kann es sein, dass er selber in
Type... von Microsoft, dass er in
TypeScript geschrieben ist, ich glaube schon. Ich meine, schon, oder? Ja.
Ja, es ist, ähm, es ist nämlich spannend,
weil ich, ich, ich denke,
TypeScript hat da einige
richtige Entscheidungen getroffen auf dem Gebiet.
Ähm, die, die ihr jetzt
alle angesprochen habt, was, was kompliziert
und was komplex in, in Python ist,
ähm, nämlich,
dass du, dass die Sprache dir zwar ein
Typsystem anbietet,
cool, aber eigentlich nichts damit macht,
sondern du brauchst eh nur einen extra Typchecker und
und anscheinend wie bei den Package Manager
dann gibt es dort noch mehrere, die du, wo du
dir dann noch aussuchen kannst, welcher gefällt dir jetzt
und welcher passt zu dir
und so weiter und ich finde das grandios, dass
es Auswahl gibt, keine Frage, aber es erhöht
natürlich auch die Komplexität. Ähm, und
TypeScript hat, hat,
TypeScript selbst ist ja eigentlich
mehrere Dinge. Es ist zum einen mal
ein Typsystem, cool, also wie werden
Typen geschrieben, definiert, wie
sind sie im Zusammenhang? Es ist auch ein
Typchecker dabei, ähm, also,
also der, der TypeScript Compiler
TSC macht in erster Linie mal Typechecking,
aber kompiliert
dann auch tatsächlich JavaScript-Code, den
du ausführen kannst. Also wir, es gibt so jetzt Proposals,
dass du auch Typernotationen
irgendwann einmal in JavaScript schreiben können
solltest, ähnlich wie in Python, wir sind dort einfach von der
Runtime ignoriert, ähm, aber da
sind wir noch nicht, also da kommen wir erst hin.
Das heißt, du brauchst auch den Compiler, das heißt, wir haben
Typsystem, Typchecker, Compiler und noch auch ganz, ganz
wichtig, eine Integration
in Editoren und Entwicklungsumgebungen,
ne? Ähm, und das war eigentlich,
also, dieser, dieser gesamte
Tooling-Aspekt rundherum, nicht nur
das Typsystem anzubieten, sondern auch
Werkzeuge anzubieten,
dass du nachher zu validem JavaScript-Code kommst
und du deine Fehler siehst
und die Fehler auch sofort in deinem Editor und
deiner IDE dargestellt werden,
sind meiner Meinung nach genau
die Punkte, die noch auch den, den
unter Anführungszeichen Ziegelszug
von, von TypeScript auch, ähm,
ähm,
zu verantworten gehabt haben, ne?
Ähm, weil ohne dem hast du halt
nur die Hälfte der Dinge, du brauchst halt irgendwie
alles, damit du sauber, ähm,
ähm, entwickeln kannst.
Ähm, und wenn ich mir denke, dass ich Visual Studio Code
aufmache, wo TypeScript schon, schon drinnen ist
und ich mache irgendein JavaScript-File auf
und es läuft im Hintergrund schon der
TypeScript-Compiler und checkt meinen JavaScript-Code
und versucht zu inferieren
und zu verstehen, was ich schon geschrieben habe,
ohne einzige Typ-Annotation
und ich kriege aus dem raus schon
Autocomplete und die
ersten Warnings, dass vielleicht irgendwas nicht ganz
schief, äh, nicht ganz, nicht ganz
rund läuft und schief geht, das ist
so viel wert, ohne dass ich eine Zeile
TypeScript schreibe, wo ich mir denke, ja,
das ist eigentlich eine richtig gute Idee
gewesen. Ja.
Naja, absolut. Da kann, da kann Python
sich, glaube ich, auch noch eine Scheibe abschneiden davon,
weil da gibt's noch so ein paar Löcher.
Ja, oder was? Also wenn ich jetzt sage,
ich installiere PyCharm oder, oder wie auch
immer, welche Idee, und die kommt
schon mit einem Typ-Interpreter
oder einem Type-Checker mit,
das wäre natürlich göttlich nett, dass du einfach sagst,
hey, du brauchst nicht mehr die Typ-Annotation schreiben,
aber die Idee hat
irgendein Type-Checker schon bei Default
drinnen. Wahrscheinlich schreiben sie ihn selbst, weil
JetBrains schreibt das irgendwie selbst.
Haben sie. Dann werde ich schon
krank. Genau, der von JetBrains ist,
der von JetBrains ist, ja, aber die,
der ist supergut.
Das ist einer der Gründe, warum man
PyCharm verwendet, weil die halt da die
beste Typ-Inferenz haben und die besten.
Ich meine, man merkt schon, dass das
Java ist, ja, aber.
Aber das können sie, also da sind sie richtig, richtig
gut. Ja, das ist super. Schon wieder Rust,
für Rust haben sie auch einen eigenen geschrieben, also
die ganze Welt verwendet Rust Analyzer.
Außer du nimmst Rust
Rover von JetBrains, dann ist
da ein eigener drinnen und sie
haben auch Recht damit. Also
das ist halt
alles richtig gut integriert in die Werkzeuge, die sie
zur Verfügung stellen.
Ja, auch ein zusätzlicher Effekt, den das
hat, dass halt quasi bei Python halt
der statische Type-Checker
halt nicht so wirklich zur Sprache dazugehört,
ist halt auch, dass bei TypeScript ist es halt
so, dass was sich sozusagen
in der TypeScript-Sprache tut,
auch immer sofort verfügbar ist.
Und halt dann, und das ist bei Python
nicht so, weil die statischen
Type-Checker vor allen Dingen von den großen
Gebaut werden, halt nicht
MyPy, Dropbox, ich weiß nicht, ob die immer
noch da so hauptsächlich dran sind, aber dann gibt's noch
einen von Google, es gibt noch Pyright.
Die hängen sowieso immer
so ein bisschen in den Versionen hinterher,
weil sie ihre Code-Basis
sowieso nicht an der aktuellsten Version halten können,
weil sie das gar nicht schaffen, weil das einfach zu viel Arbeit ist.
Das heißt, normalerweise
bei MyPy hängst du halt immer so eine
Version irgendwie von dem, was die Sprache
eigentlich kann, zurück, was halt auch
total doof ist einfach.
Du musst ja auch noch darauf einigen,
welches Subset des Typsystems du jetzt
verwendest.
Ja, genau.
Da kann ja gar nichts schief gehen.
Ja, aber TypeScript-Versionen gibt's ja jetzt auch.
Ja, also TypeScript-Versionen-Management
ist ja stark in der Kritik,
ist ja keine Frage.
Jedes Release ist ja bei KeyChange.
Ja, und
das ist aber einfach ein schwieriges Problem.
Also ich glaube tatsächlich, dass TypeScript einfach
dadurch, dass es eine jüngere Sprache ist,
gewisse Fehler
vermeiden konnte, die man halt
vor 20 oder 25 oder 30
Jahren machen musste mit diesen, mit den ganzen
alten Sprachen.
Aber manche Sachen sind halt immer noch nicht gelöst
und Version Hell gehört halt dazu.
Wobei ja
sagen wir, TypeScript ist schon historisch gewachsen.
Also du merkst schon
die dunklen Flecken der Vergangenheit
und versuchst sie zu ignorieren, das ist
ja so, das passiert halt.
So wie Sachen verwendet werden,
hast du noch die Probleme.
Ja, und das muss sich ja auch an JavaScript orientieren.
Also es muss ja JavaScript-kompatibel
sein und da kriegst du halt viel,
sag ich mal.
Ja, auch da kriegst du halt viel Historie.
Wenn ich es sehr freundlich ausdrücken will.
Ein kleines bisschen Programmiersprachen-Historie mitgeliefert.
Ja.
Ja, aber
ich wollte gerade nochmal auf Java ein, wo wir das gerade hatten.
Es ist leider schon ein bisschen drüber,
aber davon wieder weg.
Aber da hatte ich nämlich auch noch so eine
schöne, da habe ich letztens was
sehr, sehr Schönes
gelesen,
dass halt
einer der Autoren,
auch von der Sprachspezifikation
von Java, hat halt
irgendwann mal so geschrieben zu den
Generics, also dass sie die Generics eingeführt
haben. Also ja,
das war irgendwie ein Fehler.
Nein, das ist...
Und dann
hat er irgendwie noch, in dem Buch
selber findet man auch irgendwo so eine sehr schöne
Fußnote.
Ja, wo
halt quasi die
Annotation von Inam,
Inam ist auch ein Partner
ein Problem, aber in Java halt auch.
Und Inam ist
halt irgendwie definiert
als, ich suche gerade, ob ich das hier finde,
ah ja, genau, ist eine
generische Klasse definiert als
Inam, Spitze Klammer T, Extents Inam,
Inam, Spitze Klammer T, Klammer zu,
Klammer zu. Also diese
rekursive Definition ist halt so ein bisschen
schwierig
zu verstehen. Wir haben inzwischen aufgegeben,
es zu versuchen, Leuten zu erklären.
Es gibt irgendwie Spezialisten, die uns
versichert haben, über
Typtheorie, die uns versichert haben, dass das schon alles
okay ist und kein Problem. Und wir sollen uns
einfach nicht so viel Gedanken drüber machen,
was wir sehr gerne annehmen.
Und...
Die Spannung ist ja bei den
Java-Generics, dass
die als einziges
Element im Typsystem von Java
keine
Auswirkungen auf
die Gestaltung der Laufzeitobjekte haben.
Also das sind auch so...
Nach dem Compile-Schritt wird das
einfach entfernt und nie wieder
angesehen. Und das ist halt das
Beeindruckende daran. Also das war halt auch so,
oh shit, das brauchen wir jetzt, wir müssen das
jetzt machen im Release 1.5, glaube ich war das.
Und das war die einfachste und
unproblematischste Art, wie wir
dazu kommen.
Und alle Implikationen,
die das hat, die werden bis heute mitgezogen.
Du kannst quasi generische Klassen
nie wirklich optimieren. Geht einfach
nicht. Was halt spannend ist, weil die hast du
halt überall, du hast überall Generics
drin. Deswegen
werden auch
oft nur in der Standardbibliothek
das generische Object
verwendet, anstatt dass du einen generischen Typ-Parameter
hast. Und was ich mit
Innam sein kann, weiß ich sowieso nicht. Also ich habe das
gesehen bei unseren Kollegen und das ist
kein Innam. Das ist ein...
Also ich finde, das ist
eigentlich jetzt ein guter Zeitpunkt, um noch
mal so ein bisschen zu erklären, worum es
überhaupt geht und was da alles so drin ist. Wir sind jetzt auch schon
auf so einem relativ hohen Fluglevel unterwegs.
Aber wir haben auch
viele Menschen, die uns zuhören,
die vielleicht noch gar nicht wissen, was denn ein Generic
überhaupt ist. Und vielleicht
sollten wir das einmal kurz...
Ja...
Aber jetzt kriegen sie es halt mal gesagt.
Ich habe gesehen, es gibt auch Generics
in Python.
Ja, genau.
Ja, aber wovon geht da an?
Ja, aber also das ist ja...
Also diese Generics in den
Typ-Annotationen, das ist ja
wirklich nur sehr instruktiv.
Es ist nur sehr so
vage gesagt.
Das ist keine... Da ist die Erasure ja noch
viel größer.
Johannes,
was ist denn bitte ein Generic?
Ein Generic
ist eine Spezialisierung eines
Typen anhand eines anderen Typs.
Und das klassische Beispiel ist da die
Java-Liste. In Python
hast du ja eine Liste, die, sag ich mal,
dynamisch typisiert ist. Das heißt, wenn du eine Liste hast,
kannst du da eine Zahl reintun und einen String und
eine komplexe Zahl
und ein Objekt und noch eine Liste.
Das ist immer sehr unterhaltsam, wenn man das
in den... Wenn ich das in den
Seminaren mache, wenn ich den Leuten Programmieren
beibringe, die vielleicht schon mal eine Programmiersprache
gesehen haben oder die schon was
davon gehört haben, dann sage ich hier so, jetzt kannst du da...
Du kannst noch eine Liste auch reintun oder Dictionary
kannst du auch einfach in deine Liste reintun.
Und in Java geht
das nicht. In Java hat jede Liste
einen Typen. Das heißt, du kannst...
Wenn du einfach nur List sagst, dann
meinst du Liste von Objekt. Das heißt, alles, was
du da reintun kannst, ist Objekt. Aber
du kannst diese Liste
spezieller gestalten, indem
du eben diesen Generic-Mechanismus
verwendest und sagst, du hast jetzt nicht eine Liste
von Objekt, sondern du hast eine Liste von
String. Das heißt, der Compiler
weiß an bestimmten Stellen, dass
dieser Typ, den du da hingeschrieben hast,
der vorher vielleicht nur ein Platzhalter war,
in TypeScript verwendet man
dann oft T oder K oder V. Das ist
auch in Stefans Buch
kommt es mehrmals vor,
dass du dann eben zu einem bestimmten
Zeitpunkt diesen generischen
Typen ersetzt durch einen konkreten
Typen und sagst, okay, ich habe jetzt hier
nicht eine Liste von Objekt, sondern ich habe ganz
klar eine Liste von String.
Und dann kann der Compiler oder eben
das Typsystem überprüfen,
dass du da tatsächlich lauter
Strings drin hast.
Der Code, den du da ausführst,
ist genau der gleiche.
Aber du hast jetzt eben einen spezifischen
Typen für eine Liste von Strings gemacht.
Und das hat gewisse Vorteile.
Zum Beispiel, wenn du da ein Objekt rausholst,
kriegst du dann halt eben nicht nur ein
generisches Objekt zurück, sondern du kriegst
dann tatsächlich einen String zurück.
Das ist der große Vorteil, den du in Java
davon hast, dass du diese Accessor-Methoden hast,
die dir die spezifischen Sachen
rausgeben.
War das korrekt erklärt, Stefan?
Ist cool.
Super, genau das.
Also, gut, Experte.
Bestätigt.
Experte, wenn einfach nur lauf genug und schreibt den ganzen Mist auf.
Ja, gut, aber das macht dich zum Experte.
Aber nein, das beschreibt es ziemlich gut.
Du kannst halt mit
Typparametern dir die Entscheidung
auf den tatsächlichen Typen
für später aufheben.
Das ist das, was dort passiert.
Und eben, du sagst, dann wird das halt eine Array-List
von String oder eine Array-List von
Integer.
Und du substituierst diesen Typparameter
mit einem konkreten Typen
und kriegst nachher auch
solche konkreten Typen wieder.
Also, in manchen Programmiersprachen
kannst du dann auch noch so Dinge wie
Constraints oder Bounds angeben,
wo du eben sagst, hey, du hast dort
jetzt einen beliebigen Typparameter, ja,
aber er muss einem gewissen
Subtypen entsprechen. Das heißt, er muss
eine gewisse Funktionalität zur Verfügung haben
oder muss,
also,
TypeScript ist ja
strukturell typisiert,
was bedeutet, dass du einfach sagst, hey, solange die
Methoden dort sind und solange die
Properties dort sind, passt schon,
muss nicht den gleichen Namen haben, muss nicht in irgendeiner
Hierarchie sein, sondern Hauptsache
schaut irgendwie so aus, wie
das eine, was ich da erwarten würde.
Und dann haut das schon hin. Und dann kriegst du halt
zum einen die Sicherheit,
dass du nicht irgendwelche Typen reingibst, die nicht damit
funktionieren würden.
Und zum anderen
kriegst du halt noch mehr
Informationen über deinen Typ noch heraus, wenn du
ihn dann vermeidest. Wie macht man das denn in Python?
Protokolle? Doch, das ist in Python
seit drei, achtem Gründer auch
so, oder kann man das so machen? Man kann auch
nominal typisiert das Ganze
machen, aber da geht das jetzt auch, genau,
mit Typing-Protokolls
geht das auch.
Ja, genau, und dann
Habt ihr das schon mal irgendwo gesehen?
Ja, ich verwende das.
Also ich finde das eine großartige Idee, aber ich
habe es noch nie irgendwo verwendet gesehen.
Ja, ja, doch, also
ich kenne es auch vor allen Dingen aus dem
Fluent Python
Buch von
Luciano Ramalou.
Und
der hat sich auch mit diesem
Typisierungsthema stark beschäftigt
und hat dann diverse Fehler
in der Typeschat
Repository, wo halt die ganzen,
wo auch die Standardbibliothek von Python
genau annotiert
ist, hat er da gefunden
und viele davon konnte er
fixen mit
diesem Structural Typing Ansatz.
Und also Fehler
im Sinne von halt die
Annotationen waren halt irgendwie, da waren halt
False Positives oder halt False Negatives
möglich. Auch sehr interessant,
wenn man sich halt anguckt, welche
Fehler sind häufiger.
False Positives sind viel, viel
häufiger bei Typ-Annotationen als False
Negatives. Also in der
Python-Standard-Bibliothek waren es irgendwie achtmal
so häufig.
Was halt auch ein Hinweis darauf ist, dass es halt für Leute wichtiger ist, dass halt die, also falls positiv heißt, eine Annotation hat gesagt, nee, du kommst hier nicht rein, du bist nicht der richtige Typ, dein Typ ist hier nicht gefragt.
Sozusagen, obwohl es eigentlich doch okay gewesen wäre. Und also offenbar ist es halt irgendwie so mehr opportun, irgendwie auf der Seite von strikter zu sein, zu irren als umgekehrt.
Was ja auch so ein bisschen vielleicht damit zusammenhängt, für welche Leute das halt vor allen Dingen gut ist, nämlich die, die halt versuchen wollen, möglichst viel da draußen zu halten.
Und wenn sie ein bisschen zu viel draußen halten, ist es besser, als was durchzulassen, was halt dann irgendwie knallt.
Zur Laufzeit. Das wäre dann voll snaggert. Ja, genau. Und ja, der hat das also lange erklärt in dem Buch und da habe ich das halt quasi her.
Und ja, ich finde das eigentlich sehr nett, weil damit kann man im Grunde genau das gleiche machen wie ein Typescript mit diesen Intersection und Union Types.
Was ja auch irgendwie so, das ist halt so sehr cool eigentlich, dass das geht. Und das geht halt, wenn man jetzt so mit Abstract Base Classes.
Das Ganze macht. Und nominal geht das halt nicht so richtig.
Das Problem ist, wenn du nominal arbeitest, du baust da auf dem Schlag eine Hierarchie auf, wenn die implizit ist durch die Basistypen, die du definierst.
Und das kannst du sehr, sehr schön und elegant umschiffen, indem du sagst, hey, alles, was ich erwarte, ist einfach nur, dass das Ding so ausschaut oder diese Werte und Eigenschaften hat, die ich an dieser Stelle erwarte.
Methodennamen, Rückgabe-Werte, Property-Typen etc.
Und ich sage mal, Programmiersprache wie JavaScript wäre gar nicht anders zu typisieren gewesen oder es wäre gar nicht möglich gewesen, die anders zu typisieren als mit einem strukturellen Typsystem, weil sonst praktisch kein Code mehr funktioniert hätte, den du irgendwie geschrieben hast.
Und das war eben auch so ein Designprinzip von Typescript, dass sie sagen, hey, wir wollen bestehenden JavaScript-Code unterstützen und mögliche Fehler herausfinden und nicht einfach nur aufgrund von Abitur geschaffenen Hierarchie-Konstruktionen.
Und das, was ich gesehen habe, nur durchs drüberfliegen, ist dieses New-Type-Konstrukt, wo du sagen kannst, hey, es gibt schon einen bestehenden Typen und der ist vielleicht sehr, sehr freigiebig, der ist vielleicht ein Integer-Jurist oder was auch immer, aber da kannst du ihm noch dieses eine Label verschaffen, damit du jetzt nicht irgendwelche unterschiedlichen Integer-Werte durcheinander kriegst oder irgendwelche Objektwerte, die ähnlich sind, durcheinander kriegst.
Weil strukturelle Typsysteme funktionieren halt immer bis zu dem Grad, wo du sagen musst, hey, aber dieses eine Ding will ich da jetzt nicht herinnen haben, weil ich erwarte doch etwas anderes und kann das durch das Typsystem nicht so ausdrücken.
Mach das New-Type, dann wird es explizit und dann kannst du genauso, hey, an dieser Stelle erwartest du etwas, was so ausschaut, aber es muss doch etwas anderes sein. Und das finde ich eigentlich ganz, ganz, ganz brauchbar.
Das ist, genau, das benutze ich, das benutze ich genau für diesen Use-Case, dass man halt oft quasi sowas, zum Beispiel Integer möchte man einschränken von der Range, aber das kann man im Typsystem nicht so richtig ausdrücken und ich finde, dann reicht oft schon, um den gleichen Effekt zu haben, im New-Type einzuführen, also wo ich das dann halt zum Beispiel,
aktuell brauche, ist halt, ich habe halt ein Jahr und ich weiß, dieses Jahr ist halt nur von 2025 bis 2040 oder irgendwie sowas und mir reicht im Grunde, ich mache das gar nicht über das Typsystem, dass diese Range dann sozusagen abgesichert wird, aber allein dadurch, dass ich sage, ich definiere den New-Type hier, der halt eigentlich ein Int ist, kann mir der Type-Checker sagen, wenn ich irgendwie mal ein anderes Int da reingesteckt habe, was ich nicht mal als Ja irgendwo anders deklariert habe,
und kriege dann sozusagen den Effekt, dass, wenn da irgendjemand irgendwas reinsteckt, was kein Ja ist, dann gibt es halt auch ein, sagt der Type-Checker halt schon, okay, nee, das sieht nicht gut aus und so kann man sich halt das sozusagen so ein bisschen, ja, herbeiemulieren, dass man irgendwie damit auch überprüft, ob das inhaltlich Sinn macht, ja.
Aber noch cooler wäre es natürlich, wenn du auch die Werte angeben könntest.
Entschuldigung, Stefan.
Hast du?
Also gerade mit dem, also in TypeScript kannst du das ein bisschen, du kannst teilweise Werte oder geringere Wertbereiche definieren, also in TypeScript ist es das Spannende, wie in jedem Typsystem, du hast Wertemengen und du definierst ja nur, ob dieser eine Wert, den du hast, jetzt in diese Menge passt oder nicht.
Das sind sehr große Mengen zum Teil, String, Number oder alle Objects.
Zum Teil sind sie halt auf deine Objekttypen heruntergebrochen, wo du sagst, diese Kombination.
Die Kombination an Properties, die gewisse Typen haben, erlaubst du dort oder nicht.
Du kannst aber auch sagen, hey, dieser einzige oder einzelne konkrete Wert, die Zahl 1, der String Stefan, was auch immer, kann auch als Typ gelten.
Das heißt, du kannst einen Wert haben, wo du sagst, alles, was diese Variable annehmen darf, ist der Wert 1.
Und das klingt am Anfang doof, weil was machst du mit einer Variable, die nur 1 sein kann?
Du kannst es schenken.
Aber du kannst noch diese Literaltypes oder Value-Types, wie du es benannt hast, Johannes, kombinieren mit anderen Value-Types und kannst dann zum Beispiel alle validen Augenzahlen eines Würfels darstellen.
1 oder 2 oder 3 oder 4 oder 5 oder 6.
Also damit hast du schon einen sehr engen Wertebereich definiert und weißt noch aus, wenn ein Wert da reinkommt, dann hat er garantiert eine.
Und das ist spannend. Das kannst du auch mit Strings machen.
Und was halt dann wirklich elegant ist, ist, dass du zum Beispiel Strings mit gewissen Pattern definieren kannst.
Du kannst sagen, hey, du erlaubst alle Strings, die mit ON anfangen, weil du gerade dein Eventsystem implementierst und du hast halt ON-Click, ON-Key-Down, ON-Key-Press, was auch immer.
Das heißt, es muss mit ON anfangen und nachher muss der erste Buchstabe unbedingt ein Großbuchstabe sein.
Solche Strings akzeptierst du, andere akzeptierst du nicht.
Da kannst du wirklich sehr elegant Wertebereiche definieren, mit denen du korrekte Werte angibst, mit denen du auch über deine Werte diverse Aussagen treffen kannst, die dir nachher helfen, die aber jetzt nicht so übermäßig komplex sind.
Dass du jetzt sagst, hey, das ist jetzt viel zu viel Aufwand, das zu definieren.
Mein Lieblingsbeispiel ist immer noch HTTP-Methoden.
Get, Post, Delete, was auch immer.
Oder HTTP-Error-Codes, da gibt es 70 um den Dreh.
Ich weiß jetzt nicht, ob es 201 gibt, ob es 217 noch gibt, weiß ich nicht.
Das kann mir dieser Union-Typ sehr, sehr schön sagen.
Und dann bin ich mir sicher, dass ich den richtigen HTTP-Status-Code meiner Response schicke und brauche nicht großartig überlegen, ob ich noch im richtigen Wertebereich bin oder nicht.
Ich muss mich einmal korrigieren. Ich habe eben nachgelesen, Value-Types ist nicht das richtige Wort.
Das bezieht sich auch auf etwas anderes. Das wird als Abgrenzung zum Reference-Type verwendet.
Also ob man einen Wert oder eine Referenz hat. Aber das Konzept, das hast du gerade sehr schön erklärt. Vielen Dank, Steffen.
Das ist auch in Kapitel 4 beschrieben von meinem Buch und da bist du ja noch nicht.
Das haben wir vorhin schon festgestellt, ich habe noch nicht weit genug gelesen.
Aber ich habe es jetzt wieder rausgeguckt.
Ich habe es wieder rausgeholt und jetzt lege ich es mir unter das Kopfkissen und werde das per Diffusion aufnehmen.
Gut, das ist so wie die Matura geschafft mit dem Mathematikbuch unter dem Kopfkissen. Funktioniert.
Sehr gut.
Ich finde, wir müssen noch ein bisschen darüber reden, wie man das so in Python dann machen kann noch und wie man das Typing-Modul vielleicht noch so ein bisschen benutzt.
Wir hatten jetzt ein New-Type, was irgendwie ganz cool ist. Wir hatten die Generics.
Ja, so Generics. Achso, genau.
Für meinen Typ gibt es gar keine Generics.
Da dachte ich mir so, oh mein Gott, wenn hoffentlich fragt das keiner, deswegen frage ich das jetzt mal.
Kann mir einer vielleicht erklären, was der Unterschied zwischen Co-Variant, Kontra-Variant und In-Variant und so ist?
Ja, genau.
Weil das kann man nämlich auch mit angeben und ich dachte mir so, okay, ich kann es ja angeben, aber oh Gott, was bedeutet das eigentlich?
Und was macht dann Bound und so?
Ja.
Ich kann es aus einer Typ-Theorie sagen, was Co- und Kontra-Variant ist.
Ich weiß aber nicht, ob das so auf Python auch zutrifft.
Aber ich versuche es jetzt mal so zu erklären.
Co-Variant.
Co-Variant ist, wenn du...
Schnell die Petitfragen.
Nein, es ist irrsinnig schwierig zu erklären.
Lass mich kurz mein zweites Buch aufmachen, weil da wäre Zeichnung.
Also bevor ich jetzt allgemeine Beschreibungen erkläre, sage ich es lieber mal so.
In einer Co-Varianten-Beziehung hast du zum Beispiel einen Typ, der ist String oder Number.
Was bedeutet, dass wenn du einen Wert hast, der Number ist, dann kannst du den auf jeden Fall auf diesen einen Typ
zuweisen, der String oder Number sein kann.
Das heißt, je enger dieser Wertebereich wird, hat kein Effekt drauf, kannst du weiterhin darauf zuweisen.
Kontra-Variant ist genau umgekehrt.
Du kannst zum Beispiel jetzt nicht eine Funktion, die als ersten Parameter String oder Number
oder einen Funktionstypen, der als ersten Parameter String oder Number erwartet, kannst
jetzt nicht eine echte Funktion zuweisen, die nur String erwartet, weil ja der Fall, dass auch eine Number
als Parameter sein kann, nicht dadurch abgedeckt wird.
Das heißt, du hast der Typ zwar auch ein Subtyp, der Parametertyp ist ein Subtyp vom anderen,
aber nachdem der in einer Funktion steht, sind die nicht zueinander kompatibel, sondern nur umgekehrt.
Das heißt, du kannst eine Funktion, einen Funktionstypen definieren, der als ersten Parameter
eine Number angibt, was bedeutet, dass du auch Funktionen zuweisen kannst, die String oder Number
erwarten, weil eben dieser eine Fall abgedeckt ist.
Und das ist der Unterschied zwischen Co-Variant und Kontra-Variant.
Brauchst du eigentlich nie, macht du dir irgendwelche komischen Fehlermeldungen, meinst du,
irgendwelche Sachen zuweisen, willst du die dann nicht so funktionieren.
Ist aber, glaube ich, in der Typtheorie die korrekte Beschreibung.
Ist nicht kompliziert, ich möchte euch da für die Shownotes eine Grafik zur Verfügung stellen,
die das wunderschön erklärt.
Und da suche ich mir den Link jetzt wirklich raus aus meinem Buch, weil das habe ich genau
aus dem Grund habe ich es da reingetan.
Weil das sind die Sachen, die merke ich mir selber nie genau.
Das ist was man immer nachlesen muss.
Also in welche Richtung kann man was irgendwie doch voneinander erben, wenn ich das richtig verstehe?
Und das allgemeiner annotieren?
Also generisieren. Also ich habe es jetzt hier auch gerade, wenn jemand anders spricht, kann ich ja googeln.
Also so wie ich das jetzt hier verstehe, das bezieht sich direkt auf Python hier und Qualitätsquelle Stack Overflow kann man ja auch verlegen.
Es wird hier so erklärt, du hast zwei Klassen, Basisklasse und eine abgeleitete Klasse.
Die derived.
Genau. B und D. Also Basisklasse und abgeleitete Klasse.
Und du hast irgendeine generischen Typenliste mit.
Irgendeinem Typen drin.
Und jetzt ist die Frage, wenn du eine Liste hast, die den Typen B hat, also den Basistypen, kannst du die dann da verwenden, wo du eine Liste vom Typen D erwartest?
Also eine abgeleitete Klasse.
Oder ist es andersrum?
Das wäre dann Co-Variant oder Kontra-Variant.
Genau. Und das eine ist Co-Variant und das andere ist Kontra-Variant.
Weil du eben sagst, okay, wenn du den als Generic verwendest, dann geht die Beziehung in die eine Richtung oder die Beziehung geht in die andere Richtung.
Und das ist natürlich schön, dass man da zwei Worte genommen hat, die exakt gleich klingen.
Also Co-Variant ist, wenn man quasi annotiert mit der Implementierung und Kontra-Variant ist, wenn man mit der Basis annotiert.
Und In-Variant wäre dann, wenn man nicht beides verwenden darf, weil der sagt halt nö, das ist nicht genau das, was ich erwarte.
Ja, irgendwie so. Für genauere Sachen muss man PEP484 lesen. Das haben wir ja sicherlich alle schon gemacht.
Da muss man gar nicht genauer drauf eingehen.
Ich packe die Grafik in die Show Notes und dann schauen wir mal.
Genau.
Schauen wir mal, ob das hilft.
Ja, aber genau. Ich dachte auch so, man kann das ja angeben und dann dachte ich so, ich habe das noch nie verwendet.
Ist das irgendwie, habe ich was, passe ich was oder?
Und was macht dann Bound?
Also weil das macht man ja irgendwie auch bei den Kontra-Variants oder ist das schon das?
Ich weiß nicht.
Steht zumindest in der Types, Picing, Typing.
Tatsächlich ist in dem PEP484 eine sehr schöne Erklärung drin.
Mit Employees und Managers.
Wenn du eine Liste, wenn du eine Funktion hast, die eine Liste von Employee nimmst, solltest du da, kannst du da eine Liste von Managers reingeben.
Und für manche Funktionen kann das ja sein, wenn du halt sagst, okay, wir müssen Gehalt auszahlen, das müssen Angestellte kriegen und wenn die Angestellten Manager sind, dann ist es halt so.
Kann aber auch Nein sein, dass du zum Beispiel, keine Ahnung, alle die Angestellten aufsetzt, die, genau, zum Beispiel, dass du die, nee, aber dass du zum Beispiel jemanden hinzufügst zu dieser Liste.
Und wenn du sagst, okay, die Funktion nimmt eine Liste, die die Angestellte enthält, dann kannst du in diese Liste auch einen Angestellten reintun.
Wenn du aber eine Liste von Managern reingegeben hast, dann geht das nicht.
Und das ist jetzt eben genau so eine Frage.
Wo du beide Optionen haben kannst, also so eine Situation, wo du beide haben kannst und das tatsächlich auch eigentlich beantworten können musst, ob du da eine abgeleitete oder eine Basisklasse reingeben darfst.
Das ist eine Co-Variante, eine Contra-Variante. Und der Bound ist dann quasi tatsächlich die, wenn dann erst die Manager gefallen, weil das die spezialisiertere Variante ist.
Ja, wir haben noch mehr von dem Piping-Modul, damit wir uns noch mehr schöne Sachen dazu erzählen können und mehr ergänzen können.
Mit präzisem Fachwissen. Und zwar den Type-Alias und die Type-Var, die da noch irgendwie dazu können.
Also was ist denn der Unterschied? Und man kann ja auch noch das schöne Keyword Type dazu schreiben und sowas.
Na, ich meine, bei Type-Alias, das kannst du auch einfach so hinschreiben. Das ist nur eine explizitere Darstellung.
Und manchmal ist es halt problematisch, wenn du zum Beispiel einfach einen String verwendest, den du ja auch quasi benutzen kannst, statt, und manchmal muss man das ja auch, um zyklische Imports zu vermeiden und so.
Und dann ist halt unklar, was gemeint ist, wenn man den nicht Type-Alias davor schreibt.
Also beispielsweise, wenn ich jetzt...
Wenn ich jetzt irgendwie eine Union habe, das kann jetzt mehrere Sachen sein, dann kann ich dann Type-Alias dafür verwenden, dass das damit gemeinsam einen Namen gibt.
Ja, aber du könntest ja auch einfach hinschreiben, myUnion gleich und dann irgendwie der Typ, Pipe-Symbol, der andere.
Das ist dann ein Type-Alias schon. Das heißt, das selber kann ich annotieren mit... Nein, ist es nicht?
Ja, also du könntest damit dann wieder annotieren. Aber wenn du jetzt zum Beispiel da Strings verwenden wollen würdest für die Typen, dann geht es halt nicht mehr so richtig.
Ähm, macht... Also Type-Alias macht das dann halt explizit, dass das halt sozusagen...
Der Alias, das ist okay.
Ja, ähm...
Und die Type war, das hatten wir eben, TVK, TKV, was, warum TKV jetzt da im besonderen Sinne, weil ich diese kurzen Dinge, also einer der Gründe, warum ich Go nicht leiten kann, sind diese Ein-Charakter-Variablen-Namen, aber, ähm, ja.
Ja, das sind halt die Generics, oder, die wir vorhin hatten.
Ja, genau, genau.
Stefan, du hast angesetzt. Erklär uns, was Type war.
Also, ja, ja, also ich hab jetzt ganz kurz diesen PEP-4-4 aufgemacht und der ist, der ist wunderschön.
Also, also, äh...
Der ist super.
Eine Type-Variable, also ich sag immer, ich sag immer Type-Parameter dazu, aber das ist im Grunde genau das Gleiche, das ist eben diese, ähm, äh, ein Typ, der später durch einen konkreten Typ ersetzt wird.
Das heißt, du kannst jetzt sagen, hey, du hast diese, diese Typ-Variable in diesem Beispiel vom PEP-4-4 ist das ST, Seist-Type wird das heißen dort, weil da geht's um Seist, ähm, wo du sagst, hey, du machst jetzt eine Funktion, die erlaubt, ähm, x-beliebige Typen, allerdings kannst du sie, ähm, durch irgendeinen Bound,
das ist das Zweite dort, ähm, der zweite Parameter, ähm, einschränken.
Also, ein Bound sagt dir, ähm, also, oder der generelle Typ-Parameter sagt dir, alle möglichen Werte, aber später nur ein konkreter.
Ähm, und der Bound sagt dir, alle möglichen Werte, die auch diese Eigenschaften erfüllen, später ein konkreter.
Und das ist dort in diesem Beispiel recht gut, weil da wird Seist als Bound definiert, was bedeutet, dass du, ähm, ähm, äh, Länge, äh,
äh, definieren können musst oder Länge lesen können, können musst, ähm, in Python hast du nur diese, diese Hilfsfunktion, du hast ja selten Methoden auf, auf Klassen, soweit ich das, das weiß, ähm, deswegen brauchst du halt überall diese, diese Bounds, ähm, andererseits könntest du ja sagen, du hast irgendeine, irgendeine Subklasse oder so.
Oder Protokoll.
Aber, aber Bound ist auch etwas, das, das kennen wir in anderen Programmiersprachen auch, ähm, in TypeScript wird das als Konstrant bezeichnet, aber im Grund geht's darum, dass du einfach vor,
vor definierst, du hast ein paar Eigenschaften, die du sicherstellen willst, in dem Fall, was dort in meinem PEP484 ist, mit diesem Upper Bound, sagst du einfach, du willst die Möglichkeit haben, eine Länge zu berechnen, du willst einfach wissen, hey, da gibt's eine Seist, das hat, hat eine gewisse Längegröße, was auch immer.
Ähm, spannend wäre so, ob ich dort einen String reingeben kann, weil man kann ja die Länge von einem String definieren.
Ja, kannst du.
Ähm, genau.
Alles, alles, was Len von irgendwas hat, ist Seist.
Also, also, String muss auch eine Länge rauskriegen, ne?
Ja.
Und, äh, das ist auch sehr interessant hier, weil, also diese, diese Type Variable, die gerade in diesem Beispiel, ich hab's zufällig auch gerade offen, äh, benutzt wird, die wird in dieser Funktion, da wird eine Funktion definiert, die heißt Longer, und die nimmt zwei, äh, Variablen, X und Y, und die sind beide vom Typ ST, und der Rückgabewert ist ebenfalls ST.
Mhm.
Und das ist eine sehr interessante Sache, weil das eben bedeutet, du kannst hier zwei Sachen reingeben, die vom, von der gleichen Sorte sind, und kriegst wieder eins raus, was wieder von der gleichen Sorte ist.
Mhm, mhm.
Aber wir sagen gar nicht genau, was das für eine Sorte ist, sondern wir sagen nur, das muss als Anforderung haben, das Minimum, was es erfüllen muss, ja, das ist der Bound, ich muss davon die Länge abrufen können.
Mhm.
Und, ähm, und das hat diese Funktion schon sehr genau spezifiziert, ja, die Spezifikation dieser Funktion, also Longer XY ist ja erstmal sehr lose, und jetzt durch diese Typ-Variablen und durch den Bound ist es doch relativ genau spezifiziert, und auch sehr exakt, würde ich sagen.
Was ich sehr spannend finde an dem Beispiel, und das ist wahrscheinlich jetzt so ein Python-Eigenwort, aber im ersten Aufruf wird dort dieser generische Typ-Ramit oder diese Type-Var ersetzt durch eine List.
Das ist, glaube ich, die eckigen Klammern, nicht? Im zweiten Aufruf, da hast du geschwungene Klammern, wird es durch ein Set ersetzt, also der Typ wird durch ein Set ersetzt.
Aber im dritten, da ist im ersten Aufruf eckige Klammern, im zweiten Parameter sind geschwungene Klammern, da wird der Parent-Type davon eine Collection verwendet, wo du sagst, hey, okay, ist eine List, ist eine Set, also es könnte beides sein, du nimmst einfach was, was beide beschreibt.
Finde ich cool, dass das das Typ-System so macht, normalerweise würde TypeScript dir da vielleicht einen Fehler werfen.
TypeScript würde da sagen, hey, ähm.
Wenn du das einmal durch einen Typ ersetzt, dann musst du auch im zweiten Parameter den gleichen Typ verwenden und so findest du aber in der Hierarchie tatsächlich einen Parent-Type, den du nutzen kannst.
Das ist ziemlich geil.
Also, richtig cool.
Ja, das ist ziemlich schön.
Also, nämlich auch so, dass ich das jetzt verwenden möchte, muss ich ganz ehrlich sagen.
Ich glaube, ich habe das so weit.
Sehr gut.
Viele Programmsprachen-Features kommen ja durch Knight umgesetzt.
Das ist bei Python und auch nicht anders.
Viele Sachen sind einfach aus Knight durch andere Sprachen entstanden.
Also, den größten Knight habe ich ja durch die Input-Signatur.
Das macht einfach so viel einfacher.
In JavaScript ist es umgekehrt.
Du importierst zuerst die Einzelelemente aus dem Paket und das ist reine Ästhetik.
So ist es viel klarer.
Du spezifisierst zuerst das Paket und dann importierst du die Sachen draus.
Jeder Editor freut sich, wenn er das so kriegen kann.
Mhm.
Ja.
Was mich noch interessieren würde hier an der Stelle ist diese Overloads, die da mit drin stehen.
Das ist ja auch so eine Sache, die man nur bei den Type Annotations findet oder auch woanders,
wo halt derselbe Methodenname mehrfach hintereinander definiert wird.
Was macht denn das genau?
Schreibst du in Python dort dann beide Methoden aus?
Also, implementierst du da beide Methoden?
Ah, in Python musst du dich da anstrengen dafür.
Oder sind das nur die Signaturen?
In Python musst du dich da anstrengen dafür.
Das hat ja nicht mal viel mit.
Ja, es hat vielleicht schon was mit Types zu tun, aber, also so richtiges Overloading gibt es ja gar nicht in Python.
Du fügst eine weitere Signatur hinzu zur Methode.
Ja, genau.
Was macht denn das?
Warum macht man denn das?
Aber du hast doch nicht zwei Funktionen, du hast zwei Signaturen.
Du hast eine Funktion, die, also es ist so, der ganz grundlegende Prozess ist, dass eine Funktion in Python eine Variable ist,
die halt ein Funktionsobjekt enthält.
Und diese Variable hat einen Namen und diesen Namen, der ist eindeutig, den kannst du nur einmal geben.
Und zu diesem Namen kannst du auch nur diese eine Funktion geben.
Was du jetzt aber machst, um Überladung zu machen, und das, ich erkläre es gleich noch für die Zuhörer, ja,
ist, dass du sagst, du definierst eine Basismethode und der fügst du dann eine weitere Signatur hinzu.
Und wenn diese, da ist dann eben so ein, durch Dekoratoren hast du so einen Mechanismus, der diese Signaturen entsprechend überprüft.
So, was ist Überladung überhaupt und was erreicht man damit?
Überladung ist, wenn du eine Funktion, also ganz klassisch aus dem Java-Umfeld, ja, wenn du eine Funktion hast, die heißt add und die definierst du für float und float und dann kommt hinterher wieder ein float raus.
Dann funktioniert die ganz einwandfrei für floats, aber du kannst damit nicht int adden, ja, kannst keine Integer addieren, weil der Compiler dir sagt, ja, ich hab so, ich hab die Funktion gefunden, aber die geht nur für floats.
Was du jetzt in Java machst, ist, du schreibst eine zweite Funktion, die auch add heißt, aber die eine andere Signatur hat.
Und die wird durch das Kompilat eher zu einer anderen Funktion.
Das heißt, zum Zeitpunkt des Kompilierens kann der Compiler sagen, ah, du meintest diese Funktion mit der Signatur oder du meintest diese Funktion mit der Signatur.
Und das geht auch in TypeScript, wenn ich mich recht erinnere, dass du überladene Methoden hast, die dann eben durch den Compiler zum Zeitpunkt des Kompilens die richtige Zuweisung bekommen.
Die sind dann im JavaScript-Kompilat heißen die dann unterschiedlich, weil die eben da unterschiedlich sind.
Das ist genau das.
Das ist genau der Unterschied.
Heißt nicht unterschiedlich.
Nein, ein Overload in TypeScript ist im Grund nur eine andere Funktionssignatur auf Basis, über der tatsächlichen.
Du musst mindestens zwei angeben, eine, die dem Typsystem mitgeteilt wird als Nutzungssignatur und eine, die du verwenden kannst, um tatsächlich die Funktion zu implementieren.
Und dann kannst du so viele Overload schreiben, wie du willst und du hast dann einfach unterschiedliche Aufrufe.
Durch die du durchgehen kannst.
Aber im Endeffekt wird nur eine Methode aufgerufen oder eine Funktion aufgerufen.
Deswegen habe ich genauso nachgefragt, weil ich bin mir nicht sicher, wie das jetzt in Python funktioniert, weil es ist spannend.
Es ist so Führungslieder.
Okay.
Genau.
In Python ist es ein bisschen anders.
In Python musst du das eben über Dekoratoren machen, weil du diesen Namen nicht mehrfach haben darfst.
Und was da im Wesentlichen passiert ist, du sagst, wenn die Methode aufgerufen wird mit zwei.
Mit zwei Variablen oder mit Argumenten, die dieser Signatur entsprechen, dann rufe bitte diese Subsignatur auf.
Also du fügst da quasi eine weitere Funktion hinzu, die nur in bestimmten Fällen aufgerufen wird.
Aber das ist was, was zur Laufzeit passiert.
Also zur Laufzeit wird dann entschieden, welche Submethode aufgerufen wird.
Das ist im Wesentlichen ein Match Case, der da vorsteht.
Nein, aber ich weiß nicht, ob das im Typing-Zusammenhang nicht was anderes ist.
Es gibt diese Overload-Geschichten vielleicht in Klassen, aber ich meine hier auch bei Funktionen.
Wenn ich jetzt einfach das sozusagen für diese Typ-Annotation verwenden will, dann ist es, soweit ich sehen kann, auch so.
Es gibt die Funktion einmal, aber ich kann halt viele sozusagen mit Overload dekorierten Funktionen haben, die keine Implementation haben.
Aber wo ich sozusagen nur quasi die unterschiedlichen möglichen Arten, wie das aufgerufen werden kann,
anottiere, weil ich das nicht in eine Annotation schreiben kann, weil geht halt nicht.
Und ja.
Ach so, okay.
Da gibt es zwei, also es gibt da, dann habe ich, also es gibt zwei verschiedene Dinge.
Es gibt mehrere Overload-Dinge, denke ich.
Genau, also das, das, das ist blöd, ja.
Es ist blöd, dass es die, dass diese Sachen in unterschiedlichen Sprachen unterschiedliche Dinge bedeuten.
Und es ist, also wenn ich der Kaiser wäre, dann wäre das anders.
Ja.
Dieser, also das ist tatsächlich was, was ich vermisst habe, ja, an Python.
Als ich zu Python gekommen bin, aus Java.
In Java kannst du sagen, ich habe hier eine Funktion, die heißt Add und die nimmt Integer und ich habe eine Funktion Add, die nimmt Strings und ich habe eine Funktion Add, die nimmt, was weiß ich, ja, Listen.
Und die machen sehr unterschiedliche Dinge, aber im Endeffekt das gleiche Semantische, die fügen die aneinander.
Und die heißen gleich, weil die das gleiche machen.
Und sowas gibt es in Python nicht, weil in Python musst du jedes Mal eine neue, neuen Namen haben dafür oder einen neuen Scope oder so.
Single Dispatch.
Ja, das hat, ja.
Also weißt du, warum das in Java überhaupt möglich ist, dass du den gleichen Namen mehrmals vergeben kannst?
Ja, weil die beim Kompilieren die Namen, weil die Namen weg sind beim Kompilieren.
Nein, nein, weil die, weil die Funktionssignatur und Typen haben.
Weil die Funktionssignatur und Typen haben, weil Java die Typen, Typen, also erfordert und dadurch ist einfach genug Unterschied da, um das zu identifizieren können.
Vertraue und glaube, es hilft, es heilt die göttliche Kraft!
Genau, es sind unterschiedliche Funktionen.
Also die Funktion addIntInt ist eine andere Funktion
als die Funktion addFloatFloat.
Und darum ist natürlich in JavaScript und Python
halt komplett anders, weil im Endeffekt hast du halt nur
eine Funktion mit ein paar Parametern.
Und die Typen sind ja, wie wir mitbekommen haben,
eigentlich wurscht.
Die sind dann weg.
Ja, zur Laufzeit auf jeden Fall.
Aber diesen Mechanismus, den kriegst du hin,
der heißt Single Dispatch.
From Functools import Single Dispatch.
PEP 443.
Das, was ihr beschrieben habt, das heißt Overload,
das ist ja was ganz anderes.
Python Lang Util.
Okay.
Ich habe es jetzt hier aus From Typing.
Also so wie ich das gesehen habe,
es gibt auch ein sehr schönes Beispiel dafür.
Das ist auch aus dem Fluent Python Buch.
Habe ich das da.
Also das ist halt so ein Beispiel für Methoden,
die halt eigentlich in, also die hat auch so ein Problem,
dass man mit Type Annotation
halt so hat, beschreiben, nämlich,
dass man, dass halt die Type Annotation nicht so,
also die ist ja auch eine Sprache,
ist halt eine andere Art, das hinzuschreiben.
Und die ist halt nicht so expressiv wie Python selber.
Das heißt, wenn ich jetzt Funktionen habe,
wie zum Beispiel Min und Max,
jetzt weiß ich gar nicht,
ob es das in der JavaScript-Welt auch so gibt,
aber die kann ich halt sehr schön in Python hinschreiben.
So in irgendwie so 20 Zeilen oder sowas.
Und ist sehr schön zu lesen, ist nicht kompliziert.
Die Type Annotation dafür ist aber sehr, sehr schwer,
weil das halt so super generisch ist.
Und dann kann man noch ein Callback übergeben,
das halt irgendwie zum Sortieren verwendet wird und sowas.
Und ja, die korrekten Type Annotationen für Min und Max
sind halt sehr viel länger als die Implementation.
Und das ist halt, liegt halt daran,
dass man das in dieser neuen Annotationssprache
halt nicht so gut hinschreiben kann.
Und dafür, also da hast du dann halt so irgendwie,
ich weiß nicht, zig Overloads, weil,
du kannst das sowieso immer nur quasi für einen Teil der,
wie man das aufrufen kann, halt annotieren.
Und dann musst du das halt,
musst halt zehn Dinger übereinander häufen,
um das halt irgendwie abgebildet zu kriegen.
Und ja, und auch in diesen Dingern sind dann halt,
waren halt lange Fehler drin.
Und die sind halt auch echt schwer zu finden.
Also ja.
Und die machen ja auch überhaupt gar nichts,
diese Fehler, jetzt mal ganz ehrlich.
Ja gut, na gut.
Also irgendjemand hat dann halt,
es kann ja sein, du rufst das halt auf,
lässt das auf eine bestimmte Art
und dann läuft dein Type-Checker drüber
und der sagt, der spuckt dir dann halt irgendeine Fehlermeldung aus,
die vollkommen unverständlich ist.
Und das verdirbt dir halt den Vormittag oder so,
weil du verstehst gar nicht, wo das Problem ist.
Und dann war es halt nicht mal wirklich ein Problem,
sondern es ist einfach nur etwas,
was halt in den Annotationen kaputt war.
Und du hast es korrekt aufgerufen,
das ist ja schon ärgerlich.
Also ich meine, ja, also, ja, gut.
Zugegeben, ja.
Aber.
Aber was war jetzt die Lösung, Jochen?
Weil ich habe dich jetzt unterbrochen,
aber gibt es da jetzt eine Lösung dafür?
Kann ich jetzt da Overload sagen?
Genau, und du kannst jetzt sozusagen,
wenn du eine Funktion annotieren möchtest,
aber das halt die Annotation nicht einfach so hinschreiben kannst,
dann kannst du die möglichen Arten,
wie das halt, also wenn es halt mehrere,
wenn du mehrere Annotationen hinschreiben musst
für die Funktion, kannst du das per Overload hinschreiben.
Hast dann halt der Methoden-Body
oder Funktions-Body ist dann so,
sondern ist einfach Ellipsis, also Punkt, Punkt, Punkt.
Und genau, dann kannst du halt alle Arten,
wie man das Ding halt getypt aufrufen kann,
halt hinschreiben.
So richtig schön sieht das aber auch nicht aus.
Nee, das sieht nicht schön aus.
Ich würde es zum Beispiel in TypeScript
auch nicht immer verwenden.
Meistens ist das ein Codespell,
wenn du Methoden oder Funktionen hast,
die mehr können soll.
Was sie beschreibt, ja.
Ja, so ein bisschen so eine Krücke, ne?
Warum machen wir das dann nicht?
Vielleicht, wenn man so ein Public-API-Interface hat,
was unbedingt benutzt bleiben muss,
aus irgendwelchen Gründen.
Vielleicht für Legacy oder sowas.
Ja, also in TypeScript gibt es es,
weil du halt in TypeScript
Parameter weglassen kannst
oder
Parameter in unterschiedlichen Positionen
auch was anderes heißen.
Und da hat halt TypeScript irgendeine Methode braucht,
um das darzustellen.
Und deswegen haben wir es.
Ja, gut, wenn man Sternchen, Komma, Quark schreibt oder sowas, ja.
Also gerade dieses Add-Beispiel von Johannes,
das würde ich eigentlich jetzt in TypeScript
mit Generics umsetzen, oder?
Ja, gut.
Wenn du jetzt die Methode Add für Integer
und eine Methode Add für Stringen hast,
musst du ja schon zwei unterschiedliche Implementierungen haben auch.
Also an irgendeiner Stelle musst du ja deine Implementierung verzweigen.
Ja, wenn du Plus verwendest, okay.
Aber dann musst du das Plus irgendwo hin verzweigen,
weil das macht ja sehr unterschiedliche Dinge.
Ja.
Dann kann ich das dann netto überschreiben.
Ja, ja.
Ja.
Was mir noch fehlt,
wir haben schon relativ viel gesagt,
ich glaube, es ist eben schon einmal gefallen,
sowas wie rekursive Types oder sowas.
Wenn ich zum Beispiel einen JSON-Type definiere.
Oh, sag doch nicht sowas.
Oh Gott.
Kapitel 7.
Du bist ja noch weit entfernt von Kapitel 7.
Ja, also rekursive Types ist schon nötig.
Wenn du jetzt zum Beispiel irgendwie eine Liste definieren willst,
eine einfach verkettete Liste,
dann hast du dort nur einen Knoten
und du verweist auf den nächsten Knoten.
Aber das ist eigentlich nur ein Typsystem.
Du musst halt irgendwie die Möglichkeit haben,
dass du Typen definieren kannst,
die,
die sich selbst referenzieren können,
wenn nötig.
Also wie JSON-Objekte oder sowas zum Beispiel tatsächlich.
Zum Beispiel, ja, genau.
Also in JSON kannst du ja auch sehen,
dass du,
naja,
schwierig.
Ja, eine Liste von Objekten habe,
in denen andere Listen stecken,
die wieder irgendwie Teugs haben oder so.
Ja, genau.
Oder in Array von Arrays.
Ja.
Wenn du schon dabei bist, ne.
Aber ich habe eben einmal mit,
mit jemandem vom TypeScript-Team gesprochen,
bezüglich rekursiven Typen.
Ähm,
das ist meistens ein Implementierungsdatei,
wie tief der Compiler dort denn gehen kann.
Also was, was sind die,
ähm,
wie,
wie ist der Compiler entwickelt,
äh,
dass er bald genug sagen kann,
hey, da stoppe ich jetzt und passe nicht mehr weiter.
Also wie, wie geht der Compiler mit der Rekursion?
Ja, es gibt irgendwie so ein,
Typ-Systeme können das eigentlich.
Ja, da auch in der IDE irgendwie,
ich weiß nicht,
bei JSON-Type oder sowas,
wie, bis wie viel Level tief darf der denn gucken,
ob das noch stimmt.
Genau, genau.
Und da werden es auch immer besser.
Das ist der Mathematik-Egal.
Der TypeScript-Compiler ist schon recht,
das ist der Mathematik-Egal,
die guckt, ähm.
Ja, genau.
Ja, das kommt dann wieder darauf an,
welche Art von Mathematik, ne,
wenn man den konstruktiven Zweig anhängt.
Das gibt es gar nicht.
JSON-Dastro-Typik.
Wenn du schon in der Grundvorlesung,
also ich habe ja eine mathematische Ausbildung genossen
an der Universität,
da werden dann die natürlichen Zahlen nochmal definiert
und die werden rekursiert.
Ja, ja.
Da gibt es eigentlich nur eine natürliche Zahl,
das ist die 0 oder die 1,
je nachdem, wo du anfangen willst.
Und dann sagt man einfach,
jede Zahl hat einen Nachfolger
und zack, hast du alle natürlichen Zahlen beisammen.
Also es geht schon weit rein mit der Rekursion
und die geht auch weit genug in der Mathematik.
Ja.
Ja, ja, ja.
Ja, ich weiß nicht, haben wir noch,
ah, oh, was mir noch einfällt, genau.
Das schließt so ein bisschen an das Pedantik-Thema von eben an.
Ich meine, das ist ja jetzt etwas,
was man, also die Typen werden ja zur Laufzeit ignoriert in Python,
aber nicht immer und man müsste es auch nicht,
weil im Grunde kann man rausfinden,
wie die Annotationen sind.
Und es gibt halt auch Software, die das macht,
wie zum Beispiel Pedantik.
Ja, oder MyPy.
Oder FastAPI.
Ja, genau.
Gibt es sowas eigentlich in TypeScript auch?
Weil ich meine, gut, das wird ja kompiliert zu JavaScript,
aber es gibt ja jetzt auch,
glaube ich, Interpreter, die direkt TypeScript interpretieren,
so Deno oder sowas, macht das, glaube ich,
ich weiß nicht so genau,
könnte das ja im Grunde dann tun.
Das ist ja ein weitverbreiteter,
ich glaube, dass Deno direkt TypeScript interpretiert.
Ach so.
Deno hat nur einen TypeScript-Compiler inkludiert,
also kompiliert TypeScript, bevor er das JavaScript ausführt.
Also tatsächlich,
ich sage mal, im JavaScript-Bereich reden wir eher
von unterschiedlichen Typsystemen, die existieren.
Wie Flow-Type ist zum Beispiel eins,
das sehr, sehr optisch sehr, sehr ähnlich ist zu dem,
was TypeScript zur Verfügung stellt,
aber halt in den Nansen unterschiedlich ist.
Oder eben TypeScript,
und das sind auch schon die populärsten.
Der Clojure-Compiler hat einmal ähnlich funktioniert,
also wie Typ definiert werden.
CoffeeScript gab es früher.
Ja, CoffeeScript ist aber sogar eigene Programmiersprache.
Also wie Typ definiert werden.
Ja, aber kompiliert, also ich meine, ja.
Gleiche Historie.
Ähnlich, ähnlich.
Da gibt es auch einen wichtigen Punkt,
weil wie Typen definiert werden in JavaScript,
das ist ja eigentlich nicht dem TypeScript-Team zu verdanken,
sondern dem ECMAScript-4-Standard,
der schon viel, viel älter ist, der nie umgesetzt wurde,
an den sich aber alle Typsysteme jetzt irgendwie dranhalten
bei der Definition des eigenen Typsystems.
Ich würde eher sogar sagen, dass ActionScript,
also die Flash-Programmiersprache noch eher ähnlicher
oder verwandter mit TypeScript und Flow-Type ist.
Aber das ist es dann auch.
Also du hast entweder unterschiedliche Typsysteme,
dann entscheidest du dich in den meisten Fällen
heutzutage eh für TypeScript
und dann bietet dir TypeScript eigentlich alles,
was du dort dann dafür brauchst.
Also du laufst da gar nicht in Gefahr,
dass du irgendwie ein anderes Werkzeug nimmst.
Und TypeScript versteht halt auch komplett JavaScript.
Das heißt, du kannst halt dort noch mit JavaScript-Code anfangen
und dich nur einmal auf die Typ-Inferenz
vom TypeScript-Type-Checker verlassen.
Dass er sagt,
ich weiß jetzt, welche Typen du verwendest,
rein in der Verwendung deines Codes.
Dass du jetzt so eine retroaktive Typ-Annotation machst,
macht man eher nicht.
Es gibt ein paar Werkzeuge,
ich könnte aber jetzt nicht den Namen dazu sagen,
macht man aber aus dem Grund nicht,
weil der TypeScript-Type-Checker eh gut genug ist,
dass er schon sehr, sehr viel herausfindet,
bevor du überhaupt irgendeine Annotation machen musst.
Was ein gutes Migrations-Sol ist,
ist JS-Doc,
das ist halt eine Typ-Annotation im Kommentar,
wo du einfach sagst,
hey, du hast diese Funktion,
die hat drei Parameter,
du definierst den Typen im Kommentar
und nicht im Code.
Und das machen viele Bibliotheken,
das machen sehr viele alte JavaScript-Bibliotheken,
wie zum Beispiel Lodash oder Underscore schon.
Und TypeScript kann mit dem umgehen.
Also TypeScript kann auch Typ-Informationen
aus diesen Kommentaren lesen
und hat halt so weit mehr Komplizität,
Komplizität mit dem gesamten Ökosystem,
als wenn sie darauf bestehen würden,
dass sie nur die Typen verwenden,
die du annotierst.
Okay, aber so ein richtiges Äquivalent zu dem,
was der Jochen gefragt oder gesagt hat,
gibt es nicht wirklich.
Zur Laufzeit hast du nicht wirklich mehr die Typ-Informationen.
Also zur Laufzeit,
es gibt Bibliotheken,
die fügen Typ-Informationen zur Laufzeit hinzu
und leiten dadurch TypeScript-Typen,
aber das ist es dann schon.
Also es gibt auch ein paar so Reflection-Geschichten.
Achso, okay, dass du so rumgehst.
Das ist aber alles Mumpitz.
Also das möchte ich nicht einmal erwähnen,
weil es einfach Schwachsinn ist.
Eine Sache, die aber gut ist, zum Beispiel,
das ist SOD.
Wenn du jetzt sagst, du brauchst jetzt
Typ-Informationen zur Laufzeit auch,
dann kannst du über die SOD-Bibliothek
dir deinen Typen in JavaScript definieren.
Das ist aber nicht TypeScript,
das ist JavaScript.
Und kannst dann, wenn du diesen Typen
weiter im TypeScript-Code
verwenden willst, sagen,
hey, leite mir jetzt aus diesem JavaScript-Konstrukt,
das ich gebaut habe,
das einen Typen darstellen soll,
leite mir von diesem JavaScript-Konstrukt
doch einen TypeScript-Typen ab,
den ich weiterverwende.
Und du bekommst dann zum einen einen Typen,
klassischer TypeScript-Typ,
den du in deinen Methodensignaturen
verwenden kannst,
den du annotieren kannst,
wo du Type-Checking hast,
das funktioniert, das ist grandios gut.
Parallel dazu hast du aber immer noch
dieses JavaScript-Konstrukt mit der Validierung
erfahren kannst. Das heißt, du kannst
sagen, hey, du kriegst jetzt ein Chasen von einem Backend,
steckst es in den Validator rein
und kriegst entweder Ergebnisse, die es nachher
dem Typen entspricht, super, oder
Fehlermeldungen, mit der du umgehen kannst.
Und das ist eine grandiose
Bibliothek.
Erstens ist sie
so nah an TypeScript, dass du wirklich sämtliche
Dinge, die du in TypeScript schreiben kannst,
auch damit umsetzen kannst.
Und zweitens ist sie schnell,
sie macht robustere Code, ich bin total glücklich
mit der, die kann ich sehr, sehr gut empfehlen.
Also unbedingt. Aber prinzipiell
gilt als Methoden. Okay, cool. Also ein ähnliches
Verfahren. Sehr ähnlich.
In SOD ist es aber so, dass du
halt die Typen dann
anfängst zu schreiben in dieser
JavaScript-Welt
mit den dort
vorhandenen Methoden und Funktionen.
Und das ist halt umgekehrt zu dem, was du
normalerweise in TypeScript auch hast, dass du sagst,
du schreibst deine Typen und die sind nach dem
Kompilator einfach weg. Also TypeScript ist so eine
Erased-to-JavaScript-Sprache,
was auch bedeutet, wenn du es nicht zur Laufzeit haben willst,
musst du in JavaScript anfangen und musst halt dem anderen
Weg gehen.
Ja, okay, aber da steht ja nicht, würde ja nicht
prinzipiell was dagegen sprechen, oder?
Dass du von TypeScript-Typen zu
JavaScript-Typen gehst. Das ist nur jetzt halt
das Tooling, das existiert nicht.
Ja, okay, aber das ist doch cool. Also du hast auf jeden Fall
das Gleiche. Unbedingt zu empfehlen.
Ja, unbedingt zu empfehlen.
Also das ist richtig, richtig cool. Finde ich super spannend.
Könnte auch für mich nützlich sein.
Also gerade wenn du mit
Backends arbeiten musst, denen du nicht trauen kannst,
herrlich. Ja, oder
mit Inputs von Benutzern
und ich meine, da musst du eh immer Validierung
machen, aber das
kann einem ja in dem Sinne
die Arbeit ein bisschen abnehmen.
Naja, also man definiert halt, wie man gerne
hätte, dass die eigene Datenstruktur
aussieht und benutzt dann diese Information
halt auch zur Validierung dagegen.
Das ist natürlich, also ja,
das ist genau eigentlich der Use Case von
Pydentic auch.
Ja, ja. Muss man das Pydentic noch genau
anschauen. Also das hört zum
Zeitmehr, ob es heute gehört,
wie wir begonnen haben.
Und jetzt wieder. Erklär mal,
Jochen, erzähl mal den Unterschied zwischen FastAPI
und Pydentic. Ach, ja,
FastAPI ist sozusagen ein
Repetiface, was
Pydentic, ja, genau.
Automatisierung bereitstellen. Pydentic
ist eine Bibliothek, die benutzt wird von
FastAPI und
die halt sozusagen
ermöglicht, wenn man halt
mit Typannotationen sozusagen oder
der Syntax, es gibt noch mehr, weil man kann halt auch noch
mehr machen als nur die Sachen, die mit
Annotationen möglich sind. Man kann halt auch Validations
Validierungsfunktionen
haben und Upper Limits
und Lower Limits und weiß ich nicht
und ganz viel kompliziertes Zeugs halt auch mit dazu
schreiben. Das geht mit den
Typannotationen natürlich nicht, aber wenn man einfach nur
die Typannotationen hinschreibt, dann passiert
das halt auch, dass dann sozusagen
man einen JSON nehmen kann und
man hat halt eine Objektstruktur definiert
mit den Typen und dann sagt man halt, hier ist
das JSON,
passt das mal und validiert das mal.
Und wenn es nicht, dann kriegst du 422 zurück, weil
da fehlt irgendwas. Wenn es nicht
okay ist, kriegt man halt schöne Fehlermeldungen auch
zurück, wo dann genau gesagt wird, so an
der Stelle hast du gesagt, das
soll ein Number sein, aber da ist eigentlich
da ist ein String oder das ist
halt irgendwie, das passt sonst wie nicht, das soll eine Liste
sein, aber das ist halt nicht, ja
und das ist natürlich nett. Okay, also es ist
also es ist quasi
das, woraus FastAPI
gebaut wird. FastAPI ist
Identik via
HTTP. Ja,
plus es sind noch so ein paar Sachen
zusätzlich dabei. Starlet ist halt
irgendwie sozusagen das alles, was HTTP
angeht oder so macht, da drunter
die Bibliothek von Tom Christie.
Also FastAPI ist schon so ein bisschen,
ist halt so irgendwie
drei sehr coole oder drei, vier sehr
coole Open-Source-Bibliotheken in einem French-Code
irgendwie quasi. Noch so Routing,
das hat man halt irgendwie von Flask früher
kannte, als Dekorator oben. Ja, Flask war auch
sehr, ja genau, das ist natürlich auch sehr
alt. Also
Stefan, wenn du FastAPI schon kennst, dann
weißt du auch, wie Pedantic funktioniert. Nur halt
innerhalb. Okay.
Genau. Also wie gesagt, ich habe den Namen
in Architektur-Diagramm geschrieben, also das ist
meine Erfahrung damit, aber
reicht anscheinend.
Ist schon mehr damit gemacht als viele andere.
Genau.
Ja.
Genau, also ja, das ist auf jeden Fall auch so
noch ein ganz interessanter Ding, dass man, weil
ich meine, das ist ja tatsächlich so, wie viele Leute das benutzen.
Viele Leute benutzen dann TypeDict und denken,
das würde passieren, dass es validiert wird, aber es passiert halt nicht.
Ja.
Genau. Ja, ansonsten,
ich weiß es nicht, haben wir noch irgendwas Großes
vergessen oder so, aber ich glaube,
ansonsten, ich habe hier fast nichts
mehr, was ich noch irgendwie unbedingt
gerne wissen wollte. Ja.
Nach anderthalb Stunden
alles über
Typsysteme und Typen gesagt.
Das ging ja relativ schnell jetzt.
Ja. Für meinen Typen
habt ihr immer noch keine Erklärung gefunden, aber sonst.
Ja.
So Typen wie dich, Dominik,
ist schwer zu beschreiben.
Ja.
No space enough.
Ja.
Wirklich, ich finde es schön. Stefan, hast du noch was, was du
unbedingt loswerden wolltest?
Ich glaube, ich habe jetzt noch ein anschauliches Beispiel
gefunden zu Co-Varianz und Kontra-Varianz.
Oh ja. Nachdem ich mir die Grafik so lange angeschaut habe.
Ich hoffe, ich kann es erklären.
Co-Varianz ist
in Wirklichkeit, was wir als
Subtyping verstehen. Angenommen,
du hast ein Lebewesen, dann hast du ein Subtyp davon,
das ist ein Pflanzenfresser, dann hast du ein Subtyp
davon, das ist eine Kuh. Das heißt, du wirst immer
konkreter und konkreter und konkreter.
Was bedeutet, wenn du irgendwo ein Lebewesen
erwartest, kannst du dort einen Pflanzenfresser reinschmeißen,
kannst du aber auch Kühe reinschmeißen oder Schafe
reinschmeißen oder
Veganer.
Von mir aus, nicht?
Und das ist Co-Varianz.
Das heißt, du kannst
etwas sehr Breites akzeptieren
und kannst was sehr Konkretes reinstopfen,
wenn der Subtyp...
Das heißt, ich erwarte ein Lebewesen als Type Annotation quasi.
Genau, genau, genau.
Jetzt hast du aber zum Beispiel
eine andere Co-Varianz, nämlich du hast
jetzt eine Pflanze und davon abgeleitet,
Gras und davon abgeleitet vielleicht
Heuer oder so. Und jetzt
willst du
eine Funktion zur Verfügung stellen,
die akzeptiert Grasesser,
dann kannst du dort
bei den Grasessern Kühe, aber
auch Pflanzenfresser reinschmeißen.
Wenn du jetzt aber sagst, du akzeptierst
jetzt
du akzeptierst jetzt
Pflanzen, also
alle die Pflanzen
oder Funktionen, die
Pflanzen haben, dann kannst du
von Entitäten, die
alle Pflanzen essen können, alle Pflanzen,
dann kannst du dort keine Kühe
reingeben, weil Kühe können nur Gras essen.
Und das ist Kontrovarianz.
Das heißt, du hast zwar auch einen Subtypen, du hast einen
sehr breiten Typen, ich akzeptiere ja
Pflanzenfresser, allerdings kannst
du keine Kühe reingeben, weil Kühe nur Gras essen dürfen.
Okay, und Invarianz
ist dann ganz festgesetzt, dass halt nur den
einen speziellen Typ... Genau, Invarianz geht
in beide Richtungen.
Also ich hoffe, dass das nochmal
veranschaulicht. Ich glaube, wir gucken,
ich lebe dein Bild nochmal an. Ich hoffe, das Bild ist so
anschaulich für die
Leute, die ausgestiegen sind, ja.
Ja, super.
Da werden wir sicherlich ganz viele E-Mails kriegen und das
in den nächsten vier Folgen alles nochmal
genau erklären. Ja, ist ja auch okay,
wenn da eine Erklärung
dabei ist, die... Hallo bei
peistenpodcast.de. Genau. Wir haben
aber noch gar nicht ganz fertig, weil wir möchten
noch unseren Pick der Woche, glaube ich,
auswählen. Oh ja.
Ich fang mal an, ich nimm... Stefan,
weißt du denn, was ein Pick ist?
Ja.
Also müssen wir jetzt irgendeinen Link raussuchen,
den er total grandios findet. Ja, genau.
Irgendwas Schönes zeigen.
Ja, meistens nennen wir Python-Module, aber
ich nimm tatsächlich, ja,
auch nicht immer. Ich nimm tatsächlich diesmal
eins von Simon Willison,
und zwar das LLM. Ich glaube, das haben wir bei einer der
Machine Learning-Folgen... Ach, das Kommando-Zahlen-Tool.
...zwar schon irgendwo gehabt, aber
es ist tatsächlich, bei mir
ist vermehrt in Benutzung, im MonkeyPatch
schon immer das Default, aber sonst ist es sehr,
sehr schön, weil du halt ganz viele
Templates und Chains von Templates direkt
benutzt.
...benutzen kannst in deiner Kommando-Zeile,
um halt mit den verschiedenen Modellen zu sprechen
direkt, die du da haben
willst. Und es ist
toll, wenn man harte Instruktionen gibt,
dann so die Standard-Persönlichkeit
des antwortenden
LLMs irgendwie
so ein bisschen gerade zu rücken auf das, was man selber
gerne als Antwort hätte.
Such irgendwie die Leute
genug gut aus, mit denen du sprichst,
das wollte ich damit sagen, und
deswegen... Und dann lädst du
uns ein, Dominik.
Weiter hätte viel Spaß bei den nächsten
Picks, wollte ich noch sagen, ja.
Ja.
Ja, was hast du denn gepickt, Jochen?
Was wollte ich? Ah, genau, ich
dachte mir so, naja, vielleicht auch ein Buch
mal, und zwar eins, das ich
nicht gelesen habe.
Aber wo man das alles nachlesen kann,
kann man auch die Antworten,
wenn man drauf gekommen ist, wie das sein muss,
an uns schicken, und zwar
The Little Typer ist ein
Buch, das, ich hab's versucht zu lesen,
das ist irgendwie, ich hab dann zwischendurch aufgegeben.
Das muss ich sagen, wie Experiment
mit Types entbeißen.
Ja, aber da
steht, da steht das, glaub ich, alles ganz genau drin,
wenn man das wissen will. Und, ah gut,
vielleicht nochmal was Praktisches, weil
ja,
das ist ja doch nicht irgendwie
was für alle wahrscheinlich.
Doku ist ganz nett,
auch nicht Python, sondern Go.
Geschichte,
Heroku hatte ja in letzter Zeit so ein bisschen
Probleme, und
ähm, ist nicht mehr so richtig,
äh, irgendwie der Platz, wo man vielleicht so mal so,
wenn man, also früher hat man das ja irgendwie, wenn man irgendwas
mal eben deployen wollte, hat man
das oft dann bei Heroku oder so
getan, weil das halt sehr einfach war,
aber das, äh, das geht
irgendwie nicht mehr. Und, ähm,
das kann man auch in Selbstmord... Wo würdest du das jetzt machen, Jochen?
Äh, also meine, meine,
meine Lösung dafür ist ja, dass ich das halt einfach,
ich hab da so meine Standard, äh,
Ansible, äh, Dinger...
Ja, okay, gut. Also du, ja.
Du bist vom, dir ist Heroku zu
kompliziert geworden, und deshalb hast du jetzt,
äh, deine eigene Hosting-Lösung gebaut, aber das...
Ja, leider, ja. Ist natürlich keine Option,
die jetzt viele dazu haben. Genau, also,
das kann ich auch nicht unbedingt empfehlen, das ist,
äh, das unerwartet, äh,
kompliziert, aber bei mir geht's jetzt daher,
hab ich das Problem nicht mehr, ähm,
und ich mach ja auch keinen Docker oder so, sondern, äh,
ich deploye dann direkt irgendwie,
ähm... Knallhart, bare metal.
Ja, genau.
Und, ähm, äh,
bei, bei Doku hat man dann halt irgendwie
sowas, wo man dann so ähnlich wie mit
Heroku einfach, man hat halt so ein Pog-File,
und dann kann man das einfach direkt,
und da auch, also wenn man Docker-Container
bauen kann, kann man direkt Docker-Container dahin deployen,
und die laufen dann unter Subdomain
direkt mit HTTPS und so. Arbeitet das sogar
mit Heroku, also Doku?
Äh, nee, nee, das ist also, aber du,
du musst halt das Doku auf einem
von, von, von, irgendwo auf nem,
weiß ich nicht, auf einer virtuellen Maschine
irgendeinem Dings halt deployed haben,
oder auf der anderen Seite. Self-hosted Heroku. Genau,
self-hosted Heroku quasi, ja.
Und, ähm, genau,
das ist, glaube ich, manchmal ganz hilfreich, sowas zu haben.
Ah, ja.
Okay, dann, ähm, dann schließe
mich da direkt mal an, weil in dem Fall
habe ich drei Picks. Ah, okay.
Der erste ist, äh,
Vercel. Oh, cool.
Das ist, äh, das ist wie Heroku, nur cooler.
Ähm,
der zweite wäre Fly.io.
Das ist quasi, äh,
Docker-Sachen auf Hosted-Infrastruktur
überall hin, ähm,
machen, und die machen krasses technisches
Zeugs damit. Also du, du schickst dir den
Docker-Container, aber die zerlegen den, und
äh,
und, ähm, bauen sich da
eigene Sachen draus. Das, äh, ist auch
technologisch sehr interessant. Äh,
das war jetzt aber der opportunistische
Pick, äh, nur um da die,
die Alternativen zu
Heroku und self-hosted
einmal gesagt zu haben.
Jetzt sind wir ganz tief in den Pop-Rätchen, ja.
Genau, mein, mein eigentlicher Pick
ist was ganz anderes, äh, und zwar,
äh, das ZDF
hat ja eine Mediathek, und, äh,
auf dieser Mediathek kann man Sachen ansehen, und wenn man
ein paar Sachen angesehen hat, dann versucht das ZDF
da Recommendations, äh,
draus zu machen. Wow, das ist das
erste Mal seit Ewigkeiten,
dass ich von Fernsehen etwas höre.
Du meinst das ZDF, meinst du das tatsächliche
Fernsehen, was, ja, ähm,
das, äh, das, äh,
das zweite deutsche Fernsehen, meine ich, ähm,
und, äh,
die, also nur die Mediathek.
Und, ähm,
weil das ZDF ja in öffentlich-rechtlicher
Hand ist, haben die sich gesagt, eigentlich müssen
wir das ja den Leuten zurückgeben, und das haben sie tatsächlich
gemacht. Die haben ihr Recommendation-System,
ähm, auf GitHub
gepackt, und man kann das jetzt ansehen.
Und dann GitHub ZDF minus
Open Source, äh,
gibt's auch jetzt schon ein Repository drunter,
das heißt Recommendations PA Base,
ähm, und da sind,
da ist das Recommendation-System
vom ZDF drin, und fand ich einfach spannend,
ähm, das mal anzusehen,
weil da ja doch, ähm,
auch einiges an Arbeit drinsteckt,
und weil's da auch viele Firmen gibt, die sowas gerne hätten.
Genau, das war's von mir.
Stefan, hast du noch was für uns dabei?
Ja, ich hab noch was rausgefunden, das ist schon
ein älterer Artikel, ähm, vom
Bob Nystrom aus dem Jahre 2015,
ähm, heißt
What Color Is Your Function?
Und Bob Nystrom ist, ähm, einer der, der
Sprachdesigner, ähm,
der jetzt aktuell an Dart arbeitet,
ähm, und das ist sehr spannend,
weil er versucht zu erklären,
anhand von, von Farben,
ähm,
wie, wie sich
normale Funktionen und asynchrone Funktionen
in, im Sprachdesign
unterscheiden. Also rein
aus der Perspektive von, welche Herausforderungen
kriegst du als Sprachdesigner, wenn du
so ein Async-Await-Konstrukt
gestalten musst.
Ähm, und wie gesagt, der ist schon ewig alt, aber er ist
vor kurzem wieder bei uns in, in, äh,
in der Firma aufgeprobt, kann ich sehr empfehlen.
Ähm, versuch das so zu erklären,
dass du das, die, die, die
Ergebnisse, ähm,
oder die Erkenntnisse in, in Python
genauso anwenden kannst.
Und hab ich, finde ich immer wieder sehr
interessant. Gehe sehr oft
wieder drauf zu.
Für die Programmiersprachen Interessierten.
Ja, ne, sehr cool.
Also ich den, äh, quasi diese Analogie oder
diese Metapher hab ich auch schon häufig gehört. Ich wusste
aber nicht, wo sie herkommt und, äh, ja, das muss ich
auch hier nochmal lesen. Ne, da gibt's tatsächlich noch was
Älteres. What color are your bits?
Äh, ist von 2004.
Da geht's um die Herkunft
von, äh, von
Bits. Also ob deine Bits, äh,
äh, urheberrechtlich geschützt sind oder nicht.
Okay. Was du damit
machen kannst, um die Farbe, damit sie die Farbe
ändern. Aber das ist, äh, ja.
Also ich, äh, musste da auch zuerst dran denken. Also es
scheint eine gute Metapher zu sein. Wir können
das ja beides verlinken. Ja.
Sehr gern. Ja.
Ja, vielen Dank. Cool. Ich würde sagen,
herzlichen Dank, dass ihr heute alle wieder da wart.
Herzlichen Dank, Stefan. Herzlichen Dank, Johannes.
Ja, danke für die Einladung. Freut mich sehr.
Sehr gern. Und dann, ja, ähm,
bleibt uns gewogen, schaltet uns wieder ein,
hört uns, wo immer ihr gerade seid, morgens, mittags, nachts,
abends, tagsüber zum Schlafen,
zum Einschlafen. Ähm,
einen wunderschönen Tag.
Bis bald. Tschüss.
Ciao. Tschüss.