7932 lines
537 KiB
XML
7932 lines
537 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
||
<rss xmlns:atom="http://www.w3.org/2005/Atom"
|
||
xmlns:media="http://search.yahoo.com/mrss/"
|
||
xmlns:content="http://purl.org/rss/1.0/modules/content/"
|
||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||
version="2.0">
|
||
<channel>
|
||
<title>Nicole Dresselhaus</title>
|
||
<link>https://drezil.de/</link>
|
||
<atom:link href="https://drezil.de/index.xml" rel="self" type="application/rss+xml"/>
|
||
<description>Ramblings of a madwoman</description>
|
||
<generator>quarto-1.7.23</generator>
|
||
<lastBuildDate>Wed, 07 May 2025 22:00:00 GMT</lastBuildDate>
|
||
<item>
|
||
<title>Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)</title>
|
||
<link>https://drezil.de/Writing/documentation.html</link>
|
||
<description><![CDATA[
|
||
|
||
|
||
|
||
|
||
|
||
<section id="einleitung" class="level2 page-columns page-full">
|
||
<h2 class="anchored" data-anchor-id="einleitung">Einleitung</h2>
|
||
<p>Die <strong>Dokumentation von Forschungssoftware</strong> ist entscheidend, um wissenschaftliche Ergebnisse nachvollziehbar und Software für andere nutzbar zu machen. Insbesondere in den Digital Humanities (etwa in der Geschichtswissenschaft) entwickeln Forschende neben Forschung und Lehre oft eigene Software – meist unter hohem Zeitdruck und ohne formale Ausbildung in Softwareentwicklung. Häufig bleibt die Dokumentation deshalb minimal oder unvollständig, was dazu führt, dass andere (und sogar die Autor*innen selbst) viel Zeit aufwenden müssen, um den Code zu verstehen und anzuwenden. Dabei gilt gute Dokumentation als zentrale Voraussetzung, um Forschungssoftware <strong>auffindbar, nachvollziehbar und wiederverwendbar</strong> zu machen.</p>
|
||
<div class="page-columns page-full"><p></p><div class="no-row-height column-margin column-container"><span class="margin-aside">Alle Empfehlungen stützen sich auf Literatur und etablierte Richtlinien <span class="citation" data-cites="prlic2012ten wilson2017good katz2021open endings2020principles">(Prlić und Procter 2012; Wilson u. a. 2017; Katz, Niemeyer, und Smith 2021; Endings Project 2020)</span>.</span></div></div>
|
||
<p>Dieser Anforderungskatalog richtet sich an Forschende, die keine Vollzeit-Programmierer sind, und soll <strong>wissenschaftlich fundierte Richtlinien</strong> für die Dokumentation von Forschungssoftware liefern. Die Empfehlungen berücksichtigen Best Practices des Research Software Engineering (RSE) und insbesondere die Prinzipien des <em>Endings-Projekts</em> für digitale Langlebigkeit <span class="citation" data-cites="endings2020principles">(Endings Project 2020)</span>. Ziel ist es, ein praxistaugliches Gerüst bereitzustellen, das – trotz Zeitknappheit – die wesentlichen Dokumentationsaspekte abdeckt, um sowohl die <strong>Nachvollziehbarkeit</strong> der Ergebnisse als auch eine <strong>Weiterverwendung</strong> der Software zu ermöglichen. Im Folgenden werden die Anforderungen an Inhalt, Format und Umfang der Dokumentation definiert, geeignete (teil-)automatisierte Dokumentationswerkzeuge diskutiert und Best Practices in Form von Vorlagen und Checklisten vorgestellt.</p>
|
||
</section>
|
||
<section id="inhaltliche-anforderungen-an-die-dokumentation" class="level2 page-columns page-full">
|
||
<h2 class="anchored" data-anchor-id="inhaltliche-anforderungen-an-die-dokumentation">Inhaltliche Anforderungen an die Dokumentation</h2>
|
||
<p>Ein zentrales Problem in der Dokumentation wissenschaftlicher Software ist oft das fehlende <em>Big Picture</em>, also eine klare Darstellung des <em>Was</em> und <em>Warum</em>. Die Dokumentation sollte daher alle <strong>Informationen abdecken, die zum Verstehen, Nutzen und Weiterentwickeln der Software nötig sind</strong>. Insbesondere sind folgende Inhalte essenziell:</p>
|
||
<section id="ziel-und-zweck-der-software-statement-of-need" class="level3">
|
||
<h3 class="anchored" data-anchor-id="ziel-und-zweck-der-software-statement-of-need">Ziel und Zweck der Software (Statement of Need)</h3>
|
||
<p>Beschreiben Sie <em>was die Software tut</em> und <em>warum sie entwickelt wurde</em>. Nennen Sie den wissenschaftlichen Zweck, das Forschungsproblem oder die Fragestellung, die mit der Software adressiert wird, sowie die <em>Zielgruppe</em> (wer soll sie nutzen?). Dieser Kontext hilft anderen, den Nutzen der Software einzuschätzen. Beispiel: <em>“Dieses Tool extrahiert Personen-Netzwerke aus historischen Briefkorpora, um sozialwissenschaftliche Analysen zu ermöglichen.”</em> Eine klare Problem- und Zielbeschreibung richtet sich auch nach dem Umfeld ähnlicher Lösungen – falls es bereits etablierte Tools gibt, sollte die Dokumentation die eigene Herangehensweise einordnen (z. B. was die Software anders oder besser macht).</p>
|
||
</section>
|
||
<section id="input-output-spezifikation-und-datenbeschreibung" class="level3 page-columns page-full">
|
||
<h3 class="anchored" data-anchor-id="input-output-spezifikation-und-datenbeschreibung">Input-/Output-Spezifikation und Datenbeschreibung</h3>
|
||
<div class="page-columns page-full"><p>Dokumentieren Sie alle <em>Eingabeformate, Ausgabedaten und verwendeten Datensätze</em>. Nutzer*innen müssen wissen, welche Daten die Software erwartet (Dateiformate, Schnittstellen, Parameter) und welche Ergebnisse sie produziert. Idealerweise werden Beispiele angegeben: z. B. Beispiel-Dateien oder -Parameter und die korrespondierende Ausgabe. Falls die Software mit bestimmten Forschungsdaten arbeitet, beschreiben Sie diese Daten und ihre Struktur. Dies umfasst die <strong>Datenmodelle</strong> (etwa wichtige Felder, deren Bedeutung und kontrollierte Vokabulare) und Annahmen über die Daten. Gemäß den ENDINGS-Prinzipien sollte die Datenstruktur in einem <em>statischen Dokument</em> festgehalten und der Software beigelegt sein – so bleibt nachvollziehbar, wie die Software die Daten interpretiert. Eine Tabelle oder Auflistung der Eingabefelder und Ausgabegrößen mit kurzen Beschreibungen erhöht die Klarheit. </p><div class="no-row-height column-margin column-container"><span class="margin-aside">Beispiel: <em>“Eingabedatei: CSV mit Spalten <code>Autor</code>, <code>Empfänger</code>, …; Ausgabe: JSON-Datei mit Netzwerk-Metriken pro Briefwechsel.”</em></span></div></div>
|
||
</section>
|
||
<section id="code-abhängigkeiten-und-technische-voraussetzungen" class="level3 page-columns page-full">
|
||
<h3 class="anchored" data-anchor-id="code-abhängigkeiten-und-technische-voraussetzungen">Code-Abhängigkeiten und technische Voraussetzungen</h3>
|
||
<div class="page-columns page-full"><p>Listen Sie alle <em>Abhängigkeiten</em> (Dependencies) der Software auf. Dazu gehören verwendete Programmiersprachen/Versionen, erforderliche Bibliotheken oder Frameworks, und sonstige Systemvoraussetzungen (z. B. Betriebssystem, Mindesthardware, Datenbank-Versionen). Wichtig ist, <strong>wie</strong> diese Abhängigkeiten installiert werden können. Optimal ist eine automatisierte Installationsroutine (z. B. ein <code>requirements.txt</code> für Python oder ein Paketmanager-Befehl). In jedem Fall sollte die Dokumentation mindestens Schritt-für-Schritt-Installationsanleitungen enthalten (inklusive evtl. benötigter Vorkenntnisse, z. B. <em>“Python 3 erforderlich”</em>). </p><div class="no-row-height column-margin column-container"><span class="margin-aside">Beispiel: <em>“Benötigt Python 3.9 und die Bibliotheken Pandas und NetworkX. Installation: <code>pip install -r requirements.txt</code>.”</em> Falls spezielle technische Voraussetzungen bestehen – etwa Zugriff auf bestimmte Hardware, ein Hochleistungsrechner oder große Speicherkapazitäten – sind diese zu nennen.</span></div></div>
|
||
<ul>
|
||
<li><strong>Typische Nutzungsszenarien und Workflows:</strong> Zeigen Sie anhand von <em>Beispielen</em>, wie die Software benutzt wird. Ein <strong>Quickstart-Beispiel</strong> senkt die Einstiegshürde enorm. Dies kann z. B. eine Anleitung sein, wie man mit wenigen Schritten von einer Eingabedatei zum gewünschten Ergebnis kommt (<em>“Getting Started”</em>-Abschnitt). Beschreiben Sie typische Workflows in nachvollziehbaren Schritten: Eingabe vorbereiten, Software-Befehl/GUI-Aktion ausführen, Ausgabe interpretieren. Ggf. können mehrere Anwendungsfälle skizziert werden (z. B. <em>“Analyse eines einzelnen Briefes”</em> vs. <em>“Batch-Verarbeitung eines gesamten Korpus”</em>). Diese Beispiele sollten realistisch und möglichst <em>repräsentativ für wissenschaftliche Anwendungen</em> sein. Nutzen Sie gerne kleine Datensamples oder Defaults, damit Nutzer die Beispielschritte direkt ausprobieren können. Idealerweise werden Code-Beispiele mit ausgegebenen Resultaten gezeigt (z. B. in Form von Ausschnitten oder, bei Kommandozeilentools, via <code>--help</code> dokumentiert). </li>
|
||
</ul>
|
||
<div class="no-row-height column-margin column-container"><span class="margin-aside">Faustregel: <strong>Zeigen statt nur beschreiben</strong> – konkrete Anwendungsfälle in der Doku verankern.</span></div></section>
|
||
<section id="wissenschaftlicher-hintergrund-und-theoretischer-kontext" class="level3 page-columns page-full">
|
||
<h3 class="anchored" data-anchor-id="wissenschaftlicher-hintergrund-und-theoretischer-kontext">Wissenschaftlicher Hintergrund und theoretischer Kontext</h3>
|
||
<div class="page-columns page-full"><p>Da es sich um Forschungssoftware handelt, sollten Sie den <em>wissenschaftlichen Kontext</em> offenlegen. Das heißt, erklären Sie die grundlegenden Methoden, Algorithmen oder Modelle, die in der Software umgesetzt sind, zumindest in Überblicksform. Verweisen Sie auf <em>relevante Publikationen</em> oder Theorien, damit andere die wissenschaftliche Grundlage nachvollziehen können. Beispielsweise: <em>“Die Implementierung folgt dem Algorithmus von Müller et al. (2019) zur Netzwerkanalyse historischer Korrespondenz.”</em> Halten Sie diesen Abschnitt aber prägnant – Details gehören in die Forschungsarbeit selbst. Wichtig ist, dass die Dokumentation den <strong>Brückenschlag zwischen Code und Forschung</strong> herstellt. Da viele Wissenschaftler*innen zentrale Aspekte lieber in ihren Artikeln dokumentieren, sollte in der Software-Dokumentation zumindest eine Zusammenfassung mit Querverweis erfolgen. So wissen Nutzer*innen, unter welchen Annahmen oder Theorien das Tool funktioniert. </p><div class="no-row-height column-margin column-container"><span class="margin-aside">Dieser Hintergrundteil unterscheidet Forschungssoftware-Dokumentation von rein kommerzieller Dokumentation: Es geht nicht nur um <em>wie</em> man das Tool benutzt, sondern auch <em>warum</em> es so funktioniert (Stichwort Nachvollziehbarkeit).</span></div></div>
|
||
</section>
|
||
<section id="bekannte-limitationen-annahmen-und-fehlermeldungen" class="level3">
|
||
<h3 class="anchored" data-anchor-id="bekannte-limitationen-annahmen-und-fehlermeldungen">Bekannte Limitationen, Annahmen und Fehlermeldungen</h3>
|
||
<p>Geben Sie ehrlich Auskunft über die <em>Grenzen der Software</em>. Welche Fälle werden <strong>nicht</strong> abgedeckt? Welche Annahmen über die Daten oder Anwendungsszenarien werden getroffen? Dokumentieren Sie bekannte Probleme oder Einschränkungen (z. B. <em>“funktioniert nur für Deutschsprachige Texte”, “maximale Datenmenge 1 Mio. Datensätze, da Speicherbegrenzung”</em>). Solche Hinweise verhindern Fehlanwendungen und sparen Nutzern Zeit. Falls es bekannte <strong>Bugs oder Workarounds</strong> gibt, sollten diese ebenfalls (etwa in einer FAQ oder einem Abschnitt “Bekannte Probleme”) erwähnt werden. Eine transparente Auflistung von Limitationen erhöht die Vertrauenswürdigkeit und hilft anderen, die Ergebnisse richtig einzuordnen. Auch <strong>aussagekräftige Fehlermeldungen</strong> im Programm selbst sind eine Form von Dokumentation: Sie sollten nicht nur kryptisch abbrechen, sondern dem/der Anwender*in idealerweise mitteilen, was schiefging und wie es behoben werden kann (z. B. <em>“Fehler: Ungültiges Datum im Feld XY – bitte Format TT/MM/JJJJ verwenden.”</em>). Solche in den Code integrierten Hinweise ergänzen die schriftliche Dokumentation und tragen zur besseren Nutzbarkeit bei.</p>
|
||
</section>
|
||
<section id="weiterentwicklung-und-beitragsmöglichkeiten" class="level3 page-columns page-full">
|
||
<h3 class="anchored" data-anchor-id="weiterentwicklung-und-beitragsmöglichkeiten">Weiterentwicklung und Beitragsmöglichkeiten</h3>
|
||
<div class="page-columns page-full"><p>Obwohl viele Digital-Humanities-Tools primär von Einzelpersonen genutzt werden, sollte dennoch angegeben werden, wie andere ggf. <em>zur Software beitragen oder Support erhalten</em> können. Ein kurzer Hinweis auf den Issue-Tracker (z. B. <em>“Fehler bitte über GitHub-Issues melden”</em>) oder auf die Kontaktmöglichkeit zum Autor (E-Mail) gehört dazu. Ebenso können <strong>Community Guidelines</strong> skizziert werden: etwa Codierstandards oder ein Verhaltenskodex, falls Beiträge erwartet werden. Für kleinere Projekte reicht oft ein Satz wie <em>“Beiträge durch Pull Requests sind willkommen; bei Fragen wenden Sie sich an…”</em>. </p><div class="no-row-height column-margin column-container"><span class="margin-aside">Dieser Aspekt muss nicht umfangreich sein, zeigt aber Offenheit und sorgt dafür, dass im Falle von Rückfragen die Hürde für Kontaktaufnahme niedrig ist.</span></div></div>
|
||
</section>
|
||
<section id="projekt-metadaten-lizenz-zitation-version" class="level3">
|
||
<h3 class="anchored" data-anchor-id="projekt-metadaten-lizenz-zitation-version">Projekt-Metadaten (Lizenz, Zitation, Version)</h3>
|
||
<p>Teil der Dokumentation sind auch formale Informationen, die im Repository leicht zugänglich sein sollten. <strong>Lizenzinformationen</strong> klären die rechtlichen Bedingungen der Nutzung und Weiterverbreitung. Es ist Best Practice, eine <strong>LICENSE-Datei</strong> beizulegen, aber auch in der README kurz zu erwähnen, unter welcher Lizenz die Software steht. Für Forschungssoftware empfiehlt sich eine offene Lizenz (z. B. MIT, BSD oder Apache 2.0 für Code, CC-BY für Daten), um Nachnutzung nicht zu behindern. Zudem sollte angegeben werden, wie die Software <strong>zitiert</strong> werden kann (z. B. DOI, Paper-Referenz). Ein eigener Abschnitt <em>“Zitation”</em> oder eine CITATION-Datei beschreibt, welche Publikation oder welcher DOI bei Verwendung der Software in wissenschaftlichen Arbeiten anzugeben ist. Dies erhöht die akademische Sichtbarkeit und stellt sicher, dass Autor*innen Credits für ihre Software bekommen<span class="citation" data-cites="smith2016software">(Smith u. a. 2016)</span>. Schließlich ist es sinnvoll, eine <strong>Versionsnummer</strong> der Software zu nennen (idealerweise in README und im Tool selbst), damit Nutzer wissen, auf welche Ausgabe sich die Dokumentation bezieht – insbesondere, wenn es im Laufe der Zeit Aktualisierungen gibt. Diese Praxis entspricht auch den ENDINGS-Prinzipien, die verlangen, dass jede veröffentlichte Version eindeutig erkennbar ist und zitiert werden kann.</p>
|
||
</section>
|
||
<section id="zusammenfassung-der-inhaltlichen-anforderungen" class="level3">
|
||
<h3 class="anchored" data-anchor-id="zusammenfassung-der-inhaltlichen-anforderungen">Zusammenfassung der inhaltlichen Anforderungen</h3>
|
||
<p>Zusammengefasst sollte die Dokumentation alle <strong>W-Fragen</strong> beantworten: <em>Was</em> tut die Software, <em>warum</em> wurde sie geschrieben (wissenschaftlicher Zweck), <em>wer</em> soll sie nutzen, <em>wie</em> wird sie benutzt (Inputs, Outputs, Abläufe), <em>womit</em> läuft sie (Umgebung/Abhängigkeiten), <em>unter welchen Bedingungen</em> (Annahmen/Limitationen) und <em>wohin</em> können sich Nutzer wenden (Support/Zitation). All diese Punkte sorgen für <strong>Nachvollziehbarkeit</strong> (im Sinne von Reproduzierbarkeit der Ergebnisse) und <strong>Weiterverwendbarkeit</strong> (im Sinne von Adaptierbarkeit der Software für neue Kontexte).</p>
|
||
</section>
|
||
</section>
|
||
<section id="format-und-struktur-der-dokumentation" class="level2 page-columns page-full">
|
||
<h2 class="anchored" data-anchor-id="format-und-struktur-der-dokumentation">Format und Struktur der Dokumentation</h2>
|
||
<p>Für Forschende ohne viel Ressourcen muss die Dokumentation <strong>einfach zugänglich, leicht pflegbar und ohne Spezialsoftware</strong> erstellbar sein. Daher empfiehlt es sich, auf <strong>leichte Formate</strong> und eine klare Struktur zu setzen:</p>
|
||
<section id="readme.md-als-zentrales-dokument" class="level3">
|
||
<h3 class="anchored" data-anchor-id="readme.md-als-zentrales-dokument"><code>README.md</code> als zentrales Dokument</h3>
|
||
<p>Die Hauptdokumentation sollte als README in Markdown-Format im Hauptverzeichnis des Code-Repositoriums liegen. Dieses README fungiert als “Startseite” des Projekts und enthält idealerweise eine komprimierte Übersicht aller wichtigen Punkte: Zweck der Software, Kurzbeschreibung, Installation, kurzer Nutzungsbeispiel, Kontakt/Lizenz. Auf Plattformen wie GitHub, GitLab etc. wird die README automatisch angezeigt, was die Sichtbarkeit erhöht. Die Vorteile von <strong>Markdown</strong> sind die einfache Lesbarkeit in Rohform, die breite Unterstützung (auch in Renderern wie GitHub-Webansicht) und die Eignung für Versionierung (Textdatei im Git). So bleibt die Dokumentation eng mit dem Code verzahnt und unter Versionskontrolle – ein Prinzip, das auch von ENDINGS propagiert wird (Dokumentation soll statisch und zusammen mit den Daten/Code abgelegt werden).</p>
|
||
</section>
|
||
<section id="strukturierte-unterteilung-in-weitere-dateienabschnitte" class="level3 page-columns page-full">
|
||
<h3 class="anchored" data-anchor-id="strukturierte-unterteilung-in-weitere-dateienabschnitte">Strukturierte Unterteilung in weitere Dateien/Abschnitte</h3>
|
||
|
||
<div class="no-row-height column-margin column-container"><div class="">
|
||
<pre class="plain"><code>example-project/
|
||
├── README.md
|
||
├── CONTRIBUTING.md (optional)
|
||
├── CHANGELOG.md (optional)
|
||
├── CITATION.md (oder CITATION.cff)
|
||
├── LICENSE
|
||
├── data/ (optional)
|
||
│ └── sample_data.csv
|
||
├── docs/ (optional)
|
||
│ ├── INSTALL.md
|
||
│ └── USAGE.md
|
||
├── examples/ (optional)
|
||
│ └── example_workflow.ipynb
|
||
└── src/
|
||
├── script.py
|
||
└── module/
|
||
└── helper.py</code></pre>
|
||
<p>Beispielhafter Struktur eines Code-Repositories</p>
|
||
</div></div><p>Sollte die Dokumentation umfangreicher sein, ist es sinnvoll, sie in logisch getrennte Abschnitte aufzuteilen. Dies kann innerhalb der README durch Überschriften geschehen oder durch <strong>zusätzliche Markdown-Dateien</strong> im Repository (z. B. eine <code>INSTALL.md</code> für ausführliche Installationshinweise, eine <code>USAGE.md</code> oder <code>TUTORIAL.md</code> für detaillierte Benutzeranleitungen, eine <code>CHANGELOG.md</code> für Changelog etc.). Eine gängige Struktur ist z. B.:</p>
|
||
<ul>
|
||
<li><code>README.md</code> – Überblick (Ziel, Installation, kurzes Beispiel, Lizenz/Zitation)</li>
|
||
<li><code>docs/</code> Verzeichnis mit weiteren .md-Dateien für tiefergehende Dokumentation (optional)</li>
|
||
<li><code>CONTRIBUTING.md</code> – Hinweise für Beiträger (falls relevant)</li>
|
||
<li><code>LICENSE</code> – Lizenztext</li>
|
||
<li><code>CITATION.cff</code> oder <code>CITATION.md</code> – wie zu zitieren.</li>
|
||
</ul>
|
||
<p>Diese Dateien sollten konsistent formatiert und benannt sein, damit sie leicht auffindbar sind. Sie kommen ohne spezielle Tools aus – ein einfacher Texteditor genügt zum Bearbeiten. Auch <strong>Wiki-Seiten</strong> (etwa in GitHub) können genutzt werden, sind aber weniger dauerhaft versioniert im Vergleich zu Dateien im Code-Repository selbst. Die Dokumentation sollte möglichst <em>im Repository</em> selbst liegen, um sicherzustellen, dass sie gemeinsam mit dem Code versioniert, verteilt und archiviert wird. Externe Dokumentationswebsites sind für kleine Projekte oft Overkill und können im schlimmsten Fall verwaisen.</p>
|
||
</section>
|
||
<section id="keine-proprietären-formate-oder-abhängigkeit-von-werkzeugen" class="level3">
|
||
<h3 class="anchored" data-anchor-id="keine-proprietären-formate-oder-abhängigkeit-von-werkzeugen">Keine proprietären Formate oder Abhängigkeit von Werkzeugen</h3>
|
||
<p>Um Hürden für die Erstellung und Nutzung der Dokumentation gering zu halten, sollte auf gängige, offene Formate gesetzt werden (Plaintext, Markdown, reStructuredText). Vermeiden Sie nach Möglichkeit Formate wie Word-Dokumente oder PDF als primäre Dokumentationsquelle – solche Formate sind nicht diff-freundlich, erschweren Zusammenarbeits-Workflows und sind meist nicht Teil des Versionskontrollsystems. Ein Markdown-Dokument hingegen kann gemeinsam mit dem Code gepflegt werden, und Änderungen sind transparent nachvollziehbar. Zudem erlauben offene Formate eine leichtere <strong>Langzeitarchivierung</strong>: Gemäß Endings-Prinzip sollten Informationsressourcen in langfristig lesbaren Formaten vorliegen. Markdown/Plaintext erfüllt diese Bedingung (im Gegensatz etwa zu einer Datenbank-gestützten Wissensbasis oder einem proprietären Wiki, das in 10 Jahren evtl. nicht mehr läuft). Im Sinne der <em>Digital Longevity</em> ist eine <strong>statische HTML- oder PDF-Version</strong> der Dokumentation (automatisch generiert aus Markdown) als Teil der Release-Artefakte sinnvoll – so kann z. B. in jeder veröffentlichten Version ein PDF-Handbuch beigelegt werden, das später zitiert oder referenziert werden kann. <strong>Wichtig ist aber, dass die Quelle der Wahrheit immer die im Repository gepflegte Doku bleibt.</strong></p>
|
||
</section>
|
||
<section id="übersichtlichkeit-und-navigierbarkeit" class="level3">
|
||
<h3 class="anchored" data-anchor-id="übersichtlichkeit-und-navigierbarkeit">Übersichtlichkeit und Navigierbarkeit</h3>
|
||
<p>Strukturieren Sie die Dokumentation mit klaren Überschriften und Listen, damit Leser schnell die gesuchten Informationen finden. Eine <strong>logische Gliederung</strong> (wie in diesem Katalog: Einführung, Anforderungen, Installation, Nutzung, Hintergrund, etc.) hilft unterschiedlichen Nutzergruppen gezielt das Relevante zu finden. Für längere Dokumente kann ein Inhaltsverzeichnis oder eine Abschnittsübersicht am Anfang nützlich sein. Markdown bietet z. B. automatische Toc-Generierung auf manchen Plattformen. Achten Sie darauf, pro Abschnitt nur zusammenhängende Informationen zu behandeln (z. B. alles zu Installation an einem Ort). Wiederholungen sollten vermieden werden: lieber an einer Stelle ausführlich dokumentieren und sonst darauf verweisen, um Konsistenzprobleme zu vermeiden (<em>“Don’t Repeat Yourself”</em> gilt auch für Dokumentation). Bei ähnlichen Projekten können Sie sich an bestehenden <strong>Dokumentationsvorlagen</strong> orientieren: Viele erfolgreiche Open-Source-Projekte haben auf GitHub eine ähnliche README-Struktur, die als informelles Template dienen kann.</p>
|
||
</section>
|
||
<section id="beispiele-codeblöcke-und-ggf.-abbildungen-einbinden" class="level3">
|
||
<h3 class="anchored" data-anchor-id="beispiele-codeblöcke-und-ggf.-abbildungen-einbinden">Beispiele, Codeblöcke und ggf. Abbildungen einbinden</h3>
|
||
<p>Nutzen Sie die Möglichkeiten von Markdown, um die Dokumentation lebendig zu gestalten. Zeigen Sie Code-Beispiele als formatierte Codeblöcke, fügen Sie Links zu weiterführenden Ressourcen ein, oder binden Sie bei Bedarf Abbildungen ein (etwa ein Diagramm der Datenpipeline, ein Screenshot der Benutzeroberfläche, etc.). Achten Sie dabei auf Dateigrößen und Formate (Bilder als PNG/JPG, Diagramme wenn möglich als SVG für Langlebigkeit). Falls Diagramme der Architektur oder Workflow-Abbildungen hilfreich sind, können diese mit simplen Mitteln erstellt werden (zur Not handgezeichnet und abfotografiert, besser jedoch mit Tools wie mermaid.js Diagrammen in Markdown oder Graphviz). Diese Visualisierungen sind jedoch nur dann einzusetzen, wenn sie echten Mehrwert bieten und ohne komplexe Build-Prozesse eingebunden werden können. Im Zweifel hat textuelle Beschreibung Vorrang, um nicht vom <strong>prinzip “keep it simple”</strong> abzuweichen.</p>
|
||
</section>
|
||
<section id="fazit-format-und-struktur" class="level3">
|
||
<h3 class="anchored" data-anchor-id="fazit-format-und-struktur">Fazit Format und Struktur</h3>
|
||
<p>Insgesamt gilt: <strong>Die Dokumentation sollte im gleichen Repository leben wie der Code, klar strukturiert und in einem einfach handhabbaren Format vorliegen.</strong> Sie soll ohne spezielle Umgebung lesbar sein – ein Nutzer, der das Repository klont oder herunterlädt, muss sofort Zugang zur Dokumentation haben. Dieses Prinzip entspricht auch den FAIR- und RSE-Richtlinien, die fordern, Software (und deren Doku) <em>auffindbar</em> und <em>zugänglich</em> zu machen, ohne Hürden. Eine gut gepflegte README in Markdown erfüllt diese Anforderungen in den meisten Fällen optimal.</p>
|
||
</section>
|
||
</section>
|
||
<section id="umfang-und-fokus-der-dokumentation" class="level2">
|
||
<h2 class="anchored" data-anchor-id="umfang-und-fokus-der-dokumentation">Umfang und Fokus der Dokumentation</h2>
|
||
<p>Gerade weil Forschende wenig Zeit haben, muss die Dokumentation <strong>effizient</strong> gestaltet sein – sie soll alle wichtigen Informationen enthalten, aber auch nicht unnötig ausschweifen. Für typische Forschungssoftware-Projekte in den Geisteswissenschaften wird ein Umfang von <em>maximal ca. 10 Seiten</em> (bei Bedarf verteilt auf mehrere Dateien) als ausreichend erachtet. Dieser Richtwert verhindert, dass die Doku zu einer unüberschaubaren Abhandlung wird, und zwingt zur Fokussierung auf das Wesentliche. Wichtig ist der <strong>Inhalt, nicht die Länge</strong>: eine kürzere, aber inhaltsreiche Dokumentation ist besser als eine lange, die nichts aussagt.</p>
|
||
<p>Ein effizienter Umfang lässt sich erreichen, indem man sich auf die oben genannten Kernpunkte konzentriert und Ablenkendes weglässt. Dokumentieren Sie <strong>alles, was für Nachvollziehbarkeit und Wiederverwendung nötig ist, und skippen Sie alles andere</strong>. Zum Beispiel muss nicht jeder interne Programmiertrick erläutert werden – Quellcode-Kommentare richten sich an Entwickler, während die Nutzerdokumentation sich auf Nutzung und Kontext beschränkt. Verzichten Sie auf seitenlange Theorieableitungen (verweisen Sie stattdessen auf Papers) und auf generische Erklärungen bekannter Technologien (man muss Git oder Python nicht in der Doku erklären, sondern kann referenzieren). Halten Sie auch die Sprache prägnant: kurze Absätze, Listen und einfache Sätze erhöhen die Lesbarkeit. Fachtermini aus dem jeweiligen wissenschaftlichen Bereich dürfen verwendet werden, aber erklären Sie sie, falls die Zielnutzer sie evtl. nicht kennen.</p>
|
||
<p><strong>Priorisierung:</strong> Beginnen Sie mit einer Minimaldokumentation, die alle Schlüsselaspekte abdeckt (<em>“keine Dokumentation”</em> ist keine Option). <em>Good Enough Practices</em> empfehlen, als ersten Schritt zumindest einen <strong>kurzen erklärenden Kommentar am Anfang jedes Scripts</strong> oder eine README mit ein paar Sätzen zu erstellen. Diese Hürde ist niedrig und bringt bereits Nutzen – selbst wenn (noch) keine ausführliche Handbuch-Doku existiert. Später kann die Dokumentation erweitert werden, insbesondere wenn die Software in Kooperation entsteht oder mehr Nutzer gewinnt. Es hat sich gezeigt, dass ausführliche Dokumentation oft erst entsteht, wenn ein echter Bedarf (z. B. durch externe Nutzer) vorhanden ist. Daher: zögern Sie nicht, zunächst <em>klein</em> anzufangen, aber stellen Sie sicher, dass zumindest die kritischen Informationen sofort verfügbar sind (lieber ein 2-seitiges README heute, als das perfekte 30-seitige Handbuch in zwei Jahren, das evtl. nie geschrieben wird).</p>
|
||
<p>Die Obergrenze von ~10 Seiten ist ein Richtwert. Umfangreiche Projekte könnten etwas mehr benötigen, sehr kleine Tools kommen mit einer Seite aus. Das Ziel ist, dass ein interessierter Nutzer die Dokumentation in überschaubarer Zeit durchsehen kann. Ein guter Test ist: <strong>Kann eine neue Person in < 1 Stunde mit Hilfe der Doku das Tool zum Laufen bringen und ein einfaches Beispiel ausführen?</strong> Wenn ja, ist der Detailgrad angemessen. Wenn die Person hingegen nach 10 Seiten immer noch nicht weiß, wie sie loslegen soll, muss die Doku fokussierter werden. Fügen Sie zur Not eine kurze <em>Übersicht/Zusammenfassung</em> am Anfang ein, die das Wichtigste in Kürze nennt – viele Leser entscheiden in wenigen Minuten, ob sie eine Software weiter betrachten oder nicht, und hier zählt der erste Eindruck.</p>
|
||
<p>Ein weiterer Tipp zur Effizienz: Nutzen Sie <strong>Verweise und vorhandene Ressourcen</strong>. Wenn z. B. Ihr Tool auf einem komplizierten Setup (Datenbank, Webserver) aufbaut, brauchen Sie nicht jede Installationsoption im Detail in Ihrer Doku zu reproduzieren – verlinken Sie auf offizielle Installationsanleitungen dieser Abhängigkeiten, und nennen Sie nur Ihre spezifischen Konfigurationen. Ebenso können Tutorials oder Papers, die schon existieren, als weiterführende Links angegeben werden, anstatt Inhalte redundant zu erklären. Das entlastet Ihre Dokumentation und hält sie schlank.</p>
|
||
<p>Zum Fokus gehört auch, zwischen <strong>Nutzerdokumentation und Entwicklerdokumentation</strong> zu unterscheiden. Dieser Katalog adressiert primär die Nutzerdokumentation (für Endnutzer und für die Autoren selbst, wenn sie das Tool später wieder anfassen). Entwicklerdokumentation (z. B. detaillierte API-Dokumente, Code-Kommentare, technische Architektur) kann separat gehalten werden, sofern nötig, um den Hauptnutzerfluss nicht zu überfrachten. Für viele kleine Forschungssoftware-Projekte sind ausführliche Entwicklerdokus nicht nötig – hier reicht es, den Code gut zu kommentieren und eventuell eine grobe Architekturübersicht bereitzustellen. Konzentrieren Sie die Hauptdokumentation darauf, <strong>das Nutzen und Verstehen der Software von außen</strong> zu ermöglichen.</p>
|
||
<p>Abschließend sei betont: Ein kompakter, zielgerichteter Dokumentsatz, der genau die relevanten Infos liefert, erhöht die Wahrscheinlichkeit, dass er <strong>aktualisiert und genutzt</strong> wird. Umfangmonster schrecken ab und veralten schneller. Halten Sie die Dokumentation deshalb so <strong>knapp wie möglich, aber so ausführlich wie nötig</strong> – ganz im Sinne von Einsteins Prinzip, Dinge so einfach wie möglich zu machen, aber nicht einfacher.</p>
|
||
</section>
|
||
<section id="teil-automatisierte-dokumentationswerkzeuge" class="level2 page-columns page-full">
|
||
<h2 class="anchored" data-anchor-id="teil-automatisierte-dokumentationswerkzeuge">(Teil-)automatisierte Dokumentationswerkzeuge</h2>
|
||
<p>Die Dokumentationslast lässt sich durch den Einsatz geeigneter Werkzeuge erheblich senken. Gerade Forschende, die alleine programmieren, können von <strong>(teil-)automatisierter Dokumentation</strong> profitieren, um konsistente und aktuelle Unterlagen zu erhalten, ohne alles von Hand schreiben zu müssen. Im Folgenden werden einige Tools und Möglichkeiten vorgestellt – samt Empfehlungen, <em>wann</em> ihr Einsatz sinnvoll oder notwendig ist:</p>
|
||
<section id="docstrings-und-api-dokumentationsgeneratoren" class="level3 page-columns page-full">
|
||
<h3 class="anchored" data-anchor-id="docstrings-und-api-dokumentationsgeneratoren">Docstrings und API-Dokumentationsgeneratoren</h3>
|
||
<p>Nutzen Sie die Möglichkeit, Dokumentation <em>direkt im Quellcode</em> unterzubringen, z. B. in Form von <strong>Docstrings</strong> (mehrzeilige Strings in Funktionen/Klassen bei Python, Roxygen-Kommentare in R, Javadoc-Kommentare in Java, etc.). Diese dienen doppelt: Zum einen erleichtern sie es Ihnen und Kollegen, den Code beim Lesen zu verstehen, zum anderen können sie von Tools ausgelesen und zu hübschen API-Dokumentationen verarbeitet werden. Idealerweise dokumentieren Sie <em>jede wichtige Funktion, Klasse oder Modul</em> mit einem kurzen Docstring, der Zweck, Parameter, Rückgaben und ggf. Beispiele enthält. Für kleine Scripte genügen ggf. Modul- oder Abschnittskommentare. Wichtig ist Konsistenz im Stil – halten Sie sich an Konventionen Ihres Ökosystems (z. B. <strong>Google Style Guide</strong> für Python Docstrings oder entsprechende Formatvorgaben für andere Sprachen). Mit Tools wie <strong>Sphinx</strong> (für Python, aber grundsätzlich sprachunabhängig) können aus Docstrings automatisiert Webseiten oder PDF-Handbücher generiert werden. Sphinx liest z. B. die Python-Docstrings und erzeugt daraus strukturiert eine Dokumentation; Erweiterungen wie <em>napoleon</em> erlauben es, Google- oder Numpy-Style-Dokumentation direkt zu verarbeiten.</p>
|
||
|
||
<div class="no-row-height column-margin column-container"><div class="">
|
||
<p>Ähnliche Generatoren gibt es für nahezu alle Sprachen: <strong>Javadoc</strong> für Java, <strong>Doxygen</strong> für C/C++ (und viele andere Sprachen), <strong>MkDocs</strong> oder <em>pdoc</em> für Python, etc.</p>
|
||
</div></div><p>Der Einsatz solcher Tools ist besonders dann sinnvoll, wenn Ihre Forschungssoftware über eine <em>Programmierschnittstelle (API)</em> verfügt, die von anderen genutzt werden soll, oder wenn das Projekt größer wird und die interne Struktur komplexer ist. In solchen Fällen kann eine <em>API-Referenz</em> (automatisch aus dem Code erzeugt) eine erhebliche Hilfe sein. <strong>Verpflichtend</strong> wird dieser Ansatz etwa, wenn Sie ein Bibliothekspaket veröffentlichen (z. B. ein R-Package in CRAN oder Python-Package auf PyPI) – dort sind Docstrings und generierte Dokumentation quasi Standard. Für ein einmaliges Analyse-Skript in den Digital Humanities ist eine voll ausgebaute API-Doku vielleicht nicht nötig; hier reicht möglicherweise ein inline kommentierter Code. Doch sobald Funktionen von anderen aufgerufen oder das Projekt von mehreren entwickelt wird, sollte ein Dokumentationstool in Betracht gezogen werden, um den Aufwand gering zu halten und Einheitlichkeit zu gewährleisten.</p>
|
||
</section>
|
||
<section id="jupyter-notebooks-und-literate-programming" class="level3">
|
||
<h3 class="anchored" data-anchor-id="jupyter-notebooks-und-literate-programming">Jupyter Notebooks und literate programming</h3>
|
||
<p>Ein mächtiges Werkzeug – gerade in datengetriebenen Geisteswissenschaften – sind <strong>Jupyter Notebooks</strong> bzw. R Markdown Notebooks <span class="citation" data-cites="maria2019jupyter">(Kluyver u. a. 2016)</span>. Diese erlauben es, <em>ausführbaren Code mit erklärendem Text und Visualisierungen</em> in einem Dokument zu vereinen. Für Dokumentationszwecke können Notebooks zweierlei leisten: (1) als <strong>Tutorials/Beispiel-Workflows</strong>, die Nutzer interaktiv nachvollziehen können, und (2) als <strong>Reproduzierbarkeits-Dokumentation</strong> für analytische Prozesse. Wenn Ihre Forschungssoftware z. B. eine Bibliothek ist, könnten Sie ein Notebook bereitstellen, das einen typischen Anwendungsfall durchspielt (inklusive Daten-Loading, Aufruf der Funktionen, Darstellung der Ergebnisse).</p>
|
||
<p>Notebooks senken die Hürde, weil Nutzer direkt experimentieren können, und fördern transparente Forschung, da Code, Ergebnisse und Beschreibung zusammenfließen. Sie sind daher sinnvoll, <strong>wenn der Hauptanwendungsfall die Durchführung von Analysen oder Datenverarbeitungen ist</strong>, die man Schritt für Schritt demonstrieren kann.</p>
|
||
<div class="callout callout-style-default callout-warning callout-titled">
|
||
<div class="callout-header d-flex align-content-center">
|
||
<div class="callout-icon-container">
|
||
<i class="callout-icon"></i>
|
||
</div>
|
||
<div class="callout-title-container flex-fill">
|
||
Warnung
|
||
</div>
|
||
</div>
|
||
<div class="callout-body-container callout-body">
|
||
<p>Notebooks erfordern allerdings eine lauffähige Umgebung – das heißt, Sie müssen darauf achten, dass alle Abhängigkeiten im Notebook deklariert sind und die Daten zugänglich sind. Es hat sich gezeigt, dass Notebooks aus Publikationen oft nicht ohne Weiteres laufen, weil Pfade, Datenquellen oder spezielle Umgebungen fehlen. Deshalb: Wenn Sie Notebooks als Doku nutzen, stellen Sie sicher, dass sie <em>leicht ausführbar</em> sind (z. B. durch Bereitstellen von Umgebungsdateien wie <code>environment.yml</code> oder Dockerfiles, kleinen Beispieldatensätzen und klaren Anweisungen im Notebook). Ggf. kann man Notebooks auch in reine Markdown/HTML exportieren und dem Repo beilegen, damit zumindest statisch die Inhalte einsehbar sind.</p>
|
||
</div>
|
||
</div>
|
||
<p><strong>Wann sind Notebooks verpflichtend?</strong> – Nie im strengen Sinne, aber sie sind quasi Goldstandard, um wissenschaftliche Analysen nachvollziehbar zu machen. In Projekten, wo es um Data Science Workflows oder interaktive Exploration geht, sollten Notebooks stark erwogen werden, während für ein reines Tool/Script eine gut geschriebene README mit Beispielausgabe ausreichend sein kann.</p>
|
||
</section>
|
||
<section id="sphinxmkdocsdoxygen-statische-dokumentationswebseiten" class="level3 page-columns page-full">
|
||
<h3 class="anchored" data-anchor-id="sphinxmkdocsdoxygen-statische-dokumentationswebseiten">Sphinx/MkDocs/Doxygen (statische Dokumentationswebseiten)</h3>
|
||
<div class="page-columns page-full"><p>Für umfangreichere Projekte oder solche mit eigener Website kann es sinnvoll sein, eine <strong>Dokumentationswebsite</strong> zu generieren. Tools wie <em>Sphinx</em> (zusammen mit ReadTheDocs für Hosting) oder <em>MkDocs</em> erlauben es, aus Markdown/reStructuredText-Dateien einen ansprechend formatierten HTML-Dokumentationssatz zu bauen. Der Vorteil ist, dass man eine durchsuchbare, verlinkte Doku bekommt, oft mit schönem Layout und zusätzlicher Navigation. Mit <em>Continuous Integration</em> lassen sich diese Seiten bei jedem Git-Push automatisch aktualisieren. Für die Nachhaltigkeit (ENDINGS-Prinzip) ist wichtig, dass diese Webseiten statisch sind – d.h. sie funktionieren ohne Server-Backends und bleiben auch offline nutzbar. Sphinx erfüllt dies, indem es reine HTML-Seiten erzeugt. Solche Tools sind <strong>sinnvoll, wenn die Dokumentation sehr groß oder öffentlich weit verbreitet</strong> ist – z. B. wenn Ihre Software von vielen genutzt wird und Sie ein professionelles Auftreten wünschen, oder wenn Sie die Doku als PDF veröffentlichen möchten. </p><div class="no-row-height column-margin column-container"><span class="margin-aside">In kleinen DH-Projekten ist es oft nicht nötig, extra eine Webseite zu hosten; dennoch kann Sphinx auch lokal HTML/PDF erzeugen, was man dem Repo beilegen kann.</span></div></div>
|
||
<p><strong>Verpflichtend</strong> ist so ein Tool selten, höchstens wenn Förderprogramme oder Journals ein dokumentationsseitiges HTML-Manual verlangen. Wenn Sie jedoch planen, Ihre Software z. B. über Jahre zu pflegen und ggf. einem Journal wie JOSS vorzustellen, dann erwartet die Community meist, dass zumindest eine Sphinx/Doxygen-Doku für die API existiert. Als Daumenregel: ab einer Codebasis > einige tausend Zeilen oder > 5 Module lohnt es sich, eine generierte Dokumentation bereitzustellen, um den Überblick zu behalten.</p>
|
||
</section>
|
||
<section id="in-code-hilfefunktionen-und-cl-interface-doku" class="level3">
|
||
<h3 class="anchored" data-anchor-id="in-code-hilfefunktionen-und-cl-interface-doku">In-Code Hilfefunktionen und CL-Interface Doku</h3>
|
||
<p>Falls Ihre Software ein <strong>Command-Line Interface (CLI)</strong> hat, stellen Sie sicher, dass eine eingebaute Hilfe vorhanden ist (z. B. Ausgabe bei <code>--help</code>). Viele Nutzer greifen zunächst darauf zurück. Dieses Hilfemenü sollte kurz erläutern, welche Subkommandos oder Optionen existieren. Moderne CLI-Frameworks generieren solche Hilfen oft automatisch aus Ihrem Code (z. B. Click oder argparse in Python erzeugen <code>--help</code>-Texte). Nutzen Sie das, um konsistente Infos zu garantieren.</p>
|
||
<p>Für <strong>GUI-Anwendungen</strong> sollten Tooltips, Hilfetexte in der Oberfläche oder zumindest ein kleiner <em>Help</em>-Abschnitt im Handbuch vorhanden sein. Diese eingebetteten Hilfen ersetzen keine ausführliche Dokumentation, aber sie senken die Schwelle für alltägliche Fragen.</p>
|
||
</section>
|
||
<section id="versionskontrolle-und-kontinuierliche-dokumentationspflege" class="level3 page-columns page-full">
|
||
<h3 class="anchored" data-anchor-id="versionskontrolle-und-kontinuierliche-dokumentationspflege">Versionskontrolle und kontinuierliche Dokumentationspflege</h3>
|
||
<div class="page-columns page-full"><p>Eine Form der <em>Teil-Automatisierung</em> ist es, die Dokumentation an den Entwicklungs-Workflow zu koppeln. So sollte die Dokumentation im selben Versionskontrollsystem (Git) liegen wie der Code, damit Änderungen synchron nachverfolgt werden. Es empfiehlt sich, bei jedem größeren Code-Update zu prüfen, ob die Doku noch stimmt (das kann man sich z. B. als Punkt in Pull-Request-Reviews notieren oder per Issue-Template abfragen). Für Projekte mit Continuous Integration (CI) kann man sogar automatisierte Checks einrichten, die z. B. prüfen, ob die Doku gebaut werden kann oder ob Docstrings fehlen. Einige CI-Skripte generieren bei jedem Commit eine frische Doku (z. B. mittels Sphinx) und veröffentlichen sie – so ist garantiert, dass <em>die aktuelle Codeversion immer eine aktuelle Doku hat</em>. </p><div class="no-row-height column-margin column-container"><span class="margin-aside">Dieses Level an Automation ist für kleine Projekte evtl. zu viel, aber das <strong>Prinzip “Dokumentation versionieren”</strong> ist allgemeingültig, um die Entwicklungshistorie konsistent zu halten.</span></div></div>
|
||
</section>
|
||
<section id="spezialfälle" class="level3">
|
||
<h3 class="anchored" data-anchor-id="spezialfälle">Spezialfälle</h3>
|
||
<p>In bestimmten Fällen gibt es weitere Werkzeuge: z. B. <strong>Doxygen</strong> für automatisierte Code-Diagramme und Querverweise (gerne in C++-Projekten genutzt), oder <strong>Swagger/OpenAPI</strong> für automatische Dokumentation von Web-APIs. Wenn Ihre Forschungssoftware z. B. einen Webservice anbietet, kann Swagger eine interaktive API-Doku erzeugen. Ebenso können <strong>Literatur-Manager</strong> wie Manubot oder RMarkdown Bücher helfen, Code und Text zu integrieren (aber das geht über das hinaus, was die meisten DH-Projekte benötigen). Erwähnenswert ist noch <strong>Jupyter Book</strong> oder R <strong>Bookdown</strong>, womit man umfangreiche narrative Dokumentationen (inkl. Code) als Website/Book erstellen kann – nützlich, falls Ihre Dokumentation eher ein ausführlicher Lehrtext werden soll (z. B. wenn die Software einen ganzen methodischen Ansatz dokumentiert). Für den hier anvisierten Zweck (knackiger Doku-Katalog) sind solche Tools meist zu schwergewichtig.</p>
|
||
</section>
|
||
<section id="wann-ist-was-verpflichtend" class="level3">
|
||
<h3 class="anchored" data-anchor-id="wann-ist-was-verpflichtend">Wann ist was verpflichtend</h3>
|
||
<p>Es gibt kein universelles Muss, außer: <strong>Irgendeine Form der Doku ist Pflicht</strong>. Ob Sie nun per Hand Markdown schreiben oder Sphinx einsetzen, hängt von Kontext und Projektgröße ab. Allgemein gilt: Verwenden Sie Automatisierung wo immer möglich, <em>um sich zu entlasten</em>, aber vermeiden Sie Overhead durch Tools, die Sie nicht brauchen. Ein einzelnes historisches Analyse-Skript braucht kein Doxygen; ein komplexes DH-Toolkit mit API sollte hingegen Doxygen oder Sphinx nutzen, damit die Nutzer nicht den Code lesen müssen, um Funktionen zu verstehen. Denken Sie daran: <em>“Die beste Dokumentation ist die, die sich selbst schreibt.”</em> – dieses Motto aus der Literatur spielt darauf an, dass wir Tools nutzen sollen, die uns Schreibarbeit abnehmen. Perfekt autonom schreibt sich die Dokumentation zwar nie, aber moderne Werkzeuge können Routineaufgaben (z. B. Inhaltsverzeichnisse, Funktionsreferenzen, Formatierung) automatisieren. Dadurch bleibt Ihnen mehr Zeit für das inhaltliche Fine-Tuning der Texte.</p>
|
||
</section>
|
||
</section>
|
||
<section id="best-practices-vorlagen-und-checklisten" class="level2">
|
||
<h2 class="anchored" data-anchor-id="best-practices-vorlagen-und-checklisten">Best Practices, Vorlagen und Checklisten</h2>
|
||
<p>Um zu entscheiden, <em>was</em> dokumentiert wird (und was nicht), helfen etablierte <strong>Best Practices</strong> sowie Vorlagen aus der Community. Im Folgenden sind einige bewährte Richtlinien zusammengefasst, untermauert von Quellen, die bei der Priorisierung der Dokumentationsinhalte helfen:</p>
|
||
<section id="orientierung-an-nutzerbedürfnissen" class="level3">
|
||
<h3 class="anchored" data-anchor-id="orientierung-an-nutzerbedürfnissen">Orientierung an Nutzerbedürfnissen</h3>
|
||
<p>Stellen Sie sich beim Schreiben der Doku die verschiedenen <em>Nutzerrollen</em> vor: <strong>“Zukünftiges Ich”</strong>, <strong>Kolleg*innen</strong>, <strong>Fachforscher anderer Disziplin</strong> und ggf. <strong>Software-Entwickler, die den Code erweitern</strong>. Jede dieser Gruppen möchte bestimmte Dinge wissen. <em>Forscher*innen</em> fragen: <em>Was kann das Tool? Wie benutze ich es? In welchem Kontext steht es?</em>. <em>Entwickler*innen</em> fragen: <em>Wie kann ich beitragen? Wie funktioniert es unter der Haube?</em>. Priorisieren Sie zunächst die erstgenannten (Anwender) – deshalb Fokus auf Zweck, Nutzung und Ergebnisse in der Hauptdoku. Detailinfos für Entwickler (z. B. Code-Struktur, To-do-Liste) können separat oder später ergänzt werden. Halten Sie sich stets vor Augen: <strong>Dokumentation ist primär für Menschen</strong> (nicht für Maschinen), daher schreiben Sie klar und vermeiden Sie unnötigen Jargon. <em>Selbst wenn der Code “für sich spricht”</em>, denken Sie daran, dass klare Erläuterungen später viel Zeit sparen.</p>
|
||
</section>
|
||
<section id="checkliste-für-die-mindest-dokumentation" class="level3">
|
||
<h3 class="anchored" data-anchor-id="checkliste-für-die-mindest-dokumentation">Checkliste für die Mindest-Dokumentation</h3>
|
||
<p>Die folgenden Punkte fassen zusammen, was eine gute Dokumentation mindestens enthalten sollte. Sie können auch als <strong>Qualitäts-Checkliste</strong> dienen, um Ihre Dokumentation zu überprüfen:</p>
|
||
<ol type="1">
|
||
<li><strong>Zielklärung:</strong> Ist der Zweck der Software klar benannt und der wissenschaftliche <em>Need</em> begründet? (Falls nein, ergänzen: <em>Warum existiert dieses Tool?</em>)</li>
|
||
<li><strong>Installation & Voraussetzungen:</strong> Sind alle Schritte, um die Software lauffähig zu machen, dokumentiert (inkl. Dependencies, evtl. mit Installationsbefehlen)? Ist ersichtlich, welche Umgebung nötig ist (OS, Hardware)?</li>
|
||
<li><strong>Grundlegende Nutzung:</strong> Gibt es eine Anleitung oder Beispiele, wie man die Software verwendet (Eingabe -> Ausgaben)? Ist mindestens ein typischer Workflow beschrieben, idealerweise mit Beispielinput und -output?</li>
|
||
<li><strong>Optionen & Schnittstellen:</strong> Falls relevant – sind alle wichtigen Funktionen, Befehlsoptionen oder API-Methoden dokumentiert? (Nicht unbedingt jede intern, aber alles, was ein Nutzer aufrufen könnte). Für APIs: Sind Parameter und Rückgaben erläutert?</li>
|
||
<li><strong>Validierung & Einschränkungen:</strong> Werden Annahmen und Grenzen der Software genannt? Weiß ein<em>e Nutzer</em>in, welche Fälle nicht abgedeckt sind oder worauf zu achten ist (z. B. Datenqualität, maximale Größen)? Transparenz hier verhindert Frustration.</li>
|
||
<li><strong>Hintergrund & Referenzen:</strong> Sind die wichtigsten konzeptionellen Hintergründe oder Referenzen angegeben? (Z. B. theoretische Grundlagen, Algorithmen, Literaturverweise). Das muss kein Essay sein, aber ein paar Sätze + Referenzen schaffen Vertrauen in die wissenschaftliche Fundierung.</li>
|
||
<li><strong>Kontakt & Weiterführung:</strong> Ist angegeben, wie man Hilfe bekommt oder Fehler melden kann (Issue-Tracker, E-Mail)? Gibt es Hinweise für Beiträge (falls erwünscht) oder zumindest die Information, wer die Autor*innen sind?</li>
|
||
<li><strong>Rechtliches & Zitation:</strong> Liegt die Lizenz bei und wird sie genannt? Sind Infos zum Zitieren der Software vorhanden (z. B. “Bitte zitieren Sie DOI XYZ”)? Das stellt sicher, dass die Software nachnutzbar <em>und</em> akademisch kreditiert wird.</li>
|
||
<li><strong>Aktualität & Version:</strong> Entspricht die Dokumentation der aktuellen Softwareversion? (Check: Versionsnummern, Datumsangaben). Veraltete Doku kann schlimmer sein als keine – planen Sie also ein, die Doku mit jedem Release kurz zu überprüfen.</li>
|
||
<li><strong>Konsistenz & Stil:</strong> Wird ein einheitlicher Ton und Stil durchgehalten? (z. B. durchgehende Verwendung gleicher Begriffe für Konzepte, Sprache entweder Deutsch oder Englisch einheitlich je nach Zielgruppe). Kleinliche Fehler (Tippfehler, kaputte Links) sind auszumerzen, da sie Nutzer abschrecken.</li>
|
||
</ol>
|
||
<p>Diese Checkliste kann vor einem “Release” der Software durchgegangen werden, ähnlich einem Review-Prozess (vgl. JOSS Review-Kriterien, die viele dieser Punkte abdecken). Sie hilft zu entscheiden, was noch dokumentiert werden muss und was eventuell weggelassen werden kann. <strong>Alles, was für die obigen Punkte nicht relevant ist, kann man tendenziell aus der Hauptdokumentation herauslassen.</strong> Beispielsweise interne Code-Refaktorierungsdetails oder historische Anekdoten zur Entwicklung gehören eher ins interne Changelog oder in Blog-Posts, nicht in die Nutzerdokumentation.</p>
|
||
</section>
|
||
<section id="positiv--und-negativbeispiele-studieren" class="level3">
|
||
<h3 class="anchored" data-anchor-id="positiv--und-negativbeispiele-studieren">Positiv- und Negativbeispiele studieren</h3>
|
||
<p>Ein guter Weg, die eigene Dokumentation zu verbessern, ist ein Blick auf Projekte mit exzellenter Doku. In der <em>Journal of Open Source Software (JOSS)</em> oder <em>Journal of Open Research Software (JORS)</em> werden oft Softwareartikel veröffentlicht, bei denen die zugehörigen Repositorien vorbildliche READMEs und Wikis haben. Diese können als Vorlage dienen. Achten Sie darauf, wie diese Projekte ihre README strukturieren, welche Abschnitte vorhanden sind und welche nicht. Viele erfolgreiche Projekte haben z. B. eine ähnliche Reihenfolge: Introduction, Installation, Usage, Contributing, License, Citation – ein Muster, das sich bewährt hat. Ebenso gibt es von Initiativen wie der Software Sustainability Institute Blogposts mit Best Practices und sogar Vorlagen (Templates) für Dokumentation. Nutzen Sie solche Ressourcen; sie ersparen einem das Rad neu zu erfinden. Allerdings: Adaptieren Sie sie auf Ihre Bedürfnisse – nicht jede Vorlage passt 1:1.</p>
|
||
</section>
|
||
<section id="prinzipien-fair-und-endings" class="level3">
|
||
<h3 class="anchored" data-anchor-id="prinzipien-fair-und-endings">Prinzipien: FAIR und ENDINGS</h3>
|
||
<p>Beachten Sie, dass dieser Anforderungskatalog in Einklang mit den Prinzipien des <strong>Research Software Engineering</strong> und den <strong>ENDINGS-Prinzipien</strong> steht. Gutes Research Software Engineering fördert u.a. <em>Nachhaltigkeit, Offenheit und Reproduzierbarkeit</em> in der Softwareentwicklung. Dementsprechend legt unsere Dokumentations-Checkliste Wert auf Reproduzierbarkeit (Installation, Daten, Beispiele), Offenheit (Lizenz, offene Formate) und Nachhaltigkeit (Versionierung, Langlebigkeit der Doku). Die ENDINGS-Prinzipien für digitale Projekte betonen insbesondere die Bedeutung von Dokumentation für Datenstrukturen, offenen Lizenzen, statischen Outputs und Zitierbarkeit. Unsere Empfehlungen, etwa ein statisches Markdown-README beizulegen, die Datenmodell-Doku nicht auszulagern oder Zitationsangaben zu machen, setzen genau diese Vorgaben um. Indem Sie also diesem Anforderungskatalog folgen, berücksichtigen Sie automatisch wichtige anerkannte Prinzipien für gute wissenschaftliche Softwarepraxis.</p>
|
||
</section>
|
||
<section id="kontinuierliche-verbesserung-und-feedback" class="level3">
|
||
<h3 class="anchored" data-anchor-id="kontinuierliche-verbesserung-und-feedback">Kontinuierliche Verbesserung und Feedback</h3>
|
||
<p>Dokumentation ist kein einmaliges Ereignis, sondern ein fortlaufender Prozess. Best Practice ist, früh Feedback von Testnutzer*innen oder Kolleg*innen einzuholen: Lassen Sie jemanden die Anleitung befolgen und hören Sie auf Stolpersteine. Oft zeigen sich Lücken erst im Praxistest (“Ich wusste nicht, was ich nach Schritt X tun soll” etc.). Planen Sie Zeiten ein, die Dokumentation nachzuführen, insbesondere wenn sich die Software ändert. Ein lebendiges Projekt wird vielleicht Release für Release die Dokumentation erweitern (evtl. neue Tutorials, neue Module dokumentieren). Nutzen Sie auch <em>Issues</em> für Dokumentation: Wenn Nutzer Fragen stellen, überlegen Sie, ob die Antwort in die offizielle Doku übernommen werden sollte. So wächst die Dokumentation organisch entlang der tatsächlichen Bedürfnisse.</p>
|
||
</section>
|
||
<section id="zusammenfassung-best-practices" class="level3">
|
||
<h3 class="anchored" data-anchor-id="zusammenfassung-best-practices">Zusammenfassung Best Practices</h3>
|
||
<p>Zusammenfassend helfen die genannten Best Practices dabei, die Dokumentation <strong>zielgerichtet</strong> zu gestalten: Dokumentiert wird, was dem Verständnis und der Nutzung dient; weggelassen wird, was überflüssig oder selbstverständlich ist. Eine gute Dokumentation <em>erzählt eine klare Geschichte</em> über die Software, anstatt den Leser mit irrelevanten Details zu verlieren. Mit den richtigen Werkzeugen und Prinzipien an der Hand kann selbst unter Zeitdruck eine qualitativ hochwertige Dokumentation entstehen – zur Freude aller, die mit der Forschungssoftware arbeiten möchten.</p>
|
||
</section>
|
||
</section>
|
||
<section id="fazit" class="level2 page-columns page-full">
|
||
<h2 class="anchored" data-anchor-id="fazit">Fazit</h2>
|
||
<p>Die hier präsentierten Anforderungen und Empfehlungen bieten einen <strong>Leitfaden für die Dokumentation von Forschungssoftware</strong> in den Digital Humanities. Sie sind darauf ausgerichtet, mit überschaubarem Aufwand maximale <strong>Nachvollziehbarkeit, Langlebigkeit und Wiederverwendbarkeit</strong> zu erreichen. Indem zentrale Inhalte (Ziele, Inputs/Outputs, Hintergrund, etc.) klar dokumentiert, ein nutzerfreundliches Format (README im Repo) gewählt, der Umfang fokussiert gehalten und hilfreiche Tools eingesetzt werden, kann die Dokumentation zur Stärke eines Projekts werden statt einem lästigen Anhängsel.</p>
|
||
<p>Wissenschaftlich fundierte Best Practices – von <em>Ten Simple Rules for Documenting Scientific Software</em> bis zu den <em>ENDINGS-Principles</em> – untermauern diesen Katalog. Die Umsetzung dieser Richtlinien wird dazu beitragen, dass Forschungssoftware aus den Geisteswissenschaften nicht nur kurzfristig von ihren Autor*innen genutzt wird, sondern langfristig von Dritten verstanden, validiert und weiterentwickelt werden kann. So schließt sich der Kreis zwischen guter <strong>Softwareentwicklung</strong> und guter <strong>Wissenschaft</strong>: Dokumentation ist das Bindeglied, das Code und Erkenntnis transparent verbindet. In der Praxis bedeutet dies zwar zusätzliche Arbeitsschritte, doch wie die Erfahrung zeigt, zahlen sich diese in Form von <em>Zeiteinsparung bei Nutzern, höherer Zitierbarkeit und größerer Wirkung</em> der Software aus. Mit diesem Anforderungskatalog sind Forschende gut gerüstet, um ihre Softwareprojekte dokumentationstechnisch auf ein solides Fundament zu stellen – trotz knapper Zeit und ohne Informatikabschluss. Denn am Ende gilt: <strong>Gut dokumentierte Forschungscode ist nachhaltige Forschung</strong>.</p>
|
||
<section id="tabellarische-übersicht-der-dokumentations-bestandteile" class="level3 page-columns page-full">
|
||
<h3 class="anchored" data-anchor-id="tabellarische-übersicht-der-dokumentations-bestandteile">Tabellarische Übersicht der Dokumentations-Bestandteile</h3>
|
||
<div class="column-page-right">
|
||
<table class="caption-top table">
|
||
<caption><em>Empfohlene Dokumentationselemente, Inhalte und Umfang.</em> Diese Übersicht kann als Vorlage dienen, welche Komponenten ein Dokumentationspaket enthalten sollte. Je nach Projekt können einige Elemente wegfallen oder kombiniert werden – entscheidend ist, dass die Kerninformationen (siehe oben) nicht fehlen.</caption>
|
||
<colgroup>
|
||
<col style="width: 12%">
|
||
<col style="width: 43%">
|
||
<col style="width: 31%">
|
||
<col style="width: 13%">
|
||
</colgroup>
|
||
<thead>
|
||
<tr class="header">
|
||
<th><strong>Dokuelement</strong></th>
|
||
<th><strong>Inhalt/Purpose</strong></th>
|
||
<th><strong>Format/Ort</strong></th>
|
||
<th><strong>Umfang</strong></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="odd">
|
||
<td><strong>README (Hauptdoku)</strong></td>
|
||
<td>Zweck der Software; Kurzbeschreibung; Installationsanleitung; einfaches Nutzungsbeispiel; Lizenz- und Kontaktinfo</td>
|
||
<td>Markdown im Root des Repos (statisch versioniert)</td>
|
||
<td>1–2 Seiten</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td><strong>Eingabe/Ausgabe-Guide</strong></td>
|
||
<td>Beschreibung der erwarteten Inputs (Datenformat, Parameter) und generierten Outputs (Dateien, Berichte) inkl. Beispielen</td>
|
||
<td>Teil der README oder separate Datei (z.B. USAGE.md)</td>
|
||
<td>1 Seite (mit Beispielen)</td>
|
||
</tr>
|
||
<tr class="odd">
|
||
<td><strong>Wissenschaftlicher Hintergrund</strong></td>
|
||
<td>Erläuterung der Methode, Theorie, Algorithmen; Verweise auf Literatur</td>
|
||
<td>README-Abschnitt “Hintergrund” oder separate Doku (BACKGROUND.md)</td>
|
||
<td>0.5–1 Seite (plus Referenzen)</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td><strong>Bekannte Limitationen</strong></td>
|
||
<td>Auflistung von Einschränkungen, Annahmen, bekannten Problemen; ggf. Workarounds</td>
|
||
<td>README-Abschnitt “Limitations” oder FAQ.md</td>
|
||
<td>0.5 Seite</td>
|
||
</tr>
|
||
<tr class="odd">
|
||
<td><strong>Beispiel-Workflow (Tutorial)</strong></td>
|
||
<td>Schritt-für-Schritt Anleitung mit einem realistischen Anwendungsfall (ggf. mit Code und Screenshot)</td>
|
||
<td>Jupyter Notebook (<code>.ipynb</code>) im Repo <code>examples/</code> Ordner oder Markdown in docs/</td>
|
||
<td>1–3 Seiten / entsprechend Zellen</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td><strong>API-Referenz</strong></td>
|
||
<td>Technische Dokumentation von Funktionen/Klassen für Entwickler*innen</td>
|
||
<td>Automatisch generiert aus Docstrings (z.B. Sphinx in <code>docs/</code> Ordner, HTML/PDF Ausgabe)</td>
|
||
<td>Je nach Codegröße (ggf. umfangreich)</td>
|
||
</tr>
|
||
<tr class="odd">
|
||
<td><strong>CONTRIBUTING</strong></td>
|
||
<td>Anleitung für Beitragswillige: Code Style, Workflow, Tests, Kontakt</td>
|
||
<td>CONTRIBUTING.md im Repo</td>
|
||
<td>0.5–1 Seite</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td><strong>LICENSE</strong> / <strong>CITATION</strong></td>
|
||
<td>Rechtliche Infos (Lizenztext); Zitationsleitfaden (Bevorzugte Zitierweise, DOI)</td>
|
||
<td>Jeweils eigene Datei im Repo (Plain Text/Markdown)</td>
|
||
<td>Kurz (Standardtext bzw. Referenz)</td>
|
||
</tr>
|
||
<tr class="odd">
|
||
<td><strong>Release-Information</strong></td>
|
||
<td>Versionshinweise, Änderungsprotokoll (Changelog)</td>
|
||
<td>CHANGELOG.md oder Releases auf GitHub</td>
|
||
<td>fortlaufend pro Version (Stichpunkte)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</section>
|
||
<section id="schlusswort" class="level3">
|
||
<h3 class="anchored" data-anchor-id="schlusswort">Schlusswort</h3>
|
||
<p>Mit einer solchen Struktur und Herangehensweise lässt sich auch in einem kleinen Forschungsteam eine professionelle Dokumentation erzielen, die den Prinzipien von Open Science und nachhaltiger Softwareentwicklung gerecht wird. Die investierte Mühe wird durch Zeitgewinn bei Wiederverwendung und Erweiterung der Software mehr als aufgewogen. So wird die Forschungssoftware nicht zum einmaligen “Nebenprodukt”, sondern zu einem robusten, teilbaren Ergebnis wissenschaftlicher Arbeit.</p>
|
||
</section>
|
||
</section>
|
||
|
||
|
||
<div id="quarto-appendix" class="default"><section id="methodik-llms-als-autoren" class="level2 appendix"><h2 class="anchored quarto-appendix-heading">Methodik / LLMs als ‘Autoren’</h2><div class="quarto-appendix-contents">
|
||
|
||
<p>Erstellt wurde der initial draft mittels Websuche und “Deep-Research” von <code>gpt-4.5 (preview)</code>. Abschließendes Korrekturlesen/inhaltliche Prüfung/Layouting durch Nicole Dresselhaus.</p>
|
||
|
||
|
||
|
||
</div></section><section class="quarto-appendix-contents" id="quarto-bibliography"><h2 class="anchored quarto-appendix-heading">Literatur</h2><div id="refs" class="references csl-bib-body hanging-indent" data-entry-spacing="0">
|
||
<div id="ref-endings2020principles" class="csl-entry">
|
||
Endings Project. 2020. <span>„Endings Principles for Digital Longevity“</span>. <a href="https://endings.uvic.ca/principles.html">https://endings.uvic.ca/principles.html</a>.
|
||
</div>
|
||
<div id="ref-katz2021open" class="csl-entry">
|
||
Katz, Daniel S, Kyle E Niemeyer, und Arfon M Smith. 2021. <span>„The Journal of Open Source Software (JOSS)“</span>. <em>PeerJ Computer Science</em> 7: e432.
|
||
</div>
|
||
<div id="ref-maria2019jupyter" class="csl-entry">
|
||
Kluyver, Thomas, Benjamin Ragan-Kelley, Fernando Pérez, Brian Granger, Matthias Bussonnier, Jonathan Frederic, Kyle Kelley, u. a. 2016. <span>„Jupyter notebooks—a publishing format for reproducible computational workflows“</span>. <em>Positioning and Power in Academic Publishing: Players, Agents and Agendas</em> 20: 87–90.
|
||
</div>
|
||
<div id="ref-prlic2012ten" class="csl-entry">
|
||
Prlić, Andreas, und James B Procter. 2012. <span>„Ten simple rules for documenting scientific software“</span>. <em>PLoS Computational Biology</em> 8 (12): e1002802.
|
||
</div>
|
||
<div id="ref-smith2016software" class="csl-entry">
|
||
Smith, Arfon M, Daniel S Katz, Kyle E Niemeyer, FORCE11 Software Citation Working Group, u. a. 2016. <span>„Software citation principles“</span>. <em>PeerJ Computer Science</em> 2: e86.
|
||
</div>
|
||
<div id="ref-wilson2017good" class="csl-entry">
|
||
Wilson, Greg, Jennifer Bryan, Karen Cranston, Justin Kitzes, Lex Nederbragt, und Tracy K Teal. 2017. <span>„Good enough practices in scientific computing“</span>. <em>PLoS computational biology</em> 13 (6): e1005510.
|
||
</div>
|
||
</div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Zitat</h2><div><div class="quarto-appendix-secondary-label">Mit BibTeX zitieren:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@online{dresselhaus2025,
|
||
author = {Dresselhaus, Nicole and , GPT-4.5},
|
||
title = {Anforderungskatalog für die Dokumentation von
|
||
Forschungssoftware (Digital Humanities)},
|
||
date = {2025-05-08},
|
||
url = {https://drezil.de/Writing/documentation.html},
|
||
langid = {de},
|
||
abstract = {Diese Dokumentation fasst zusammen, welche
|
||
wissenschaftlichen Konzepte, Algorithmen und Theorien hinter der
|
||
Software stehen. Sie dient dazu, den Nutzer*innen zu helfen, die
|
||
theoretischen Grundlagen nachvollziehbar zu machen.}
|
||
}
|
||
</code></pre><div class="quarto-appendix-secondary-label">Bitte zitieren Sie diese Arbeit als:</div><div id="ref-dresselhaus2025" class="csl-entry quarto-appendix-citeas">
|
||
Dresselhaus, Nicole, and GPT-4.5. 2025. <span>“Anforderungskatalog für
|
||
die Dokumentation von Forschungssoftware (Digital Humanities).”</span>
|
||
May 8, 2025. <a href="https://drezil.de/Writing/documentation.html">https://drezil.de/Writing/documentation.html</a>.
|
||
</div></div></section></div> ]]></description>
|
||
<category>Article</category>
|
||
<category>Best Practices</category>
|
||
<guid>https://drezil.de/Writing/documentation.html</guid>
|
||
<pubDate>Wed, 07 May 2025 22:00:00 GMT</pubDate>
|
||
<media:content url="https://drezil.de/thumbs/writing_documentation.png" medium="image" type="image/png" height="83" width="144"/>
|
||
</item>
|
||
<item>
|
||
<title>Case Study: Local LLM-Based NER with n8n and Ollama</title>
|
||
<link>https://drezil.de/Writing/ner4all-case-study.html</link>
|
||
<description><![CDATA[
|
||
|
||
|
||
|
||
|
||
|
||
<section id="background-llm-based-ner-method-overview" class="level2">
|
||
<h2 class="anchored" data-anchor-id="background-llm-based-ner-method-overview">Background: LLM-Based NER Method Overview</h2>
|
||
<p>The referenced study introduced a prompt-driven approach to NER, reframing it “from a purely linguistic task into a humanities-focused task”. Instead of training a specialized NER model for each corpus, the method leverages the fact that large pretrained LLMs already contain vast world knowledge and language understanding. The key idea is to <strong>provide the model with contextual definitions and instructions</strong> so it can recognize entities in context. Notably, the authors found that with proper prompts, a commercial LLM (ChatGPT-4) could achieve <strong>precision and recall on par with or better than</strong> state-of-the-art NER tools on a 1921 historical travel guide. This was achieved <strong>zero-shot</strong>, i.e. without any fine-tuning or additional training data beyond the prompt itself.</p>
|
||
<p><strong>Prompt Strategy:</strong> The success of this approach hinges on careful prompt engineering. The final prompt used in the paper had multiple components:</p>
|
||
<ul>
|
||
<li><strong>Persona & Context:</strong> A brief introduction framing the LLM as an <em>expert</em> reading a historical text, possibly including domain context (e.g. “This text is an early 20th-century travel guide; language is old-fashioned”). This primes the model with relevant background.</li>
|
||
<li><strong>Task Instructions:</strong> A clear description of the NER task, including the list of entity categories and how to mark them in text. For example: <em>“Identify all Person (PER), Location (LOC), and Organization (ORG) names in the text and mark each by enclosing it in tags.”</em></li>
|
||
<li><strong>Optional Examples:</strong> A few examples of sentences with correct tagged output (few-shot learning) to guide the model. Interestingly, the study found that zero-shot prompting often <strong>outperformed few-shot</strong> until ~16 examples were provided. Given the cost of preparing examples and limited prompt length, our implementation will focus on zero-shot usage for simplicity.</li>
|
||
<li><strong>Reiteration & Emphasis:</strong> The prompt repeated key instructions in different words and emphasized compliance (e.g. <em>“Make sure you follow the tagging format exactly for every example.”</em>). This redundancy helps the model adhere to instructions.</li>
|
||
<li><strong>Prompt Engineering Tricks:</strong> They included creative cues to improve accuracy, such as offering a “monetary reward for each correct classification” and the phrase <em>“Take a deep breath and think step by step.”</em>. These tricks, drawn from prior work, encouraged the model to be thorough and careful.</li>
|
||
<li><strong>Output Format:</strong> Crucially, the model was asked to <strong>repeat the original text exactly</strong> but insert tags around entity mentions. The authors settled on a format like <code><<PER ... /PER>></code> to tag people, <code><<LOC ... /LOC>></code> for locations, etc., covering each full entity span. This inline tagging format leveraged the model’s familiarity with XML/HTML syntax (from its training data) and largely eliminated problems like unclosed tags or extra spaces. By instructing the model <em>not to alter any other text</em>, they ensured the output could be easily compared to the input and parsed for entities.</li>
|
||
</ul>
|
||
<p><strong>Why Local LLMs?</strong> The original experiments used a proprietary API (ChatGPT-4). To make the method accessible to all (and avoid data governance issues of cloud APIs), we implement it with <strong>open-source LLMs running locally</strong>. Recent openly licensed models are rapidly improving and can handle such extraction tasks given the right prompt. Running everything locally also aligns with the paper’s goal of “democratizing access” to NER for diverse, low-resource texts – there are no API costs or internet needed, and data stays on local hardware for privacy.</p>
|
||
</section>
|
||
<section id="solution-architecture" class="level2">
|
||
<h2 class="anchored" data-anchor-id="solution-architecture">Solution Architecture</h2>
|
||
<p>Our solution consists of a <strong>workflow in n8n</strong> that orchestrates the NER process, and a <strong>local Ollama server</strong> that hosts the LLM for text analysis. The high-level workflow is as follows:</p>
|
||
<ol type="1">
|
||
<li><strong>Webhook Trigger (n8n):</strong> A user initiates the process by sending an HTTP request to n8n’s webhook with two inputs: (a) a simple text defining the entity categories of interest (for example, <code>"PER, ORG, LOC"</code>), and (b) the text to analyze (either included in the request or accessible via a provided file URL). This trigger node captures the input and starts the automation.</li>
|
||
<li><strong>Prompt Construction (n8n):</strong> The workflow builds a structured prompt for the LLM. Based on the webhook input, it prepares the system instructions listing each entity type and guidelines, then appends the user’s text. Essentially, n8n will merge the <em>entity definitions</em> into a pre-defined prompt template (the one derived from the paper’s method). This can be done using a <strong>Function node</strong> or an <strong>LLM Prompt node</strong> in n8n to ensure the text and instructions are combined correctly.</li>
|
||
<li><strong>LLM Inference (Ollama + LLM):</strong> n8n then passes the prompt to an <strong>Ollama Chat Model node</strong>, which communicates with the Ollama server’s API. The Ollama daemon hosts the selected 14B model on the local GPU and returns the model’s completion. In our case, the completion will be the original text with NER tags inserted around the entities (e.g. <code><<PER John Doe /PER>> went to <<LOC Berlin /LOC>> ...</code>). This step harnesses the A100 GPU to generate results quickly, using the chosen model’s weights locally.</li>
|
||
<li><strong>Output Processing (n8n):</strong> The tagged text output from the LLM can be handled in two ways. The simplest is to <strong>return the tagged text directly</strong> as the response to the webhook call – allowing the user to see their original text with all entities highlighted by tags. Alternatively, n8n can post-process the tags to extract a structured list of entities (e.g. a JSON array of <code class="sourceCode json"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">{</span><span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">"entity"</span><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"John Doe"</span><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">,</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">"type"</span><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"PER"</span><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">}</span></code> objects). This parsing can be done with a Regex or code node, but given our focus on correctness, we often trust the model’s tagging format to be consistent (the paper reported the format was reliably followed when instructed clearly). Finally, an <strong>HTTP Response</strong> node sends the results back to the user (or stores them), completing the workflow.</li>
|
||
</ol>
|
||
<p><strong>Workflow Structure:</strong> In n8n’s interface, the workflow might look like a sequence of connected nodes: <strong>Webhook → Function (build prompt) → AI Model (Ollama) → Webhook Response</strong>. If using n8n’s new AI Agent feature, some steps (like prompt templating) can be configured within the AI nodes themselves. The key is that the Ollama model node is configured to use the local server (usually at <code>http://127.0.0.1:11434</code> by default) and the specific model name. We assume the base pipeline (available on GitHub) already includes most of this structure – our task is to <strong>slot in the custom prompt and model configuration</strong> for the NER use case.</p>
|
||
</section>
|
||
<section id="setup-and-infrastructure-requirements" class="level2">
|
||
<h2 class="anchored" data-anchor-id="setup-and-infrastructure-requirements">Setup and Infrastructure Requirements</h2>
|
||
<p>To reproduce this solution, you will need a machine with an <strong>NVIDIA GPU</strong> and the following software components installed:</p>
|
||
<ul>
|
||
<li><p><strong>n8n (v1.</strong>x** or later)** – the workflow automation tool. You can install n8n via npm, Docker, or use the desktop app. For a server environment, Docker is convenient. For example, to run n8n with Docker:</p>
|
||
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb1-1"><span class="ex" style="color: null;
|
||
background-color: null;
|
||
font-style: inherit;">docker</span> run <span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;">-it</span> <span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;">--rm</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">\</span></span>
|
||
<span id="cb1-2"> <span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;">-p</span> 5678:5678 <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">\</span></span>
|
||
<span id="cb1-3"> <span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;">-v</span> ~/.n8n:/home/node/.n8n <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">\</span></span>
|
||
<span id="cb1-4"> n8nio/n8n:latest</span></code></pre></div>
|
||
<p>This exposes n8n on <code>http://localhost:5678</code> for the web interface. (If you use Docker and plan to connect to a host-running Ollama, start the container with <code>--network=host</code> to allow access to the Ollama API on localhost.)</p></li>
|
||
<li><p><strong>Ollama (v0.x*)</strong> – an LLM runtime that serves models via an HTTP API. Installing Ollama is straightforward: download the installer for your OS from the official site (Linux users can run the one-line script <code>curl -sSL https://ollama.com/install.sh | sh</code>). After installation, start the Ollama server (daemon) by running:</p>
|
||
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb2-1"><span class="ex" style="color: null;
|
||
background-color: null;
|
||
font-style: inherit;">ollama</span> serve</span></code></pre></div>
|
||
<p>This will launch the service listening on port 11434. You can verify it’s running by opening <code>http://localhost:11434</code> in a browser – it should respond with “Ollama is running”. <em>Note:</em> Ensure your system has recent NVIDIA drivers and CUDA support if using GPU. Ollama supports NVIDIA GPUs with compute capability ≥5.0 (the A100 is well above this). Use <code>nvidia-smi</code> to confirm your GPU is recognized. If everything is set up, Ollama will automatically use the GPU for model inference (falling back to CPU if none available).</p></li>
|
||
<li><p><strong>LLM Model (14B class):</strong> Finally, download at least one large language model to use for NER. You have a few options here, and you can “pull” them via Ollama’s CLI:</p>
|
||
<ul>
|
||
<li><p><em>DeepSeek-R1 14B:</em> A 14.8B-parameter model distilled from larger reasoning models (based on Qwen architecture). It’s optimized for reasoning tasks and compares to OpenAI’s models in quality. Pull it with:</p>
|
||
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb3-1"><span class="ex" style="color: null;
|
||
background-color: null;
|
||
font-style: inherit;">ollama</span> pull deepseek-r1:14b</span></code></pre></div>
|
||
<p>This downloads ~9 GB of data (the quantized weights). If you have a very strong GPU (e.g. A100 80GB), you could even try <code>deepseek-r1:70b</code> (~43 GB), but 14B is a good balance for our use-case. DeepSeek-R1 is licensed MIT and designed to run locally with no restrictions.</p></li>
|
||
<li><p><em>Cogito 14B:</em> A 14B “hybrid reasoning” model by Deep Cogito, known for excellent instruction-following and multilingual capability. Pull it with:</p>
|
||
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb4-1"><span class="ex" style="color: null;
|
||
background-color: null;
|
||
font-style: inherit;">ollama</span> pull cogito:14b</span></code></pre></div>
|
||
<p>Cogito-14B is also ~9 GB (quantized) and supports an extended context window up to <strong>128k tokens</strong> – which is extremely useful if you plan to analyze very long documents without chunking. It’s trained in 30+ languages and tuned to follow complex instructions, which can help in structured output tasks like ours.</p></li>
|
||
<li><p><em>Others:</em> Ollama offers many models (LLaMA 2 variants, Mistral, etc.). For instance, <code>ollama pull llama2:13b</code> would get a LLaMA-2 13B model. These can work, but for best results in NER with no fine-tuning, we suggest using one of the above well-instructed models. If your hardware is limited, you could try a 7-8B model (e.g., <code>deepseek-r1:7b</code> or <code>cogito:8b</code>), which download faster and use ~4–5 GB VRAM, at the cost of some accuracy. In CPU-only scenarios, even a 1.5B model is available – it will run very slowly and likely miss more entities, but it proves the pipeline can work on minimal hardware.</p></li>
|
||
</ul></li>
|
||
</ul>
|
||
<p><strong>Hardware Requirements:</strong> Our case assumes an NVIDIA A100 GPU (40 GB), which comfortably hosts a 14B model in memory and accelerates inference. In practice, any modern GPU with ≥10 GB memory can run a 13–14B model in 4-bit quantization. For example, an RTX 3090 or 4090 (24 GB) could handle it, and even smaller GPUs (or Apple Silicon with 16+ GB RAM) can run 7B models. Ensure you have sufficient <strong>system RAM</strong> as well (at least as much as the model size, plus overhead for n8n – 16 GB RAM is a safe minimum for 14B). Disk space of ~10 GB per model is needed. If using Docker for n8n, allocate CPU and memory generously to avoid bottlenecks when the LLM node processes large text.</p>
|
||
</section>
|
||
<section id="building-the-n8n-workflow" class="level2">
|
||
<h2 class="anchored" data-anchor-id="building-the-n8n-workflow">Building the n8n Workflow</h2>
|
||
<p>With the environment ready, we now construct the n8n workflow that ties everything together. We outline each component with instructions:</p>
|
||
<section id="webhook-input-for-entities-and-text" class="level3">
|
||
<h3 class="anchored" data-anchor-id="webhook-input-for-entities-and-text">1. Webhook Input for Entities and Text</h3>
|
||
<p>Start by creating a <strong>Webhook trigger</strong> node in n8n. This will provide a URL (endpoint) that you can send a request to. Configure it to accept a POST request containing the necessary inputs. For example, we expect the request JSON to look like:</p>
|
||
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode json code-with-copy"><code class="sourceCode json"><span id="cb5-1"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">{</span></span>
|
||
<span id="cb5-2"> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">"entities"</span><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"PER, ORG, LOC"</span><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">,</span></span>
|
||
<span id="cb5-3"> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">"text"</span><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"John Doe visited Berlin in 1921 and met with the Board of Acme Corp."</span></span>
|
||
<span id="cb5-4"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">}</span></span></code></pre></div>
|
||
<p>Here, <code>"entities"</code> is a simple comma-separated string of entity types (you could also accept an array or a more detailed schema; for simplicity we use the format used in the paper: PER for person, LOC for location, ORG for organization). The <code>"text"</code> field contains the content to analyze. In a real scenario, the text could be much longer or might be sent as a file. If it’s a file, one approach is to send it as form-data and use n8n’s <strong>Read Binary File</strong> + <strong>Move Binary Data</strong> nodes to get it into text form. Alternatively, send a URL in the JSON and use an HTTP Request node in the workflow to fetch the content. The key is that by the end of this step, we have the raw text and the list of entity labels available in the n8n workflow as variables.</p>
|
||
</section>
|
||
<section id="constructing-the-llm-prompt" class="level3">
|
||
<h3 class="anchored" data-anchor-id="constructing-the-llm-prompt">2. Constructing the LLM Prompt</h3>
|
||
<p>Next, add a node to build the prompt that will be fed to the LLM. You can use a <strong>Function</strong> node (JavaScript code) or the <strong>“Set” node</strong> to template a prompt string. We will create two pieces of prompt content: a <strong>system instruction</strong> (the role played by the system prompt in chat models) and the <strong>user message</strong> (which will contain the text to be processed).</p>
|
||
<p>According to the method, our <strong>system prompt</strong> should incorporate the following:</p>
|
||
<ul>
|
||
<li><strong>Persona/Context:</strong> e.g. <em>“You are a historian and archivist analyzing a historical document. The language may be old or have archaic spellings. You have extensive knowledge of people, places, and organizations relevant to the context.”</em> This establishes domain expertise in the model.</li>
|
||
<li><strong>Task Definition:</strong> e.g. <em>“Your task is to perform Named Entity Recognition. Identify all occurrences of the specified entity types in the given text and annotate them with the corresponding tags.”</em></li>
|
||
<li><strong>Entity Definitions:</strong> List the entity categories provided by the user, with a brief definition if needed. For example: <em>“The entity types are: PER (persons or fictional characters), ORG (organizations, companies, institutions), LOC (locations such as cities, countries, landmarks).”</em> If the user already provided definitions in the webhook, include those; otherwise a generic definition as shown is fine.</li>
|
||
<li><strong>Tagging Instructions:</strong> Clearly explain the tagging format. We adopt the format from the paper: each entity should be wrapped in <code><<TYPE ... /TYPE>></code>. So instruct: <em>“Enclose each entity in double angle brackets with its type label. For example: <<PER John Doe /PER>> for a person named John Doe. Do not alter any other text – only insert tags. Ensure every opening tag has a closing tag.”</em> Also mention that tags can nest or overlap if necessary (though that’s rare).</li>
|
||
<li><strong>Output Expectations:</strong> Emphasize that the output should be the <strong>exact original text, verbatim, with tags added</strong> and nothing else. For example: <em>“Repeat the input text exactly, adding the tags around the entities. Do not add explanations or remove any content. The output should look like the original text with markup.”</em> This is crucial to prevent the model from omitting or rephrasing text. The paper’s prompt literally had a line: “Repeat the given text exactly. Be very careful to ensure that nothing is added or removed apart from the annotations.”.</li>
|
||
<li><strong>Compliance & Thoughtfulness:</strong> We can borrow the trick of telling the model to take its time and be precise. For instance: <em>“Before answering, take a deep breath and think step by step. Make sure you find <strong>all</strong> entities. You will be rewarded for each correct tag.”</em> While the notion of reward is hypothetical, such phrasing has been observed to sharpen the model’s focus. This is optional but can be useful for complex texts.</li>
|
||
</ul>
|
||
<p>Once this system prompt is assembled as a single string, it will be sent as the system role content to the LLM. Now, for the <strong>user prompt</strong>, we simply supply the text to be analyzed. In many chat-based LLMs, the user message would contain the text on which the assistant should perform the task. We might prefix it with something like “Text to analyze:” for clarity, or just include the raw text. (Including a prefix is slightly safer to distinguish it from any instructions, but since the system prompt already set the task, the user message can be just the document text.)</p>
|
||
<p>In n8n, if using the <strong>Basic LLM Chain</strong> node, you can configure it to use a custom system prompt. For example, connect the Function/Set node output into the LLM node, and in the LLM node’s settings choose “Mode: Complete” or similar, then under <strong>System Instructions</strong> put an expression that references the constructed prompt text (e.g., <code>{ $json["prompt"] }</code> if the prompt was output to that field). The <strong>User Message</strong> can similarly be fed from the input text field (e.g., <code>{ $json["text"] }</code>). Essentially, we map our crafted instruction into the system role, and the actual content into the user role.</p>
|
||
</section>
|
||
<section id="configuring-the-local-llm-ollama-model-node" class="level3">
|
||
<h3 class="anchored" data-anchor-id="configuring-the-local-llm-ollama-model-node">3. Configuring the Local LLM (Ollama Model Node)</h3>
|
||
<p>Now configure the LLM node to use the <strong>Ollama</strong> backend and your downloaded model. n8n provides an “Ollama Chat Model” integration, which is a sub-node of the AI Agent system. In the n8n editor, add or open the LLM node (if using the AI Agent, this might be inside a larger agent node), and look for model selection. Select <strong>Ollama</strong> as the provider. You’ll need to set up a credential for Ollama API access – use <code>http://127.0.0.1:11434</code> as the host (instead of the default localhost, to avoid any IPv6 binding issues). No API key is needed since it’s local. Once connected, you should see a dropdown of available models (all the ones you pulled). Choose the 14B model you downloaded, e.g. <code>deepseek-r1:14b</code> or <code>cogito:14b</code>.</p>
|
||
<p>Double-check the <strong>parameters</strong> for generation. By default, Ollama models have their own preset for max tokens and temperature. For an extraction task, we want the model to stay <strong>focused and deterministic</strong>. It’s wise to set a relatively low temperature (e.g. 0.2) to reduce randomness, and a high max tokens so it can output the entire text with tags (set max tokens to at least the length of your input in tokens plus 10-20% for tags). If using Cogito with its 128k context, you can safely feed very long text; with other models (often ~4k context), ensure your text isn’t longer than the model’s context limit or use a model variant with extended context. If the model supports <strong>“tools” or functions</strong>, you won’t need those here – this is a single-shot prompt, not a multi-step agent requiring tool usage, so just the chat completion mode is sufficient.</p>
|
||
<p>At this point, when the workflow runs to this node, n8n will send the system and user messages to Ollama and wait for the response. The heavy lifting is done by the LLM on the GPU, which will generate the tagged text. On an A100, a 14B model can process a few thousand tokens of input and output in just a handful of seconds (exact time depends on the model and input size).</p>
|
||
</section>
|
||
<section id="returning-the-results" class="level3">
|
||
<h3 class="anchored" data-anchor-id="returning-the-results">4. Returning the Results</h3>
|
||
<p>After the LLM node, add a node to handle the output. If you want to present the <strong>tagged text</strong> directly, you can pass the LLM’s output to the final Webhook Response node (or if using the built-in n8n chat UI, you would see the answer in the chat). The tagged text will look something like:</p>
|
||
<pre class="plain"><code><<PER John Doe /PER>> visited <<LOC Berlin /LOC>> in 1921 and met with the Board
|
||
of <<ORG Acme Corp /ORG>>.</code></pre>
|
||
<p>This format highlights each identified entity. It is immediately human-readable with the tags, and trivial to post-process if needed. For example, one could use a regex like <code><<(\w+) (.*?) /\1>></code> to extract all <code>type</code> and <code>entity</code> pairs from the text. In n8n, a quick approach is to use a <strong>Function</strong> node to find all matches of that pattern in <code>item.json["data"]</code> (assuming the LLM output is in <code>data</code>). Then one could return a JSON array of entities. However, since our focus is on correctness and ease, you might simply return the marked-up text and perhaps document how to parse it externally if the user wants structured data.</p>
|
||
<p>Finally, use an <strong>HTTP Response</strong> node (if the workflow was triggered by a Webhook) to send back the results. If the workflow was triggered via n8n’s chat trigger (in the case of interactive usage), you would instead rely on the chat UI output. For a pure API workflow, the HTTP response will contain either the tagged text or a JSON of extracted entities, which the user’s script or application can then use.</p>
|
||
<p><strong>Note:</strong> If you plan to run multiple analyses or have an ongoing service, you might want to <strong>persist the Ollama server</strong> (don’t shut it down between runs) and perhaps keep the model loaded in VRAM for performance. Ollama will cache the model in memory after the first request, so subsequent requests are faster. On an A100, you could even load two models (if you plan to experiment with which gives better results) but be mindful of VRAM usage if doing so concurrently.</p>
|
||
</section>
|
||
</section>
|
||
<section id="model-selection-considerations" class="level2">
|
||
<h2 class="anchored" data-anchor-id="model-selection-considerations">Model Selection Considerations</h2>
|
||
<p>We provided two example 14B models (DeepSeek-R1 and Cogito) to use with this pipeline. Both are good choices, but here are some considerations and alternatives:</p>
|
||
<ul>
|
||
<li><strong>Accuracy vs. Speed:</strong> Larger models (like 14B or 30B) generally produce more accurate and coherent results, especially for complex instructions, compared to 7B models. Since our aim is correctness of NER output, the A100 allows us to use a 14B model which offers a sweet spot. In preliminary tests, these models can correctly tag most obvious entities and even handle some tricky cases (e.g. person names with titles, organizations that sound like person names, etc.) thanks to their pretrained knowledge. If you find the model is making mistakes, you could try a bigger model (Cogito 32B or 70B, if resources permit). Conversely, if you need faster responses and are willing to trade some accuracy, a 7-8B model or running the 14B at a higher quantization (e.g. 4-bit) on CPU might be acceptable for smaller texts.</li>
|
||
<li><strong>Domain of the Text:</strong> The paper dealt with historical travel guide text (1920s era). These open models have been trained on large internet corpora, so they likely have seen a lot of historical names and terms, but their coverage might not be as exhaustive as GPT-4. If your text is in a specific domain (say, ancient mythology or very obscure local history), the model might miss entities that it doesn’t recognize as famous. The prompt’s context can help (for example, adding a note like <em>“Note: Mythological characters should be considered PERSON entities.”</em> as they did for Greek gods). For extremely domain-specific needs, one could fine-tune a model or use a specialized one, but that moves beyond the zero-shot philosophy.</li>
|
||
<li><strong>Language:</strong> If your texts are not in English, ensure the chosen model is multilingual. Cogito, for instance, was trained in over 30 languages, so it can handle many European languages (the paper also tested German prompts). If using a model that’s primarily English (like some LLaMA variants), you might get better results by writing the instructions in English but letting it output tags in the original text. The study found English prompts initially gave better recall even on German text, but with prompt tweaks the gap closed. For our pipeline, you can simply provide the definitions in English and the text in the foreign language – a capable model will still tag the foreign entities. For example, Cogito or DeepSeek should tag a German sentence’s <em>“Herr Schmidt”</em> as <code><<PER Herr Schmidt /PER>></code>. Always test on a small sample if in doubt.</li>
|
||
<li><strong>Extended Context:</strong> If your input text is very long (tens of thousands of words), you should chunk it into smaller segments (e.g. paragraph by paragraph) and run the model on each, then merge the outputs. This is because most models (including DeepSeek 14B) have a context window of 2048–8192 tokens. However, Cogito’s 128k context capability is a game-changer – in theory you could feed an entire book and get a single output. Keep in mind the time and memory usage will grow with very large inputs, and n8n might need increased timeout settings for such long runs. For typical use (a few pages of text at a time), the standard context is sufficient.</li>
|
||
</ul>
|
||
<p>In our implementation, we encourage experimenting with both DeepSeek-R1 and Cogito models. Both are <strong>open-source and free for commercial use</strong> (Cogito uses an Apache 2.0 license, DeepSeek MIT). They represent some of the best 14B-class models as of early 2025. You can cite these models in any academic context if needed, or even switch to another model with minimal changes to the n8n workflow (just pull the model and change the model name in the Ollama node).</p>
|
||
</section>
|
||
<section id="example-run" class="level2">
|
||
<h2 class="anchored" data-anchor-id="example-run">Example Run</h2>
|
||
<p>Let’s run through a hypothetical example to illustrate the output. Suppose a historian supplies the following via the webhook:</p>
|
||
<ul>
|
||
<li><strong>Entities:</strong> <code>PER, ORG, LOC</code></li>
|
||
<li><strong>Text:</strong> <em>“Baron Münchhausen was born in Bodenwerder and served in the Russian military under Empress Anna. Today, the Münchhausen Museum in Bodenwerder is operated by the town council.”</em></li>
|
||
</ul>
|
||
<p>When the workflow executes, the LLM receives instructions to tag people (PER), organizations (ORG), and locations (LOC). With the prompt techniques described, the model’s output might look like:</p>
|
||
<pre class="plain"><code><<PER Baron Münchhausen /PER>> was born in <<LOC Bodenwerder /LOC>> and served
|
||
in the Russian military under <<PER Empress Anna /PER>>. Today, the <<ORG
|
||
Münchhausen Museum /ORG>> in <<LOC Bodenwerder /LOC>> is operated by the town
|
||
council.</code></pre>
|
||
<p>All person names (Baron Münchhausen, Empress Anna) are enclosed in <code><<PER>></code> tags, the museum is marked as an organization, and the town Bodenwerder is marked as a location (twice). The rest of the sentence remains unchanged. This output can be returned as-is to the user. They can visually verify it or programmatically parse out the tagged entities. The correctness of outputs is high: each tag corresponds to a real entity mention in the text, and there are no hallucinated tags. If the model were to make an error (say, tagging “Russian” as LOC erroneously), the user could adjust the prompt (for example, clarify that national adjectives are not entities) and re-run.</p>
|
||
</section>
|
||
<section id="limitations-and-solutions" class="level2">
|
||
<h2 class="anchored" data-anchor-id="limitations-and-solutions">Limitations and Solutions</h2>
|
||
<p>While this pipeline makes NER easier to reproduce, it’s important to be aware of its limitations and how to mitigate them:</p>
|
||
<ul>
|
||
<li><p><strong>Model Misclassifications:</strong> A local 14B model may not match GPT-4’s level of understanding. It might occasionally tag something incorrectly or miss a subtle entity. For instance, in historical texts, titles or honorifics (e.g. <em>“Dr. John Smith”</em>) might confuse it, or a ship name might be tagged as ORG when it’s not in our categories. <strong>Solution:</strong> Refine the prompt with additional guidance. You can add a “Note” section in the instructions to handle known ambiguities (the paper did this with notes about Greek gods being persons, etc.). Also, a quick manual review or spot-check is recommended for important outputs. Since the output format is simple, a human or a simple script can catch obvious mistakes (e.g., if “Russian” was tagged LOC, a post-process could remove it knowing it’s likely wrong). Over time, if you notice a pattern of mistakes, update the prompt instructions accordingly.</p></li>
|
||
<li><p><strong>Text Reproduction Issues:</strong> We instruct the model to output the original text verbatim with tags, but LLMs sometimes can’t resist minor changes. They may “correct” spelling or punctuation, or alter spacing. The paper noted this tendency and used fuzzy matching when evaluating. In our pipeline, minor format changes usually don’t harm the extraction, but if preserving text exactly is important (say for downstream alignment), this is a concern. <strong>Solution:</strong> Emphasize fidelity in the prompt (we already do). If needed, do a diff between the original text and tagged text and flag differences. Usually differences will be small (e.g., changing an old spelling to modern). You can then either accept them or attempt a more rigid approach (like asking for a JSON list of entity offsets – though that introduces other complexities and was intentionally avoided by the authors). In practice, we found the tag insertion approach with strong instructions yields nearly identical text apart from the tags.</p></li>
|
||
<li><p><strong>Long Inputs and Memory:</strong> Very large documents may exceed the model’s input capacity or make the process slow. The A100 GPU can handle a lot, but n8n itself might have default timeouts for a single workflow execution. <strong>Solution:</strong> For long texts, break the input into smaller chunks (maybe one chapter or section at a time). n8n can loop through chunks using the Split In Batches node or simply by splitting the text in the Function node and feeding the LLM node multiple times. You’d then concatenate the outputs. If chunking, ensure that if an entity spans a chunk boundary, it might be missed – usually rare in well-chosen chunk boundaries (paragraph or sentence). Alternatively, use Cogito for its extended context to avoid chunking. Make sure to increase n8n’s execution timeout if needed (via environment variable <code class="sourceCode bash"><span class="ex" style="color: null;
|
||
background-color: null;
|
||
font-style: inherit;">N8N_DEFAULT_TIMEOUT</span></code> or in the workflow settings).</p></li>
|
||
<li><p><strong>Concurrent Usage:</strong> If multiple users or processes hit the webhook simultaneously, they would be sharing the single LLM instance. Ollama can queue requests, but the GPU will handle them one at a time (unless running separate instances with multiple GPUs). For a research setting with one user at a time, this is fine. If offering this as a service to others, consider queuing requests or scaling out (multiple replicas of this workflow on different GPU machines). The stateless design of the prompt makes each run independent.</p></li>
|
||
<li><p><strong>n8n Learning Curve:</strong> For historians new to n8n, setting up the workflow might be unfamiliar. However, n8n’s no-code interface is fairly intuitive with a bit of guidance. This case study provides the logic; one can also import pre-built workflows. In fact, the <em>n8n</em> community has template workflows (for example, a template for chatting with local LLMs) that could be adapted. We assume the base pipeline from the paper’s authors is available on GitHub – using that as a starting point, one mostly needs to adjust nodes as described. If needed, one can refer to n8n’s official docs or community forum for help on creating a webhook or using function nodes. Once set up, running the workflow is as easy as sending an HTTP request or clicking “Execute Workflow” in n8n.</p></li>
|
||
<li><p><strong>Output Verification:</strong> Since we prioritize correctness, you may want to evaluate how well the model did, especially if you have ground truth annotations. While benchmarking is out of scope here, note that you can integrate evaluation into the pipeline too. For instance, if you had a small test set with known entities, you could compare the model output tags with expected tags using a Python script (n8n has an Execute Python node) or use an NER evaluation library like <em>nervaluate</em> for precision/recall. This is exactly what the authors did to report performance, and you could mimic that to gauge your chosen model’s accuracy.</p></li>
|
||
</ul>
|
||
</section>
|
||
<section id="conclusion" class="level2">
|
||
<h2 class="anchored" data-anchor-id="conclusion">Conclusion</h2>
|
||
<p>By following this guide, we implemented the <strong>NER4All</strong> paper’s methodology with a local, reproducible setup. We used n8n to handle automation and prompt assembly, and a local LLM (via Ollama) to perform the heavy-duty language understanding. The result is a flexible NER pipeline that requires <strong>no training data or API access</strong> – just a well-crafted prompt and a powerful pretrained model. We demonstrated how a user can specify custom entity types and get their text annotated in one click or API call. The approach leverages the strengths of LLMs (vast knowledge and language proficiency) to adapt to historical or niche texts, aligning with the paper’s finding that a bit of context and expert prompt design can unlock high NER performance.</p>
|
||
<p>Importantly, this setup is <strong>easy to reproduce</strong>: all components are either open-source or freely available (n8n, Ollama, and the models). A research engineer or historian can run it on a single machine with sufficient resources, and it can be shared as a workflow file for others to import. By removing the need for extensive data preparation or model training, this lowers the barrier to extracting structured information from large text archives.</p>
|
||
<p>Moving forward, users can extend this case study in various ways: adding more entity types (just update the definitions input), switching to other LLMs as they become available (perhaps a future 20B model with even better understanding), or integrating the output with databases or search indexes for further analysis. With the rapid advancements in local AI models, we anticipate that such pipelines will become even more accurate and faster over time, continually democratizing access to advanced NLP for all domains.</p>
|
||
<p><strong>Sources:</strong> This implementation draws on insights from Ahmed et al. (2025) for the prompt-based NER method, and uses tools like n8n and Ollama as documented in their official guides. The chosen models (DeepSeek-R1 and Cogito) are described in their respective releases. All software and models are utilized in accordance with their licenses for a fully local deployment.</p>
|
||
</section>
|
||
|
||
|
||
<div id="quarto-appendix" class="default"><section id="methodik-llms-als-autoren" class="level2 appendix"><h2 class="anchored quarto-appendix-heading">Methodik / LLMs als ‘Autoren’</h2><div class="quarto-appendix-contents">
|
||
|
||
<p>Erstellt wurde der initial draft mittels Websuche und “Deep-Research” von <code>gpt-4.5 (preview)</code>. Abschließendes Korrekturlesen/inhaltliche Prüfung/Layouting durch Nicole Dresselhaus.</p>
|
||
|
||
|
||
|
||
</div></section><section class="quarto-appendix-contents" id="quarto-bibliography"><h2 class="anchored quarto-appendix-heading">References</h2><div id="refs" class="references csl-bib-body" data-entry-spacing="0">
|
||
<div id="ref-ollama_chroma_cookbook" class="csl-entry">
|
||
<div class="csl-left-margin">1. </div><div class="csl-right-inline"><a href="https://cookbook.chromadb.dev/integrations/ollama/embeddings/">Ollama - chroma cookbook</a>. 2024.</div>
|
||
</div>
|
||
<div id="ref-smart_connections_plugin" class="csl-entry">
|
||
<div class="csl-left-margin">2. </div><div class="csl-right-inline"><a href="https://www.reddit.com/r/ObsidianMD/comments/1fzmkdk/just_wanted_to_mention_that_the_smart_connections/">Just wanted to mention that the smart connections plugin is incredible. : R/ObsidianMD</a>. 2024.</div>
|
||
</div>
|
||
<div id="ref-khoj_plugin" class="csl-entry">
|
||
<div class="csl-left-margin">3. </div><div class="csl-right-inline"><a href="https://forum.obsidian.md/t/khoj-an-ai-powered-search-assistant-for-you-second-brain/53756">Khoj: An AI powered search assistant for your second brain - share & showcase - obsidian forum</a>. 2023.</div>
|
||
</div>
|
||
<div id="ref-supercharging_obsidian_search" class="csl-entry">
|
||
<div class="csl-left-margin">4. </div><div class="csl-right-inline">@airabbitX. 2024. <a href="https://medium.com/@airabbitX/supercharging-obsidian-search-with-local-llms-a-personal-journey-1e008eb649a6">Supercharging obsidian search with AI and ollama</a>.</div>
|
||
</div>
|
||
<div id="ref-export_to_common_graph_formats" class="csl-entry">
|
||
<div class="csl-left-margin">5. </div><div class="csl-right-inline"><a href="https://forum.obsidian.md/t/export-to-common-graph-formats/4138">Export to common graph formats - plugins ideas - obsidian forum</a>. 2020.</div>
|
||
</div>
|
||
<div id="ref-personal_knowledge_graphs_in_obsidian" class="csl-entry">
|
||
<div class="csl-left-margin">6. </div><div class="csl-right-inline">Pavlyshyn, Volodymyr. 2024. <a href="https://volodymyrpavlyshyn.medium.com/personal-knowledge-graphs-in-obsidian-528a0f4584b9">Personal knowledge graphs in obsidian</a>.</div>
|
||
</div>
|
||
<div id="ref-export_obsidian_to_rdf" class="csl-entry">
|
||
<div class="csl-left-margin">7. </div><div class="csl-right-inline">Pavlyshyn, Volodymyr. 2024. <a href="https://volodymyrpavlyshyn.medium.com/how-to-export-your-obsidian-vault-to-rdf-00fb2539ed18">How to export your obsidian vault to RDF</a>.</div>
|
||
</div>
|
||
<div id="ref-ai_empowered_zettelkasten_with_ner_and_graph_llm" class="csl-entry">
|
||
<div class="csl-left-margin">8. </div><div class="csl-right-inline"><a href="https://forum.obsidian.md/t/ai-empowered-zettelkasten-with-ner-and-graph-llm/79112">AI empowered zettelkasten with NER and graph LLM - knowledge management - obsidian forum</a>. 2024.</div>
|
||
</div>
|
||
<div id="ref-build_your_second_brain_with_khoj_ai" class="csl-entry">
|
||
<div class="csl-left-margin">9. </div><div class="csl-right-inline"><a href="https://dswharshit.medium.com/build-your-second-brain-with-khoj-ai-high-signal-ai-2-87492730d7ce">Build your second brain with khoj AI</a>. 2024.</div>
|
||
</div>
|
||
<div id="ref-second_brain_assistant_with_obsidian" class="csl-entry">
|
||
<div class="csl-left-margin">10. </div><div class="csl-right-inline"><a href="https://www.ssp.sh/brain/second-brain-assistant-with-obsidian-notegpt/">Second brain assistant with obsidian</a>. 2025.</div>
|
||
</div>
|
||
<div id="ref-basic_memory_ai_conversations_that_build_knowledge" class="csl-entry">
|
||
<div class="csl-left-margin">11. </div><div class="csl-right-inline"><a href="https://basicmachines.co/">Basic memory | AI conversations that build knowledge</a>.</div>
|
||
</div>
|
||
<div id="ref-local_free_rag_with_question_generation" class="csl-entry">
|
||
<div class="csl-left-margin">12. </div><div class="csl-right-inline">Galvis, Oscar. 2024. <a href="https://lomaky.medium.com/local-free-rag-with-question-generation-using-lm-studio-nomic-embeddings-chromadb-and-llama-3-2-9758877e93b4">Local (free) RAG with question generation using LM studio, nomic embeddings, ChromaDB and llama 3.2 on a mac mini M1</a>.</div>
|
||
</div>
|
||
<div id="ref-private_gpt_llama_cpp_based_scripts" class="csl-entry">
|
||
<div class="csl-left-margin">13. </div><div class="csl-right-inline"><a href="https://www.ssp.sh/brain/second-brain-assistant-with-obsidian-notegpt/">privateGPT / llama.cpp based scripts</a>. 2025.</div>
|
||
</div>
|
||
</div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@online{2025,
|
||
author = {, GPT-4.5 and , cogito-v1-preview and Dresselhaus, Nicole},
|
||
title = {Case {Study:} {Local} {LLM-Based} {NER} with N8n and
|
||
{Ollama}},
|
||
date = {2025-05-05},
|
||
url = {https://drezil.de/Writing/ner4all-case-study.html},
|
||
langid = {en},
|
||
abstract = {Named Entity Recognition (NER) is a foundational task in
|
||
text analysis, traditionally addressed by training NLP models on
|
||
annotated data. However, a recent study – \_“NER4All or Context is
|
||
All You Need”\_ – showed that out-of-the-box Large Language Models
|
||
(LLMs) can **significantly outperform** classical NER pipelines
|
||
(e.g. spaCy, Flair) on historical texts by using clever prompting,
|
||
without any model retraining. This case study demonstrates how to
|
||
implement the paper’s method using entirely local infrastructure: an
|
||
**n8n** automation workflow (for orchestration) and a **Ollama**
|
||
server running a 14B-parameter LLM on an NVIDIA A100 GPU. The goal
|
||
is to enable research engineers and tech-savvy historians to
|
||
**reproduce and apply this method easily** on their own data, with a
|
||
focus on usability and correct outputs rather than raw performance.
|
||
We will walk through the end-to-end solution – from accepting a
|
||
webhook input that defines entity types (e.g. Person, Organization,
|
||
Location) to prompting a local LLM to extract those entities from a
|
||
text. The solution covers setup instructions, required
|
||
infrastructure (GPU, memory, software), model configuration, and
|
||
workflow design in n8n. We also discuss potential limitations (like
|
||
model accuracy and context length) and how to address them. By the
|
||
end, you will have a clear blueprint for a **self-hosted NER
|
||
pipeline** that leverages the knowledge encoded in LLMs (as
|
||
advocated by the paper) while maintaining data privacy and
|
||
reproducibility.}
|
||
}
|
||
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-2025" class="csl-entry quarto-appendix-citeas">
|
||
GPT-4.5, cogito-v1-preview, and Nicole Dresselhaus. 2025. <span>“Case
|
||
Study: Local LLM-Based NER with N8n and Ollama.”</span> May 5, 2025. <a href="https://drezil.de/Writing/ner4all-case-study.html">https://drezil.de/Writing/ner4all-case-study.html</a>.
|
||
</div></div></section></div> ]]></description>
|
||
<category>Article</category>
|
||
<category>Case-study</category>
|
||
<category>ML</category>
|
||
<category>NER</category>
|
||
<guid>https://drezil.de/Writing/ner4all-case-study.html</guid>
|
||
<pubDate>Sun, 04 May 2025 22:00:00 GMT</pubDate>
|
||
<media:content url="https://drezil.de/thumbs/writing_ner4all-case-study.png" medium="image" type="image/png" height="96" width="144"/>
|
||
</item>
|
||
<item>
|
||
<title>RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze</title>
|
||
<link>https://drezil.de/Writing/Obsidian-RAG.html</link>
|
||
<description><![CDATA[
|
||
|
||
|
||
|
||
|
||
|
||
<section id="hintergrund-und-zielsetzung" class="level2">
|
||
<h2 class="anchored" data-anchor-id="hintergrund-und-zielsetzung">Hintergrund und Zielsetzung</h2>
|
||
<p>Der Nutzer verfügt über eine Obsidian-Wissensdatenbank, in der Markdown-Dateien mit <strong>typisierten</strong> Inhalten (FileClasses wie <code>Person</code>, <code>Projekt</code>, <code>Deliverable</code> etc.) verwaltet werden. Die Notizen enthalten strukturierte <strong>YAML-Metadaten</strong> (unterstützt durch Plugins wie <em>Metadata Menu</em>) und sind durch viele <strong>Wiki-Links</strong> miteinander vernetzt. Standardisierte Templates (via <em>Templater</em>) sorgen dafür, dass z.B. Personenseiten immer ähnliche Felder (Name, ORCID, etc.) aufweisen.</p>
|
||
<p><strong>Ziel</strong> ist es, mithilfe eines Language Models (LLM) wiederkehrende Aufgaben zu erleichtern, zum Beispiel: automatisch YAML-Felder ausfüllen (etwa fehlende ORCID iDs bei Personen ergänzen), neue Entitätsseiten anhand von Templates befüllen oder sinnvolle Verlinkungen zwischen Notizen vorschlagen. Dabei reicht ein tägliches Neu-Einlesen der Obsidian-Daten (via Cronjob o.Ä.) aus – eine Echtzeit-Synchronisation ist optional. Die Obsidian-internen Wikilinks (<code>[[...]]</code>) müssen im LLM-Ausgabeformat nicht unbedingt klickbar sein (es genügt, wenn sie referenziert werden).</p>
|
||
<p>Um diese Funktionen umzusetzen, bieten sich verschiedene <strong>technische Ansätze</strong> an. Im Folgenden werden fünf Optionen untersucht: (1) Nutzung eines <strong>Vektorspeichers</strong> für semantische Suche, (2) Aufbau eines <strong>Knowledge Graph</strong> aus den Notizen, (3) eine <strong>Hybrid-Lösung</strong> aus Graph und Vektor, (4) Extraktion & Normalisierung der <strong>YAML-Metadaten</strong> und (5) existierende <strong>Tools/Workflows</strong> zur Automatisierung. Jede Option wird mit Funktionsweise, Vorund Nachteilen, Aufwand, Integrationsmöglichkeiten (insb. mit lokalen LLMs wie LLaMA, Deepseek, Cogito etc.) sowie konkreten Tool-Empfehlungen dargestellt.</p>
|
||
</section>
|
||
<section id="vektorbasierter-ansatz-semantic-search-mit-embeddings" class="level2 page-columns page-full">
|
||
<h2 class="anchored" data-anchor-id="vektorbasierter-ansatz-semantic-search-mit-embeddings">1. Vektorbasierter Ansatz: Semantic Search mit Embeddings</h2>
|
||
<section id="prinzip" class="level3">
|
||
<h3 class="anchored" data-anchor-id="prinzip">Prinzip</h3>
|
||
<p>Alle Markdown-Notizen (bzw. deren Inhalt) werden in kleinere Chunks zerlegt und durch einen Embedding-Modell in hochdimensionale Vektoren umgewandelt. Diese Vektoren werden in einem <strong>Vektorstore</strong> (wie ChromaDB oder Weaviate) gespeichert. Bei Anfragen des LLM (z.B. <em>“Welche Projekte hat Person X?”</em> oder <em>“Erstelle eine neue Organisation XYZ basierend auf ähnlichen Einträgen”</em>), können mittels <strong>ähnlichkeitssuche</strong> semantisch passende Notiz-Abschnitte abgerufen und dem LLM als Kontext mitgegeben werden (Retrieval-Augmented Generation).</p>
|
||
</section>
|
||
<section id="implementierung" class="level3 page-columns page-full">
|
||
<h3 class="anchored" data-anchor-id="implementierung">Implementierung</h3>
|
||
<p>In der Praxis ließe sich z.B. ein Workflow mit <strong>Ollama</strong> + <strong>Nomic Embeddings</strong> + <strong>Chroma</strong><sup>1</sup> aufbauen. Ollama stellt ein lokales LLM-Serving bereit und bietet auch eine API für Embeddins <span class="citation" data-cites="ollama_chroma_cookbook">[1]</span>. Man könnte ein spezialisiertes Embeddin-Modell wie <code>nomic-embed-text</code> verwenden, welches kompakte 1024-dimensionale Textvektoren liefert <span class="citation" data-cites="ollama_chroma_cookbook">[1]</span>. Die Notizen des Obsidian Vault würden per Skript täglich eingelesen, in Sinnabschnitte (Chunks) aufgeteilt (z.B. nach Überschriften oder einer festen Token-Länge) und über Ollamas Embedding-API in Vektoren umgewandelt <span class="citation" data-cites="ollama_chroma_cookbook">[1]</span>. Diese Vektoren speichert man in einer lokalen DB wie Chroma. Anfragen an das LLM werden dann zunächst an den Vektorstore gestellt, um die relevantesten Notiz-Abschnitte zu finden, welche dann zusammen mit der eigentlichen Frage an das LLM gegeben werden (klassischer RAG-Pipeline). Dieses Verfahren ist vergleichbar mit dem <em>Smart Connections</em> Obsidian-Plugin: Dort wird ebenfalls ein “Text Embedding Model” auf den Vault angewendet, um zu einer Nutzerfrage automatisch thematisch passende Notizen zu finden und dem LLM bereitzustellen <span class="citation" data-cites="smart_connections_plugin">[2]</span>. So konnte im Beispiel ein lokales LLaMA-basiertes Modell Fragen zum eigenen Vault korrekt beantworten, indem es zuvor den passenden Ausschnitt (hier: eine Styleguide-Notiz) über Embeddings gefunden hatte <span class="citation" data-cites="smart_connections_plugin">[2]</span>.</p>
|
||
<div class="no-row-height column-margin column-container"><div id="fn1"><p><sup>1</sup> Alle diese Teile laufen bereits individuell in der Arbeitsgruppe bzw. werden schon genutzt.</p></div></div></section>
|
||
<section id="integration-mit-lokalen-llms" class="level3">
|
||
<h3 class="anchored" data-anchor-id="integration-mit-lokalen-llms">Integration mit lokalen LLMs</h3>
|
||
<p>Ein Vorteil dieses Ansatzes ist, dass er schon heute mit lokalen Open-Source-LLMs funktioniert. Beispielsweise ließ sich in <em>Smart Connections</em> ein lokal gehostetes LLaMA-Model (3B Instruct) via text-generation-webui einbinden <span class="citation" data-cites="smart_connections_plugin">[2]</span>. Alternativ kann man auch <em>LLM-as-a-service</em> Tools wie <strong>Ollama</strong> nutzen, um ein Modell wie Llama 2 bereitzustellen. Die Open-Source-Tools <strong>LangChain</strong> oder <strong>LlamaIndex</strong> bieten Module, um Vektorstores anzubinden und mit LLM-Abfragen zu kombinieren – dies kann man auch mit lokal eingebundenen Modellen (z.B. über LlamaCpp oder GPT4All) verwenden. Zahlreiche fertige Projekte demonstrieren dieses Vorgehen: z.B. <em>privateGPT</em> kombiniert LangChain, GPT4All (lokales LLM) und Chroma, um komplett offline Fragen über lokale Dateien zu beantworten <span class="citation" data-cites="second_brain_assistant_with_obsidian">[3]</span>. Auch <strong>Khoj</strong> verfolgt einen ähnlichen Pfad: Es indexiert den Vault und erlaubt semantische <strong>Natürliche Sprache Suche</strong> über Markdown-Inhalte sowie <em>“ähnliche Notizen finden”</em> <span class="citation" data-cites="khoj_plugin">[4]</span>.</p>
|
||
</section>
|
||
<section id="leistung" class="level3">
|
||
<h3 class="anchored" data-anchor-id="leistung">Leistung</h3>
|
||
<p>Dank moderner Embedding-Modelle können semantisch ähnliche Inhalte gefunden werden, selbst wenn die Schlagwörter nicht exakt übereinstimmen. Das löst das in Obsidian bekannte Problem, dass die eingebaute Suche nur exakte Worttreffer findet <span class="citation" data-cites="supercharging_obsidian_search">[5]</span>. Der Ansatz skaliert auch auf größere Wissensbasen; Vektordatenbanken wie Weaviate oder Chroma sind für zehntausende Einträge ausgelegt. Eine tägliche Aktualisierung ist machbar, da nur neue/geänderte Notizen re-embedded werden müssen.</p>
|
||
</section>
|
||
<section id="nachteile-und-aufwand" class="level3 page-columns page-full">
|
||
<h3 class="anchored" data-anchor-id="nachteile-und-aufwand">Nachteile und Aufwand</h3>
|
||
<p>Die Einrichtung erfordert mehrere Komponenten. Man benötigt Pipeline-Schritte für das Chunking, Embedding und das Handling des Vektorstores – dies bedeutet anfängliche Komplexität und Rechenaufwand <span class="citation" data-cites="supercharging_obsidian_search">[5]</span>. Insbesondere das Generieren der Embeddings kann bei großen Vaults zeitund speicherintensiv sein (je nach Modell und Hardware) <span class="citation" data-cites="supercharging_obsidian_search">[5]</span>. Laufende Kosten sind bei rein lokaler Verarbeitung allerdings kein Thema außer CPU/GPU-Last. Ein potenzieller Nachteil ist, dass rein embeddings-basierte Suche keine <strong>strukturierte</strong> Abfrage erlaubt – das Modell findet zwar thematisch passende Textpassagen, aber um z.B. <strong>eine bestimmte Eigenschaft</strong> (wie eine fehlende ORCID) gezielt abzufragen, müsste man dennoch im Text suchen oder zusätzliche Logik anwenden. Das LLM kann aus den gefundenen Texten zwar implizit Fakten entnehmen, hat aber kein explizites Wissen über die Datenstruktur. Zudem können irrelevante Kontextstücke eingebunden werden, wenn das semantische Matching fehlerhaft ist (dies erfordert ggf. Feintuning der Chunk-Größe oder Filtern per Dateityp/-klasse)<sup>2</sup>.</p>
|
||
<div class="no-row-height column-margin column-container"><div id="fn2"><p><sup>2</sup> Und diese Nachteile machen dies zu einem Deal-Breaker. Gerade in Tabellen oder Auflistungen kann der Attention-Mechanismus der LLM schnell zu einem Mischen oder Verwechseln von präsentierten Informationen führen. Besonders kleine Netze (meist bis ~7b) sind hier anfällig.</p></div></div></section>
|
||
<section id="zusammenfassung-ansatz-1-vektordatenbank-embeddings" class="level3 page-columns page-full">
|
||
<h3 class="anchored" data-anchor-id="zusammenfassung-ansatz-1-vektordatenbank-embeddings">Zusammenfassung – Ansatz 1: Vektordatenbank (Embeddings)</h3>
|
||
<table class="caption-top table">
|
||
<colgroup>
|
||
<col style="width: 6%">
|
||
<col style="width: 93%">
|
||
</colgroup>
|
||
<thead>
|
||
<tr class="header">
|
||
<th></th>
|
||
<th><strong>Details</strong></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="odd">
|
||
<td><strong>Vorgehen</strong></td>
|
||
<td>Inhalte aller Markdown-Dateien in semantische Vektoren kodieren (z.B. mit <code>nomic-embed-text</code> (<span class="citation" data-cites="ollama_chroma_cookbook">[1]</span>)) und in einer Vektor-DB speichern. LLM-Anfragen per Similarity Search mit relevantem Kontext anreichern.</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td><strong>Stärken</strong></td>
|
||
<td><em>Semantische Suche</em> (findet thematisch passende Infos, nicht nur exakte Worttreffer) <span class="citation" data-cites="supercharging_obsidian_search">[5]</span>. Skaliert auf große Textmengen. Bereits heute mit lokalen LLMs erprobt (z.B. <em>Smart Connections</em> Plugin) <span class="citation" data-cites="smart_connections_plugin">[2]</span>. Gut geeignet für Q&A, Textzusammenfassungen und Link-Vorschläge basierend auf Ähnlichkeit.</td>
|
||
</tr>
|
||
<tr class="odd">
|
||
<td><strong>Schwächen</strong></td>
|
||
<td>Komplexeres Setup (Embedding-Model + DB + Pipeline) <span class="citation" data-cites="supercharging_obsidian_search">[5]</span>. Hoher Rechenaufwand für Embeddings bei großen Vaults. Kein explizites Modell von Beziehungen/Metadaten – strukturierte Abfragen (z.B. <em>“zeige alle Personen ohne ORCID”</em>) nur mit Zusatzlogik. Kontexttreffer können ungenau sein (erfordert ggf. Feinjustierung).</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td><strong>Integrations-Optionen</strong></td>
|
||
<td>Lokale LLM-Einbindung möglich (z.B. LLaMA 2 über Ollama-API). Tools: <strong>ChromaDB</strong>, <strong>Weaviate</strong> oder <strong>FAISS</strong> als Vektorstore; <strong>LangChain/LlamaIndex</strong> für Pipeline-Management. Obsidian-Plugins: <em>Smart Connections</em> (komplett integriert mit lokalem Embedder+LLM) <span class="citation" data-cites="smart_connections_plugin">[2]</span>; <em>Khoj</em> (separater Suchassistent mit Embeddings) <span class="citation" data-cites="khoj_plugin">[4]</span>.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<div class="cell page-columns page-full" data-layout-align="default">
|
||
<div class="cell-output-display column-screen-inset-right">
|
||
<div>
|
||
<p></p><figure class="figure"><p></p>
|
||
<div>
|
||
<pre class="mermaid mermaid-js">graph LR
|
||
A[Obsidian Vault] --> B[Chunking]
|
||
B --> C[Embedding Model]
|
||
C --> D[(Vector Database)]
|
||
E[User Query] --> F[Query Embedding]
|
||
F --> G[Similarity Search]
|
||
D --> G
|
||
G --> H[Relevant Chunks]
|
||
H --> I[LLM]
|
||
E --> I
|
||
I --> J[Response]
|
||
</pre>
|
||
</div>
|
||
<p></p></figure><p></p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</section>
|
||
<section id="knowledge-graph-ansatz-strukturierte-graphdatenbank" class="level2 page-columns page-full">
|
||
<h2 class="anchored" data-anchor-id="knowledge-graph-ansatz-strukturierte-graphdatenbank">2. Knowledge-Graph-Ansatz: Strukturierte Graphdatenbank</h2>
|
||
<section id="prinzip-1" class="level3">
|
||
<h3 class="anchored" data-anchor-id="prinzip-1">Prinzip</h3>
|
||
<p>Statt (oder zusätzlich zu) freiem Text wird der Informationsgehalt des Vaults als <strong>Graph</strong> modelliert. Jede Notiz entspricht einem <strong>Knoten</strong> im Graphen (mit Typ-Label gemäß FileClass, z.B. <code>Person</code>, <code>Projekt</code> etc.). Relationen zwischen Notizen – implizit durch Obsidian-Wikilinks gegeben – werden zu expliziten <strong>Kanten</strong> im Graph (z.B. eine Person “arbeitet in” Organisation, ein Projekt “liefert” ein Deliverable). Auch Metadaten aus YAML können als Knoten oder Properties modelliert werden (z.B. ORCID als Attribut eines Person-Knotens, Tags als Relationen <em>“hat Schlagwort”</em> usw.). Das Ergebnis ist ein <strong>Wissensgraph</strong>, der ähnlich einem klassischen RDF-Triple-Store oder Neo4j-Property-Graph komplexe Abfragen und Analysen ermöglicht.</p>
|
||
</section>
|
||
<section id="erstellung-des-graphen" class="level3">
|
||
<h3 class="anchored" data-anchor-id="erstellung-des-graphen">Erstellung des Graphen</h3>
|
||
<p>Eine Möglichkeit ist die Obsidian-Daten nach RDF zu exportieren. So beschreibt Pavlyshyn (2023) ein Verfahren, einen Vault ins RDF-Format zu überführen, um “komplexe Abfragen mit klassischen Semantic-Tools” zu ermöglichen <span class="citation" data-cites="export_obsidian_to_rdf">[6]</span>. Alternativ kann man direkt in einer Graphdatenbank wie <strong>Neo4j</strong> modellieren. Ein Community-Plugin (<em>obsidian-neo4j-stream</em>) hat beispielsweise versucht, den Obsidian-Linkgraph in Neo4j importierbar zu machen <span class="citation" data-cites="export_to_common_graph_formats">[7]</span>. Konkret würde man pro Markdown-Datei einen Node mit dessen YAML-Feldern als Properties anlegen. Bestehende Wiki-Links zwischen Dateien werden als ungerichtete oder gerichtete Edges abgebildet (hier kann man, sofern man mehr Semantik will, Link-Typen einführen – z.B. im Text <code>[[Albert Einstein|Autor]]</code> könnte der Alias “Autor” als Kanten-Label genutzt werden). Da Obsidian standardmäßig keine typisierten Kanten unterstützt, bleiben Relationstypen begrenzt – Plugins wie <em>Juggl</em> oder <em>Graph-Link-Types</em> erlauben allerdings das Hinzufügen von Link-Metadaten, was für eine genauere Graph-Modellierung hilfreich sein könnte <span class="citation" data-cites="personal_knowledge_graphs_in_obsidian">[8]</span>. YAML-Inhalte, die auf andere Notizen referenzieren, können ebenfalls als Kanten kodiert werden (Beispiel: In einer Projekt-Notiz listet das YAML-Feld <code>team:</code> mehrere Personen – diese Verweise werden im Graph als Kanten <em>Projekt —hatTeam→ Person</em> umgesetzt). Nicht referenzielle Metadaten (etwa ein ORCID-Wert) bleiben einfach als Datenfeld am Knoten.</p>
|
||
</section>
|
||
<section id="nutzung-für-llm-aufgaben" class="level3">
|
||
<h3 class="anchored" data-anchor-id="nutzung-für-llm-aufgaben">Nutzung für LLM-Aufgaben</h3>
|
||
<p>Ein solcher Graph erlaubt <strong>strukturierte Abfragen</strong> und <strong>Schlussfolgerungen</strong>. Für wiederkehrende Aufgaben kann man den Graph gezielt auswerten. Beispielsweise ließen sich <em>“alle Personen ohne ORCID”</em> mittels einer einfachen Graph-Query ermitteln. Das LLM könnte diese Liste als Input erhalten und dann (ggf. mittels Tools oder Wissensbasis) die fehlenden IDs ergänzen. Auch <em>Link-Vorschläge</em> können aus dem Graph gezogen werden: Durch Graph-Analysen wie das Finden von gemeinsamen Nachbarn oder kürzesten Pfaden entdeckt man Verbindungen, die im Vault noch nicht als direkte Links existieren. So könnte man z.B. feststellen, dass zwei Personen an vielen gleichen Meetings teilgenommen haben und dem Nutzer vorschlagen, diese Personen direkt miteinander zu verknüpfen. Oder man erkennt durch <em>link prediction</em> Algorithmen neue mögliche Beziehungen. Forschung und Community sehen hier großes Potential: Eine AI-gestützte Graphanalyse kann helfen, verborgene Zusammenhänge im eigenen Zettelkasten zu finden <span class="citation" data-cites="ai_empowered_zettelkasten_with_ner_and_graph_llm">[9]</span>. Mit Graph-basiertem Reasoning ließe sich sogar <strong>neues Wissen entdecken</strong> oder logisch konsistente Antworten generieren <span class="citation" data-cites="ai_empowered_zettelkasten_with_ner_and_graph_llm">[8]</span> – etwas, das rein embeddings-basierte Ansätze so nicht leisten.</p>
|
||
</section>
|
||
<section id="integration-mit-llms" class="level3 page-columns page-full">
|
||
<h3 class="anchored" data-anchor-id="integration-mit-llms">Integration mit LLMs</h3>
|
||
<p>Die Integration eines Graphen erfordert meist eine <strong>Zwischenschicht</strong>. Ein LLM kann nicht direkt “in” einer Neo4j-Datenbank suchen, aber man kann ihm eine Schnittstelle anbieten. Zwei Strategien sind denkbar:</p>
|
||
<ol type="1">
|
||
<li><strong>Verbalize & Prompt:</strong> Informationen aus dem Graph gezielt ins Prompt einbetten. Z.B. könnte man bei einer Frage wie “In welcher Organisation arbeitet Alice?” erst eine Graphdatenbank-Anfrage (z.B. in Cypher oder SPARQL) ausführen und das Ergebnis (etwa: “Alice – arbeitetBei → AcmeCorp”) in Textform dem Modell vorgeben, bevor es antwortet. Solche Abfragen könnte ein LLM theoretisch sogar selbst generieren (LangChain bietet z.B. Agents, die Cypher-Queries formulieren und ausführen können). Für definierte Use-Cases kann man aber auch feste Query-Vorlagen verwenden.</li>
|
||
<li><strong>LLM-in-the-Loop Graph Reasoning:</strong> Neuere Libraries wie LlamaIndex ermöglichen es, LLMs als Reasoner über Graphen einzusetzen. Der Graph wird dabei intern z.B. als Tripel-Liste gehalten, und das LLM kann mittels promptbasierter Logik Kettenschlüsse durchführen. Allerdings muss der Graph dafür in das Prompt passen (bei sehr vielen Knoten unrealistisch) – es ist also eher für Teilgraphen oder summarische Beziehungen geeignet<sup>3</sup>.</li>
|
||
</ol>
|
||
<div class="no-row-height column-margin column-container"><div id="fn3"><p><sup>3</sup> Via ‘Tool Use’ in Modernen LLM könnte das LLM selbst eine Suche auslösen und so den Teilgraphen wählen. Aber alleine die formulierung der Suche führt dann direkt zu dem hybriden Ansatz unten.</p></div></div><p>Eine andere interessante Möglichkeit ist die Nutzung <strong>graphbasierter KI-Modelle</strong> (Graph Neural Networks o.ä.), die aber in unserem Kontext (persönlicher Vault) noch experimentell sind. Erwähnenswert ist z.B. MyKin.ai, ein Projekt, das einen privaten KI-Assistenten baut, der gemeinsam mit dem Nutzer einen persönlichen Wissensgraphen aufbaut und nutzt <span class="citation" data-cites="personal_knowledge_graphs_in_obsidian">[8]</span>. Hier übernimmt die KI das “heavy lifting” der Graph-Pflege, während der Nutzer chattet – ein hybrider Ansatz aus Conversation und Graphaufbau. Für unseren Anwendungsfall wäre jedoch eher ein statischer Graph sinnvoll, den wir periodisch aktualisieren.</p>
|
||
</section>
|
||
<section id="zusammenfassung---ansatz-2-graphdatenbank" class="level3 page-columns page-full">
|
||
<h3 class="anchored" data-anchor-id="zusammenfassung---ansatz-2-graphdatenbank">Zusammenfassung - Ansatz 2: Graphdatenbank</h3>
|
||
<table class="caption-top table">
|
||
<colgroup>
|
||
<col style="width: 4%">
|
||
<col style="width: 95%">
|
||
</colgroup>
|
||
<thead>
|
||
<tr class="header">
|
||
<th></th>
|
||
<th><strong>Details</strong></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="odd">
|
||
<td><strong>Vorgehen</strong></td>
|
||
<td>Konvertiere Vault-Inhalte in einen strukturierten Graphen (Knoten = Notizen/Entitäten; Kanten = Obsidian-Links oder abgeleitete Relationen). Nutzen von Graph-DB (Neo4j, RDF-Store) für Abfragen und Analysen.</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td><strong>Stärken</strong></td>
|
||
<td>Explizite <strong>Struktur</strong>: ermöglicht genaue Abfragen, z.B. Finde fehlende Werte oder alle Verknüpfungen eines Knotens auf einen Blick. <strong>Logische Inferenzen</strong> möglich (Graph Reasoning) – unterstützt Link-Empfehlungen und Konsistenzprüfungen <span class="citation" data-cites="ai_empowered_zettelkasten_with_ner_and_graph_llm">[8]</span>. Gute Ergänzung zu YAML-Typisierung: FileClass-Struktur wird vollständig nutzbar. Persistenz: Graph kann unabhängig von Obsidian analysiert, versioniert, mit anderen Daten gemappt werden (z.B. ORCID-Abgleich via externen Datensatz).</td>
|
||
</tr>
|
||
<tr class="odd">
|
||
<td><strong>Schwächen</strong></td>
|
||
<td>Erheblicher <strong>Initialaufwand</strong>: Datenmodell entwerfen, Export-Skripte schreiben oder Tools einrichten <span class="citation" data-cites="export_to_common_graph_formats">[7]</span>. Keine fertige Out-of-the-box-Lösung für Obsidian↔︎Graph (bislang nur Ansätze in der Community). Laufende Synchronisation nötig (Vault-Änderungen -> Graph-Update). Die LLM-Integration ist komplexer – erfordert Query-Tool oder das Einbetten von Graph-Daten ins Prompt. Für offene Fragen (Freitext) allein nicht ausreichend, da der Graph primär Fakten repräsentiert, nicht Fließtext.</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td><strong>Integrations-Optionen</strong></td>
|
||
<td><strong>Neo4j</strong> (mit APOC/Neosemantics für RDF-Unterstützung) eignet sich für Property-Graph-Modell; <strong>Apache Jena</strong> oder GraphDB für RDF-Triple-Store. <em>LangChain</em> bietet Memory/Agent, um Wissensgraphen abzufragen (z.B. ConversationKGMemory). <em>LlamaIndex</em> hat einen KnowledgeGraphIndex, der Tripel extrahieren und durchs LLM traversieren kann. Diese Lösungen sind aber noch experimentell. Evtl. Kombination mit Obsidian-Plugin: Ein früher Plugin-Prototyp streamte Obsidian-Daten nach Neo4j <span class="citation" data-cites="export_to_common_graph_formats">[7]</span> – dieser könnte als Ausgangspunkt dienen.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<div class="cell page-columns page-full" data-layout-align="default">
|
||
<div class="cell-output-display column-screen-inset-right">
|
||
<div>
|
||
<p></p><figure class="figure"><p></p>
|
||
<div>
|
||
<pre class="mermaid mermaid-js">graph LR
|
||
A[Obsidian Vault] --> B[Entity Extraction]
|
||
A --> C[Relationship Extraction]
|
||
B --> D[Graph Construction]
|
||
C --> D
|
||
D --> E[(Graph Database)]
|
||
F[User Query] --> G[Query Parser]
|
||
G --> H[Graph Traversal]
|
||
E --> H
|
||
H --> I[Structured Facts]
|
||
I --> J[LLM]
|
||
F --> J
|
||
J --> K[Response]
|
||
</pre>
|
||
</div>
|
||
<p></p></figure><p></p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<section id="fazit-graphdatenbank" class="level3">
|
||
<h3 class="anchored" data-anchor-id="fazit-graphdatenbank">Fazit Graphdatenbank</h3>
|
||
<p>Ein Wissensgraph spielt seine Stärken vor allem bei <strong>strukturbezogenen Aufgaben</strong> aus. Für das automatische Ausfüllen von YAML-Feldern oder das Prüfen von Verlinkungen ist er ideal, da solche Fragen direkte Graphabfragen ermöglichen. Auch für neuartige Verknüpfungen (Link-Vorschläge) lässt sich ein Graph analytisch nutzen (z.B. “Link Prediction” auf Basis von Graph-Nachbarschaft). Allerdings ist die Umsetzung deutlich komplexer als beim Vektorstore, und viele RAG-Anwendungsfälle (Zusammenfassungen, inhaltliche Q&A) erfordern trotzdem den Rückgriff auf die eigentlichen Texte – was wiederum den Vektoransatz benötigt. Daher bietet sich oft eine Kombination beider Methoden an.</p>
|
||
</section>
|
||
</section>
|
||
<section id="hybrid-ansatz-kombination-aus-graph-und-vektor-rag" class="level2 page-columns page-full">
|
||
<h2 class="anchored" data-anchor-id="hybrid-ansatz-kombination-aus-graph-und-vektor-rag">3. Hybrid-Ansatz: Kombination aus Graph und Vektor-RAG</h2>
|
||
<p>Dieser Ansatz versucht, <strong>semantische Textsuche</strong> und <strong>strukturierte Graph-Abfragen</strong> zu vereinen, um die Vorteile beider Welten auszuschöpfen. In der Praxis gibt es mehrere Möglichkeiten, wie ein hybrides System ausgestaltet sein kann:</p>
|
||
<ul>
|
||
<li><strong>Parallelbetrieb mit separaten Pipelines:</strong> Vektorstore und Knowledge Graph werden beide gepflegt. Je nach Anfrage oder Teilaufgabe wird das eine oder andere genutzt. Beispiel: Für eine Q&A-Frage holt das System erst relevante Text-Passagen via Vektorstore, <strong>und</strong> prüft zusätzlich im Graph, welche Entitäten darin vorkommen und ruft deren Beziehungen ab. Das LLM bekäme dann sowohl inhaltliche Ausschnitte als Kontext als auch strukturierte Fakten (z.B. <em>“Alice arbeitetBei AcmeCorp”</em>) als Knowledge-Panel. Für die Aufgabe <em>Link-Vorschläge</em> könnte das System sowohl einen Embedding-Vergleich zwischen Notizen nutzen (um thematisch ähnliche Notes zu finden), als auch den Graphen auswerten (um strukturell nahe, aber unverbundene Knoten zu entdecken). Die finalen Vorschläge wären die <strong>Schnittmenge</strong> bzw. <strong>Union</strong> beider Methoden – das erhöht Präzision und Reichweite der Empfehlungen.</li>
|
||
<li><strong>Integration innerhalb einer Datenplattform:</strong> Moderne Vector-Datenbanken wie <em>Weaviate</em> erlauben es, semantische Vektorsuche mit symbolischen Filtern zu kombinieren. Man kann Objekte (hier: Notizen) mit ihren strukturierten Feldern in Weaviate speichern und neben dem Vektorindex auch die Metadaten abfragen. Z.B. könnte man eine Query formulieren: <em>“Gib mir die 5 ähnlichsten Notizen zu <code>[Text]</code>, die vom Typ Projekt sind und nach 2020 erstellt wurden.”</em> – Weaviate würde erst nach Ähnlichkeit filtern, dann die Metadaten-Bedingungen anwenden. So eine <strong>hybride Suche</strong> könnte man nutzen, um etwa bei Template-Befüllung <strong>nur vergleichbare Objekte</strong> zum Prompt hinzuzufügen (z.B. nur andere Organisationen, keine Meeting-Notizen). Auch ChromaDB arbeitet an Feature-Filterfunktionen, die so etwas erlauben würden. Alternativ kann man den Graphen selbst mit Vektor-Embeddings anreichern: Man könnte jedem Knotentyp einen eigenen Vektor zuordnen, der den gesamten Inhalt der zugehörigen Notiz(en) repräsentiert. Diese Vektoren ließen sich im Graphen als Attribut halten und für Ähnlichkeitssuchen zwischen Knoten verwenden (<em>knowledge graph embeddings</em>). Allerdings ist das experimentell – man müsste z.B. bei Kanten-Traversierung dynamisch Nachbarschaftsvektoren kombinieren, was nicht trivial ist.</li>
|
||
<li><strong>LLM als Orchestrator:</strong> Hier steuert das LLM, wann welcher Ansatz gezogen wird. Beispielsweise könnte man ein System bauen, in dem das LLM zunächst entscheidet: <em>“Brauche ich strukturiertes Wissen?”</em> Wenn ja, könnte es per Tool-Use einen Graph-Query durchführen (z.B. via Cypher) – eine Technik, die mit LangChain Agents umsetzbar wäre. Danach würde es ggf. einen zweiten Schritt machen: <em>“Benötige ich noch Detailinformationen oder Zitate?”</em> – dann die Vektor-Datenbank abfragen, relevante Textstücke holen, und schließlich alles in einer konsolidierten Antwort formulieren. Dieser agentenbasierte Ansatz ist sehr flexibel, aber auch am anspruchsvollsten in der Implementierung (er erfordert zuverlässig trainierte/verfeinerte LLM-Prompts, die wissen, wann und wie die jeweiligen Werkzeuge zu benutzen sind).</li>
|
||
</ul>
|
||
<section id="vor-nachteile" class="level3">
|
||
<h3 class="anchored" data-anchor-id="vor-nachteile">Vor-/Nachteile</h3>
|
||
<p>Die Hybridlösung verspricht <strong>maximale Abdeckung</strong> der Anwendungsfälle. Strukturierte Fakten und unstrukturierte Inhalte können gemeinsam dem LLM präsentiert werden, was sowohl präzise Faktenkenntnis als auch reichhaltigen Kontext ermöglicht. Gerade für komplexe Aufgaben – etwa das automatisierte Erstellen einer neuen Entitätenseite – wären wohl beide Aspekte wichtig: das LLM müsste sich an vorhandenen ähnlichen Seiten <strong>inhaltlich</strong> orientieren (Vektorsuche nach ähnlichen Organisations-Beschreibungen) und zugleich <strong>korrekte Verknüpfungen</strong> setzen (Graph checken, ob z.B. die neue Organisation bereits Personen im Vault hat, die als Mitarbeiter verknüpft werden sollten). Ein solches System könnte also dem Nutzer sehr viel Arbeit abnehmen und dabei konsistente, vernetzte Notizen erzeugen.</p>
|
||
<p>Dem steht jedoch ein hoher <strong>Architekturund Wartungsaufwand</strong> gegenüber. Man muss im Grunde zwei Systeme aufbauen und aktuell halten. Zudem ist die Logik, wie die Ergebnisse zusammenfließen, nicht trivial. Ohne gutes Design kann es passieren, dass der Graph-Teil und der Vektor-Teil widersprüchliche oder redundante Informationen liefern. Auch muss man Performance beachten – doppelte Abfragen kosten mehr Zeit. In vielen Fällen mag auch ein einzelner Ansatz ausreichen, sodass die Zusatzkomplexität nicht immer gerechtfertigt ist.</p>
|
||
</section>
|
||
<section id="integrationsmöglichkeiten" class="level3">
|
||
<h3 class="anchored" data-anchor-id="integrationsmöglichkeiten">Integrationsmöglichkeiten</h3>
|
||
<p>Auf technischer Seite ist so ein hybrides System durchaus machbar. Beispielsweise ließe sich <strong>LlamaIndex</strong> verwenden, um unterschiedliche Indexe (VectorIndex, KnowledgeGraphIndex) zu kombinieren – es gibt Konzepte wie “Composable Indices”, mit denen man hierarchische Abfragen bauen kann. So könnte man erst den Graph nach relevanten Knoten filtern und dann nur die zugehörigen Dokumente vektor-suchen (oder umgekehrt). Weaviate als All-in-one-Lösung wurde bereits erwähnt. In kleineren Umgebungen kann man auch pragmatisch vorgehen: Ein Python-Skript, das bei bestimmten Fragen zuerst einen Neo4j-Query absetzt und dessen Ergebnis dem LLM als Teil des Prompts voranstellt, während es parallel eine Chroma-Query macht, wäre eine einfache implementierbare Variante.</p>
|
||
</section>
|
||
<section id="zusammenfassung-ansatz-3-hybrid-lösung" class="level3 page-columns page-full">
|
||
<h3 class="anchored" data-anchor-id="zusammenfassung-ansatz-3-hybrid-lösung">Zusammenfassung – Ansatz 3: Hybrid-Lösung</h3>
|
||
<table class="caption-top table">
|
||
<colgroup>
|
||
<col style="width: 5%">
|
||
<col style="width: 94%">
|
||
</colgroup>
|
||
<thead>
|
||
<tr class="header">
|
||
<th></th>
|
||
<th><strong>Details</strong></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="odd">
|
||
<td><strong>Vorgehen</strong></td>
|
||
<td>Kombination beider Ansätze: Pflege einer Graph-Struktur <strong>und</strong> eines Vektorindex. Nutzung je nach Bedarf – entweder separat oder durch orchestrierte Abfragen, um sowohl strukturiertes Wissen als auch relevante Texte bereitzustellen.</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td><strong>Stärken</strong></td>
|
||
<td>Sehr <strong>leistungsfähig</strong> – deckt sowohl faktische als auch kontextuelle Fragen ab. Kann die <strong>höchste Antwortqualität</strong> liefern (Konsistenz durch Graph-Fakten, Detail durch Textauszüge). Hilft, sowohl “Known-item” Suchen (explizite Werte) als auch “Open-ended” Suchen (Texte) zu bedienen. Für Link-Vorschläge ideal: Kombination aus semantischer Ähnlichkeit und Graph-Nachbarschaft erhöht Trefferquote sinnvoll.</td>
|
||
</tr>
|
||
<tr class="odd">
|
||
<td><strong>Schwächen</strong></td>
|
||
<td><strong>Sehr komplex</strong> in Umsetzung und Wartung. Erfordert doppelte Infrastruktur. Koordination zwischen Graph und Vectorstore nötig – potenziell fehleranfällig. Höhere Latenz durch Mehrfach-Abfragen. Nur lohnend, wenn wirklich vielfältige Aufgaben automatisiert werden sollen; für rein textliche Q&A overkill.</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td><strong>Integrations-Optionen</strong></td>
|
||
<td>Weaviate (Vectors + strukturierte Class-Properties in einem System), oder Kombination aus Neo4j + Chroma. LangChain Agents könnten Graphund Vektor-Tools parallel nutzen. <strong>LlamaIndex</strong> bietet experimentell kombinierbare Indizes. Workflows müssen sorgfältig entworfen werden (z.B. zuerst Graph-Query, dann Vector-Query auf Untermenge).</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<div class="cell page-columns page-full" data-layout-align="default">
|
||
<div class="cell-output-display column-screen-inset-right">
|
||
<div>
|
||
<p></p><figure class="figure"><p></p>
|
||
<div>
|
||
<pre class="mermaid mermaid-js">graph LR
|
||
A[Obsidian Vault] --> B[Chunking]
|
||
A --> C[Entity Extraction]
|
||
B --> D[Embedding Model]
|
||
C --> E[Graph Construction]
|
||
D --> F[(Vector Database)]
|
||
E --> G[(Graph Database)]
|
||
H[User Query] --> I[Query Embedding]
|
||
H --> J[Graph Query]
|
||
I --> K[Vector Search]
|
||
J --> L[Graph Traversal]
|
||
F --> K
|
||
G --> L
|
||
K --> M[Text Context]
|
||
L --> N[Structured Facts]
|
||
M --> O[Combined Context]
|
||
N --> O
|
||
H --> P[LLM]
|
||
O --> P
|
||
P --> Q[Enriched Response]
|
||
</pre>
|
||
</div>
|
||
<p></p></figure><p></p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<section id="fazit-hybrid-lösung" class="level3">
|
||
<h3 class="anchored" data-anchor-id="fazit-hybrid-lösung">Fazit Hybrid-Lösung</h3>
|
||
<p>Die Hybrid-Lösung ist die <strong>ambitionierteste</strong>, aber auch zukunftsträchtigste Option. Sie empfiehlt sich, wenn sowohl inhaltliche Assistenz (Texte zusammenfassen, beantworten) als auch datenbankartige Operationen (Felder validieren, Beziehungen auswerten) gefragt sind – was hier der Fall ist. Oft kann man auch schrittweise vorgehen: zunächst mit einem Vektor-RAG starten (geringerer Aufwand) und dann gezielt Graph-Features ergänzen, sobald z.B. Link-Empfehlungen oder Konsistenzprüfungen wichtiger werden.</p>
|
||
</section>
|
||
</section>
|
||
<section id="datenaufbereitung-yaml-metadaten-extrahieren-und-normalisieren" class="level2 page-columns page-full">
|
||
<h2 class="anchored" data-anchor-id="datenaufbereitung-yaml-metadaten-extrahieren-und-normalisieren">4. Datenaufbereitung: YAML-Metadaten extrahieren und normalisieren</h2>
|
||
<p>Unabhängig vom gewählten Retrieval-Ansatz ist es essenziell, die in YAML front matter steckenden strukturierten Informationen effektiv zu nutzen. Die Obsidian-Plugins <em>Metadata Menu</em> und <em>Templater</em> stellen sicher, dass viele wichtige Daten bereits sauber in den Notizen vorliegen (z.B. hat eine Personenseite Felder wie <code>fullname</code>, <code>birthdate</code>, <code>ORCID</code> usw.). Ein LLM könnte zwar theoretisch auch direkt im Markdown nach diesen Mustern suchen, aber es ist deutlich effizienter, die Daten einmalig zu <strong>extrahieren</strong> und in einer leichter nutzbaren Form vorzuhalten.</p>
|
||
<section id="extraktion" class="level3">
|
||
<h3 class="anchored" data-anchor-id="extraktion">Extraktion</h3>
|
||
<p>Ein möglicher Schritt im täglichen Refresh ist ein Skript, das alle Dateien durchläuft und die YAML-Blöcke parst (z.B. mit einem YAML-Parser in Python oder JavaScript). Die extrahierten Felder können dann in eine <strong>normale Datenbank</strong> (SQLite/CSV/JSON) oder direkt als Knoten/Properties in den Knowledge Graph überführt werden. Damit erhält man z.B. eine Tabelle aller Personen mit ihren ORCID-IDs, eine Liste aller Projekte mit Start-/Enddatum etc.</p>
|
||
</section>
|
||
<section id="normalisierung" class="level3">
|
||
<h3 class="anchored" data-anchor-id="normalisierung">Normalisierung</h3>
|
||
<p>Oft müssen die Rohwerte etwas vereinheitlicht werden. Beispielsweise sollten Datumsangaben ein konsistentes Format haben, Personennamen evtl. in Vor-/Nachname zerlegt, und fehlende Felder explizit als <code>null</code> markiert werden. Außerdem kann man hier foreign-key-Bezüge auflösen: Wenn z.B. im YAML einer Publikation <code>author: "[[Doe, John]]"</code> steht, könnte das Skript erkennen, dass dies die Person mit UID XYZ ist, und entsprechend in der extrahierten Struktur statt des Link-Codes einen eindeutigen Verweis (auf die Person John Doe) speichern. Diese Normalisierung erleichtert nachfolgende Analysen enorm – insbesondere kann man einfache Regeln ableiten, die dann vom LLM geprüft oder genutzt werden. Zum Beispiel: <em>“Wenn <code>person.ORCID</code> leer ist, schlage vor, ihn zu ergänzen”</em> – das kann das LLM dann direkt als Aufforderung bekommen. Oder: <em>“Beim Erstellen einer neuen Person fülle Felder X,Y nach Vorlage aus”</em> – hier weiß man aus der YAML-Definition bereits, welche Felder existieren müssen.</p>
|
||
</section>
|
||
<section id="nutzung-durch-llm" class="level3">
|
||
<h3 class="anchored" data-anchor-id="nutzung-durch-llm">Nutzung durch LLM</h3>
|
||
<p>Der aufbereitete YAML-Datensatz kann auf zwei Weisen eingebunden werden:</p>
|
||
<ul>
|
||
<li><strong>Inline im Prompt:</strong> Für bestimmte Aufgaben kann man dem LLM direkt Ausschnitte aus dieser strukturierten Sammlung geben. Etwa: <em>“In unserer Datenbank fehlt für <code>Person[42]</code> der ORCID. Hier ist eine Liste aller Personennamen mit ORCID, finde anhand des Namens den passenden ORCID und trage ihn ein.”</em> – Falls die Person woanders erwähnt wurde, könnte das Modell es herausfinden. (Eher unwahrscheinlich ohne Internetzugriff – ORCID erfordert eher einen API-Call, aber zumindest könnte das LLM erkennen, <em>dass</em> es fehlt und ggf. den Nutzer nach der ID fragen). Für Link-Empfehlungen könnte man dem LLM eine Liste aller Titel geben – oder besser direkt die Graph-Info wie <em>“Person A und Person B haben 3 gemeinsame Projekte”</em> – siehe Hybrid-Ansatz.</li>
|
||
<li><strong>Programmatisch außerhalb des LLMs:</strong> Viele Routineaufgaben lassen sich erkennen, ohne das LLM zu bemühen. Man könnte einen Teil der Automatisierung rein mit Skripten vorab erledigen. Z.B. neue Links: Ein Skript könnte alle Personennamen im Fließtext durchsuchen und prüfen, ob sie bereits als <code>[[Link]]</code> markiert sind; wenn nicht, die Stelle hervorheben und dem LLM als <em>“Kandidat für Verlinkung”</em> präsentieren. Oder bei einer neuen Organisation könnten automatisch Felder aus externen APIs gezogen und ins Template eingetragen werden (sofern erlaubt). Das LLM hätte dann eher die Rolle, die zusammengestellten Infos in schönen Prosa-Text zu gießen, anstatt die Fakten selbst zu suchen.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="beispiel-workflows" class="level3 page-columns page-full">
|
||
<h3 class="anchored" data-anchor-id="beispiel-workflows">Beispiel-Workflows</h3>
|
||
<ul>
|
||
<li>YAML-Exports lassen sich mit vorhandenen Tools unterstützen. Es gibt z.B. das Obsidian-Plugin <em>Dataview</em>, welches Abfragen auf YAML ermöglichen kann – allerdings nur innerhalb Obsidian. Man könnte aber ein Dataview JS-Skript<sup>4</sup> schreiben, das alle Einträge eines Typs ausgibt, und diese Output-Datei dann weiterverarbeiten. Alternativ direkt auf Dateisystemebene arbeiten: Python mit <code>os</code> und <code>pyyaml</code> kann alle <code>.md</code> Files scannen.</li>
|
||
<li>Die extrahierten Daten kann man mit dem Graph-Ansatz koppeln: etwa alle Personen ohne ORCID als Cypher-Query generieren lassen und automatisch in eine “ToDo”-Liste (Obsidian Note) schreiben, die vom LLM oder Nutzer geprüft wird.</li>
|
||
<li>Durch Templates sind die Felder pro FileClass ja bekannt. Diese Knowledge kann ins Prompt fließen: <em>“Eine Organisation hat die Felder Name, Typ, Beschreibung, Mitarbeiter, etc. Fülle basierend auf den folgenden Infos…”</em> Das Modell weiß dann genau, welche YAML-Spalten es ausgeben soll.</li>
|
||
</ul>
|
||
<div class="no-row-height column-margin column-container"><div id="fn4"><p><sup>4</sup> oder Plugins wie <em>Dataview-Publisher</em> benutzen, die die Ergebnisse als Markdown-Tabell in ein Dokument schreiben</p></div></div></section>
|
||
<section id="vor--nachteile" class="level3 page-columns page-full">
|
||
<h3 class="anchored" data-anchor-id="vor--nachteile">Vor- & Nachteile</h3>
|
||
<p>Die <strong>Vorteile</strong> der strukturierten Extraktion liegen auf der Hand – Performance und Präzision. Man muss nicht jedes Mal den gesamten Markdown-Text durchsuchen, um z.B. den Wert eines bestimmten Feldes zu finden; man hat ihn direkt. Außerdem reduziert es die Abhängigkeit vom LLM für einfache Aufgaben (Daten finden, vergleichen). Für die meisten Menschen ist es auch leichter zu verstehen und zu prüfen, wenn man z.B. eine CSV mit allen ORCIDs hat, als wenn man dem LLM blind glauben muss.<br>
|
||
Als <strong>Nachteil</strong> kann gesehen werden, dass es zusätzlicher Implementierungsaufwand ist und eine gewisse Duplizierung der Daten (die YAML-Inhalte leben dann in zwei Formen: im Markdown und in der extrahierten Sammlung). Die Synchronisation muss bei Änderungen immer gewährleistet sein (Cronjob). Allerdings ist das, verglichen mit dem Aufwand der LLM-Integration, relativ gering und gut automatisierbar.</p>
|
||
<div class="cell page-columns page-full" data-layout-align="default">
|
||
<div class="cell-output-display column-screen-inset-right">
|
||
<div>
|
||
<p></p><figure class="figure"><p></p>
|
||
<div>
|
||
<pre class="mermaid mermaid-js">graph LR
|
||
A[Obsidian Vault] --> B[FileClass Detection]
|
||
B --> C[Type-Specific Extraction]
|
||
C --> D[YAML Parser]
|
||
D --> E[Data Validation]
|
||
E --> F[Type Normalization]
|
||
F --> G[(Typed Collections)]
|
||
H[Task Request] --> I[Schema Lookup]
|
||
I --> J[Targeted Data Fetch]
|
||
G --> J
|
||
J --> K[Context Assembly]
|
||
H --> K
|
||
K --> L[LLM Processing]
|
||
L --> M[Schema-Aware Output]
|
||
</pre>
|
||
</div>
|
||
<p></p></figure><p></p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<section id="zusammenfassung-ansatz-4-extraktion" class="level3">
|
||
<h3 class="anchored" data-anchor-id="zusammenfassung-ansatz-4-extraktion">Zusammenfassung – Ansatz 4: Extraktion</h3>
|
||
<p>In jedem Fall sollte man eine Pipeline vorsehen, die die YAML-<strong>Metadaten extrahiert</strong> und in eine strukturierte Form bringt. Diese bildet das Rückgrat für den Knowledge-Graph-Ansatz (ohne diese wären die Knoten nackte Titel ohne Attribute) und ist auch für Vektor-RAG nützlich (z.B. als Filter oder zur post-processing der LLM-Antworten). Insbesondere dank der FileClass-Typisierung im Vault kann man hier sehr <strong>zielgerichtet</strong> vorgehen – etwa nur definierte Entitätstypen verarbeiten. In Community-Diskussionen wurde vorgeschlagen, YAML-Metadaten zu nutzen, um AI-Aufgaben einzuschränken: z.B. NER-Modelle nur auf bestimmten Notizen laufen zu lassen, die laut YAML einen bestimmten Typ haben <span class="citation" data-cites="ai_empowered_zettelkasten_with_ner_and_graph_llm">[9]</span>. Solche Optimierungen werden durch saubere strukturelle Aufbereitung erst möglich.</p>
|
||
</section>
|
||
</section>
|
||
<section id="automatisierungstools-und-workflows" class="level2 page-columns page-full">
|
||
<h2 class="anchored" data-anchor-id="automatisierungstools-und-workflows">5. Automatisierungstools und Workflows</h2>
|
||
<p>Für die Umsetzung der oben beschriebenen Ansätze gibt es bereits einige Tools, Projekte und Best Practices, die man nutzen oder von denen man lernen kann. Hier eine strukturierte Übersicht samt Empfehlungen:</p>
|
||
<section id="obsidian-plugins-in-app-ki-features" class="level3">
|
||
<h3 class="anchored" data-anchor-id="obsidian-plugins-in-app-ki-features">Obsidian-Plugins (In-App KI-Features)</h3>
|
||
<ul>
|
||
<li><em>Smart Connections:</em> Plugin, das innerhalb Obsidian mit lokalen Embeddings arbeitet, um <strong>ähnliche Notizen</strong> zu finden, und einen Chatbot bereitstellt. Es kann ein lokales LLM (oder OpenAI API) einbinden und versorgt es automatisch mit Kontext aus dem Vault <span class="citation" data-cites="smart_connections_plugin">[2]</span>. Vorteil: einfache Installation, enge Vault-Integration (Antworten können direkt als Notiz eingefügt werden). Nachteil: begrenzt anpassbar – der Workflow ist vordefiniert (hauptsächlich Q&A Chat). Für den Start aber exzellent, um ein Gefühl für RAG im eigenen Vault zu bekommen.</li>
|
||
<li><em>Khoj:</em> Ein Open-Source Projekt, bestehend aus einem lokalen Backend <span class="citation" data-cites="khoj_plugin">[4]</span> und Obsidian-Plugin. Ermöglicht <strong>natürliche Sprachsuche</strong> und Chat über die eigenen Notizen <span class="citation" data-cites="khoj_plugin">[4]</span>. Es kann sowohl online-Modelle (GPT-4 etc.) als auch lokale Modelle nutzen <span class="citation" data-cites="build_your_second_brain_with_khoj_ai">[10]</span>. Khoj fokussiert auf schnelle semantische Suche; der Chat-Teil ist vor allem QA-orientiert. Als persönlicher Suchassistent ist es sehr interessant – etwa um via Obsidian Command Palette Fragen ans Vault zu stellen. Es ist weniger darauf ausgelegt, automatisch Links zu erzeugen oder YAML zu verändern (dafür wäre wiederum ein LLM mit Schreibrechten nötig).</li>
|
||
<li><em>Obsidian Copilot / GPT-Assistant:</em> Es existieren mehrere Plugins, die GPT-3/4 in Obsidian integrieren (teils auch lokal via LLaMA). Diese sind im Prinzip UI-Verbesserungen, um das LLM “im Editor” zu nutzen. Für RAG kann man sie einsetzen, indem man manuell Kontext reinkopiert, aber automatisches Retrieval bieten sie nicht ohne weiteres.</li>
|
||
<li><em>Obsidian Neo4j Plugin (Experimentell):</em> Das erwähnte <em>obsidian-neo4j-stream</em> von @HEmile <span class="citation" data-cites="export_to_common_graph_formats">[7]</span> könnte als Ausgangspunkt dienen, falls man die Graph-Route ausprobieren will. Es war dazu gedacht, den Vault als kontinuierlichen Stream in Neo4j zu spiegeln. Leider wurde es nicht fertiggestellt/maintained. Dennoch ließe sich der Code evtl. anpassen, um zumindest einmalig einen Export durchzuführen. Alternativ: Im Obsidian-Forum gibt es auch Beispiele, wie man mit ein paar Skriptzeilen alle Links extrahieren kann. Zusammen mit den YAML-Daten könnte man so einen Basic-Graphen schon bekommen.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="externe-anwendungen-skripte" class="level3 page-columns page-full">
|
||
<h3 class="anchored" data-anchor-id="externe-anwendungen-skripte">Externe Anwendungen / Skripte</h3>
|
||
<ul>
|
||
<li><em>LlamaIndex (GPT Index):</em> Diese Python-Bibliothek ist eine <strong>Schweizer Taschenmesser</strong> für RAG. Man kann Dokumente laden (Markdown wird unterstützt), unterschiedliche Indizes erstellen (Vector, List, KnowledgeGraph etc.) und Abfragen mit LLM orchestrieren. Sie eignet sich, um schnell Prototypen zu bauen. Beispielsweise könnte man einen <strong>KnowledgeGraphIndex</strong> erstellen, der mittels Instruct-LLM Tripel aus den Notizen extrahiert (z.B. “Person X – arbeitet für – Organisation Y”). Anschließend kann man Abfragen in natürlicher Sprache stellen, die vom LLM in Graph-Traversals übersetzt werden. Oder man nutzt den simpleren VectorIndex auf Markdown-Chunks. LlamaIndex kann auch <strong>Komposition</strong>: man könnte pro FileClass einen Index bauen (z.B. alle Personen in einem VectorIndex, alle Projekte in einem anderen) und dann einen übergeordneten Query laufen lassen. Diese Flexibilität ist mächtig – aber es erfordert eben etwas Programmierung. Für einen produktiven Workflow (täglicher Cronjob) müsste man ein eigenes Python-Skript schreiben, das die Indizes aktualisiert.</li>
|
||
<li><em>LangChain:</em> Ein Framework v.a. für komplexere Chains und Agenten. Es liefert Bausteine, um z.B. eine Tool-using Agent zu bauen, die mit einer <strong>Vector DB Suche</strong> und einer <strong>Graph-DB Abfrage</strong> als Tools ausgestattet ist. Damit ließe sich ein Dialogsystem kreieren, das je nach Frage entscheidet, ob es den Neo4j-Graph oder den Chroma-Vektorindex konsultiert. Allerdings setzt dies einiges an Prompt Engineering voraus, damit der Agent zuverlässig funktioniert. Alternativ kann man LangChain auch einfach nutzen, um entweder Vector-search oder Graph-DB-Queries einzeln bequemer zu machen (es gibt z.B. vorgefertigte Neo4j Retriever-Klassen etc.).</li>
|
||
<li><em>Haystack:</em> Das von deepset (evtl. in der Frage mit “Deepseek” gemeint) entwickelte Open-Source-Toolkit <strong>Haystack</strong> ist ebenfalls auf Dokumenten-QA spezialisiert. Es unterstützt das Indexieren von Markdown, verschiedene Vector-Backends und kann auch Knowledge-Graph-Komponenten integrieren. Zudem hat es Pipeline-Knoten zum z.B. Fragenklassifizieren, dass bestimmte Fragen an bestimmte Reader geleitet werden. Für einen produktiven Einsatz mit lokalem UI ggf. eine Option. Allerdings eher heavy-weight und auf QA fokussiert, weniger auf Wissensbasis-Pflege.</li>
|
||
<li><em>privateGPT / llama.cpp based scripts:</em> Für einfache Frage-Antwort-Systeme auf dem eigenen Vault kann man vorhandene Lösungen wie <em>privateGPT</em> oder <em>GPT4All</em> (mit UI) verwenden <span class="citation" data-cites="second_brain_assistant_with_obsidian">[3]</span>. Diese bringen einen Großteil der Vector+LLM Pipeline schon fertig mit. Sie indexieren Ordner voller Dokumente (auch Markdown) und erlauben dann Queries an ein lokales Modell. Der Anpassungsspielraum (z.B. andere Tasks als reines QA) ist aber gering. Als <strong>Baseline</strong> sind sie nützlich – man könnte damit z.B. testen, wie gut ein LLM mit den eingebetteten Obsidian-Notizen Fragen beantwortet, und daraus Anforderungen ableiten.</li>
|
||
<li><em>Basic Memory (basicmachines):</em> Ein innovativer Ansatz ist hier zu erwähnen: <strong>Basic Memory</strong> speichert AI-Konversationen als Markdown in Obsidian und baut daraus sukzessive einen semantischen Wissensgraph <span class="citation" data-cites="basic_memory_ai_conversations_that_build_knowledge">[11]</span>. D.h. wenn man mit dem LLM chatbasiert arbeitet, erstellt das Tool automatisch Notizen und verbindet sie (z.B. werden erkannte Entitäten verlinkt). Es ist quasi das Gegenstück zu unserem Problem – statt einen bestehenden Vault zu nutzen, erzeugt es einen Vault. Dennoch kann man sich dort Konzepte abschauen: z.B. wie strukturierte Notizen aus LLM-Ausgaben generiert werden können, oder wie man <em>bi-direktional</em> arbeitet (User editiert Notiz, KI liest Änderungen beim nächsten Mal). Basic Memory setzt auf lokale Dateien und betont Privatsphäre, was dem hiesigen Anforderungsprofil ähnelt. Für die konkreten Aufgaben (ORCID-Suche, Link-Vorschlag) liefert es zwar keine fertige Lösung, aber die <strong>Idee, KI beim Nutzer Notizen anlegen/ändern zu lassen,</strong> ist hier praktisch umgesetzt.</li>
|
||
<li><strong>Externe APIs / Datenquellen:</strong><br>
|
||
Für bestimmte Felder wie ORCID wird ein rein lokales LLM kaum die Werte erraten können, sofern sie nicht schon irgendwo im Vault stehen. Falls Internetzugriff eine Option ist, könnte man ein Plugin oder einen Workflow integrieren, der <strong>ORCID API</strong> Abfragen durchführt (z.B. über den Namen der Person) und die ID zurückliefert. Ein LLM-Agent könnte auch so einen API-Call ausführen (via Tools in LangChain). Alternativ: Alle bekannten ORCID-IDs der eigenen Personen könnte man in einer Datei sammeln; wenn das LLM eine Lücke findet, bittet es den Nutzer um Input. Hier muss man die Limitierungen eines LLM realistisch sehen und ggf. klassische Automatisierung (API-Skripte) kombinieren.</li>
|
||
</ul>
|
||
<div class="cell page-columns page-full" data-layout-align="default">
|
||
<div class="cell-output-display column-screen-inset-right">
|
||
<div>
|
||
<p></p><figure class="figure"><p></p>
|
||
<div>
|
||
<pre class="mermaid mermaid-js">graph LR
|
||
subgraph Obsidian
|
||
A[Vault] --> B[Plugins]
|
||
B --> C[Templater]
|
||
B --> D[Metadata Menu]
|
||
B --> E[AI Assistant]
|
||
end
|
||
|
||
subgraph External Processing
|
||
A --> F[Daily Export]
|
||
F --> G[Data Processing]
|
||
G --> H[LLM Analysis]
|
||
H --> I[Automation Scripts]
|
||
end
|
||
|
||
subgraph Integration
|
||
I --> J[Change Proposals]
|
||
J --> K[User Review]
|
||
K --> L[Accepted Changes]
|
||
L --> M[Vault Updates]
|
||
M --> A
|
||
end
|
||
</pre>
|
||
</div>
|
||
<p></p></figure><p></p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</section>
|
||
<section id="zusammenfassende-empfehlung" class="level2">
|
||
<h2 class="anchored" data-anchor-id="zusammenfassende-empfehlung">Zusammenfassende Empfehlung</h2>
|
||
<p>Für einen ersten Prototypen empfiehlt es sich, mit dem <strong>Vektorstore-Ansatz (1)</strong> zu beginnen, da dieser am schnellsten sichtbare Erfolge bringt. Man kann z.B. mit ChromaDB + einem lokalen LLM experimentieren, oder direkt das Smart-Connections-Plugin ausprobieren, um ein Gefühl für semantische Suche im Vault zu bekommen. Die YAML-Daten sollte man von Anfang an <strong>mit-extrahieren (4)</strong>, da sie die Grundlage für weitere Strukturierungsmaßnahmen bilden. Anschließend kann man gezielt <strong>Graph-Features (2)</strong> ergänzen: etwa den exportierten Vault in Neo4j laden und ein paar Abfragen formulieren, um Missing Links oder fehlende Felder aufzuspüren. Mittelfristig dürfte eine <strong>Kombination (3)</strong> notwendig sein, um sowohl Inhalt als auch Struktur abzudecken – dies kann man Schritt für Schritt angehen (z.B. zunächst Vector-RAG für inhaltliche Fragen, und separate Tools/Reports für strukturierte Checks; später dann Integration zu einem einheitlichen KI-Assistenten). Unterstützend sollte man vorhandene <strong>Tools (5)</strong> nutzen, wo möglich – z.B. Khoj für ad-hoc Fragen, oder LlamaIndex für schnelle Implementierung von Prototypen. Generell gilt: lokale LLMs sind inzwischen leistungsfähig genug für solche Aufgaben, wie die genannten Beispiele zeigen (Chat mit Vault über LLaMA etc.). Wichtig ist es, die <strong>Vault-Organisation</strong> konsequent weiterzuführen (FileClasses, Templates), da ein sauber strukturiertes Wissen die Grundlage für jede erfolgreiche RAG-Lösung ist – egal ob Vektor, Graph oder hybrid.</p>
|
||
</section>
|
||
<section id="quellen" class="level2">
|
||
<h2 class="anchored" data-anchor-id="quellen">Quellen</h2>
|
||
<p>Die Analyse basiert auf aktuellen Erkenntnissen aus der Obsidian-Community und KI-Fachwelt, u.a. Erfahrungen mit semantischer Suche <span class="citation" data-cites="smart_connections_plugin">[2]</span>, Diskussionen zu Knowledge Graphs in PKM <span class="citation" data-cites="ai_empowered_zettelkasten_with_ner_and_graph_llm">[9]</span> und Berichten über lokale RAG-Implementierungen <span class="citation" data-cites="local_free_rag_with_question_generation">[2]</span>.</p>
|
||
</section>
|
||
|
||
|
||
|
||
<div id="quarto-appendix" class="default"><section id="methodik-llms-als-autoren" class="level2 appendix"><h2 class="anchored quarto-appendix-heading">Methodik / LLMs als ‘Autoren’</h2><div class="quarto-appendix-contents">
|
||
|
||
<p>Erstellt wurde der initial draft mittels Websuche und “Deep-Research” von <code>gpt-4.5 (preview)</code>. Systematische Überarbeitungen (Extraktion Bibliographie, Überarbeitung Metadaten) mittels <code>cogito-v0.1</code> im Editor. Übernahme nach manueller Prüfung. Erstellung der Mermaid-Diagramme mittels <code>Claude 3.7 Sonnet</code>. Abschließendes Korrekturlesen/inhaltliche Prüfung/Layouting durch Nicole Dresselhaus.</p>
|
||
|
||
|
||
|
||
</div></section><section class="quarto-appendix-contents" id="quarto-bibliography"><h2 class="anchored quarto-appendix-heading">Literatur</h2><div id="refs" class="references csl-bib-body" data-entry-spacing="0">
|
||
<div id="ref-ollama_chroma_cookbook" class="csl-entry">
|
||
<div class="csl-left-margin">1. </div><div class="csl-right-inline"><a href="https://cookbook.chromadb.dev/integrations/ollama/embeddings/">Ollama - Chroma Cookbook</a>. 2024.</div>
|
||
</div>
|
||
<div id="ref-smart_connections_plugin" class="csl-entry">
|
||
<div class="csl-left-margin">2. </div><div class="csl-right-inline"><a href="https://www.reddit.com/r/ObsidianMD/comments/1fzmkdk/just_wanted_to_mention_that_the_smart_connections/">Just wanted to mention that the smart connections plugin is incredible. : r/ObsidianMD</a>. 2024.</div>
|
||
</div>
|
||
<div id="ref-second_brain_assistant_with_obsidian" class="csl-entry">
|
||
<div class="csl-left-margin">3. </div><div class="csl-right-inline"><a href="https://www.ssp.sh/brain/second-brain-assistant-with-obsidian-notegpt/">Second Brain Assistant with Obsidian</a>. 2025.</div>
|
||
</div>
|
||
<div id="ref-khoj_plugin" class="csl-entry">
|
||
<div class="csl-left-margin">4. </div><div class="csl-right-inline"><a href="https://forum.obsidian.md/t/khoj-an-ai-powered-search-assistant-for-you-second-brain/53756">Khoj: An AI powered Search Assistant for your Second Brain - Share & showcase - Obsidian Forum</a>. 2023.</div>
|
||
</div>
|
||
<div id="ref-supercharging_obsidian_search" class="csl-entry">
|
||
<div class="csl-left-margin">5. </div><div class="csl-right-inline">@airabbitX. 2024. <a href="https://medium.com/@airabbitX/supercharging-obsidian-search-with-local-llms-a-personal-journey-1e008eb649a6">Supercharging Obsidian Search with AI and Ollama</a>.</div>
|
||
</div>
|
||
<div id="ref-export_obsidian_to_rdf" class="csl-entry">
|
||
<div class="csl-left-margin">6. </div><div class="csl-right-inline">Pavlyshyn, Volodymyr. 2024. <a href="https://volodymyrpavlyshyn.medium.com/how-to-export-your-obsidian-vault-to-rdf-00fb2539ed18">How to export your Obsidian Vault to RDF</a>.</div>
|
||
</div>
|
||
<div id="ref-export_to_common_graph_formats" class="csl-entry">
|
||
<div class="csl-left-margin">7. </div><div class="csl-right-inline"><a href="https://forum.obsidian.md/t/export-to-common-graph-formats/4138">Export to common graph formats - Plugins ideas - Obsidian Forum</a>. 2020.</div>
|
||
</div>
|
||
<div id="ref-personal_knowledge_graphs_in_obsidian" class="csl-entry">
|
||
<div class="csl-left-margin">8. </div><div class="csl-right-inline">Pavlyshyn, Volodymyr. 2024. <a href="https://volodymyrpavlyshyn.medium.com/personal-knowledge-graphs-in-obsidian-528a0f4584b9">Personal Knowledge Graphs in Obsidian</a>.</div>
|
||
</div>
|
||
<div id="ref-ai_empowered_zettelkasten_with_ner_and_graph_llm" class="csl-entry">
|
||
<div class="csl-left-margin">9. </div><div class="csl-right-inline"><a href="https://forum.obsidian.md/t/ai-empowered-zettelkasten-with-ner-and-graph-llm/79112">AI empowered Zettelkasten with NER and Graph LLM - Knowledge management - Obsidian Forum</a>. 2024.</div>
|
||
</div>
|
||
<div id="ref-build_your_second_brain_with_khoj_ai" class="csl-entry">
|
||
<div class="csl-left-margin">10. </div><div class="csl-right-inline"><a href="https://dswharshit.medium.com/build-your-second-brain-with-khoj-ai-high-signal-ai-2-87492730d7ce">Build your second brain with Khoj AI</a>. 2024.</div>
|
||
</div>
|
||
<div id="ref-basic_memory_ai_conversations_that_build_knowledge" class="csl-entry">
|
||
<div class="csl-left-margin">11. </div><div class="csl-right-inline"><a href="https://basicmachines.co/">Basic Memory | AI Conversations That Build Knowledge</a>.</div>
|
||
</div>
|
||
<div id="ref-local_free_rag_with_question_generation" class="csl-entry">
|
||
<div class="csl-left-margin">12. </div><div class="csl-right-inline">Galvis, Oscar. 2024. <a href="https://lomaky.medium.com/local-free-rag-with-question-generation-using-lm-studio-nomic-embeddings-chromadb-and-llama-3-2-9758877e93b4">Local (Free) RAG with Question Generation using LM Studio, Nomic embeddings, ChromaDB and Llama 3.2 on a Mac mini M1</a>.</div>
|
||
</div>
|
||
</div></section></div> ]]></description>
|
||
<category>Article</category>
|
||
<category>RAG</category>
|
||
<category>ML</category>
|
||
<guid>https://drezil.de/Writing/Obsidian-RAG.html</guid>
|
||
<pubDate>Wed, 23 Apr 2025 22:00:00 GMT</pubDate>
|
||
<media:content url="https://drezil.de/thumbs/writing_obsidian-rag.png" medium="image" type="image/png" height="104" width="144"/>
|
||
</item>
|
||
<item>
|
||
<title>Webapp-Example: Main.hs</title>
|
||
<link>https://drezil.de/Coding/Haskell/Webapp-Example/Main.hs.html</link>
|
||
<description><![CDATA[
|
||
|
||
|
||
|
||
|
||
|
||
<p>Wie man das verwendet, siehe <a href="../../../Coding/Haskell/Webapp-Example/index.html">Webapp-Example</a>.</p>
|
||
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb1-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# OPTIONS_GHC -Wno-name-shadowing #-}</span></span>
|
||
<span id="cb1-2"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE FlexibleContexts #-}</span></span>
|
||
<span id="cb1-3"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE LambdaCase #-}</span></span>
|
||
<span id="cb1-4"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE OverloadedStrings #-}</span></span>
|
||
<span id="cb1-5"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE RankNTypes #-}</span></span>
|
||
<span id="cb1-6"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE RecordWildCards #-}</span></span>
|
||
<span id="cb1-7"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE ScopedTypeVariables #-}</span></span>
|
||
<span id="cb1-8"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">module</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">MyService</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">where</span></span>
|
||
<span id="cb1-9"></span>
|
||
<span id="cb1-10"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- generische imports aus den dependencies/base, nicht in der prelude</span></span>
|
||
<span id="cb1-11"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Codec.MIME.Type</span></span>
|
||
<span id="cb1-12"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Configuration.Dotenv</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">as</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Dotenv</span></span>
|
||
<span id="cb1-13"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Control.Concurrent</span> (forkIO, threadDelay)</span>
|
||
<span id="cb1-14"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Control.Concurrent.Async</span></span>
|
||
<span id="cb1-15"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Control.Concurrent.STM</span></span>
|
||
<span id="cb1-16"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Control.Monad</span></span>
|
||
<span id="cb1-17"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Control.Monad.Catch</span></span>
|
||
<span id="cb1-18"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Control.Monad.Except</span></span>
|
||
<span id="cb1-19"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Conversion</span></span>
|
||
<span id="cb1-20"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Conversion.Text</span> ()</span>
|
||
<span id="cb1-21"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.Binary.Builder</span></span>
|
||
<span id="cb1-22"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.String</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IsString</span> (..))</span>
|
||
<span id="cb1-23"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.Time</span></span>
|
||
<span id="cb1-24"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.Time.Clock</span></span>
|
||
<span id="cb1-25"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.Time.Format</span></span>
|
||
<span id="cb1-26"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.Default</span></span>
|
||
<span id="cb1-27"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Network.HostName</span></span>
|
||
<span id="cb1-28"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Network.HTTP.Client</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">as</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">HTTP</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">hiding</span></span>
|
||
<span id="cb1-29"> (withConnection)</span>
|
||
<span id="cb1-30"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Network.HTTP.Types</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Status</span>, statusCode)</span>
|
||
<span id="cb1-31"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Network.Mom.Stompl.Client.Queue</span></span>
|
||
<span id="cb1-32"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Network.Wai</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Middleware</span>)</span>
|
||
<span id="cb1-33"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Network.Wai.Logger</span></span>
|
||
<span id="cb1-34"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Network.Wai.Middleware.Cors</span></span>
|
||
<span id="cb1-35"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Network.Wai.Middleware.RequestLogger</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">OutputFormat</span> (..),</span>
|
||
<span id="cb1-36"> logStdout,</span>
|
||
<span id="cb1-37"> mkRequestLogger,</span>
|
||
<span id="cb1-38"> outputFormat)</span>
|
||
<span id="cb1-39"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Servant.Client</span> (mkClientEnv,</span>
|
||
<span id="cb1-40"> parseBaseUrl)</span>
|
||
<span id="cb1-41"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">System.Directory</span></span>
|
||
<span id="cb1-42"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">System.Envy</span></span>
|
||
<span id="cb1-43"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">System.IO</span></span>
|
||
<span id="cb1-44"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">System.Log.FastLogger</span></span>
|
||
<span id="cb1-45"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Text.PrettyPrint.GenericPretty</span></span>
|
||
<span id="cb1-46"></span>
|
||
<span id="cb1-47"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- generische imports, aber qualified, weil es sonst zu name-clashes kommt</span></span>
|
||
<span id="cb1-48"></span>
|
||
<span id="cb1-49"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">qualified</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.ByteString</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">as</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">BS</span></span>
|
||
<span id="cb1-50"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- import qualified Data.ByteString.Char8 as BS8</span></span>
|
||
<span id="cb1-51"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">qualified</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.ByteString.Lazy</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">as</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LBS</span></span>
|
||
<span id="cb1-52"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">qualified</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Network.HTTP.Client.TLS</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">as</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">UseDefaultHTTPSSettings</span> (tlsManagerSettings)</span>
|
||
<span id="cb1-53"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">qualified</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Network.Mom.Stompl.Client.Queue</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">as</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">AMQ</span></span>
|
||
<span id="cb1-54"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">qualified</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Network.Wai</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">as</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">WAI</span></span>
|
||
<span id="cb1-55"></span>
|
||
<span id="cb1-56"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Handler für den MyServiceBackend-Typen und Imports aus den Libraries</span></span>
|
||
<span id="cb1-57"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">MyService.Handler</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">as</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">H</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- handler der H.myApiEndpointV1Post implementiert</span></span>
|
||
<span id="cb1-58"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">MyService.Types</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- weitere Type (s. nächste box)</span></span>
|
||
<span id="cb1-59"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">MyServiceGen.API</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">as</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">MS</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- aus der generierten library</span></span>
|
||
<span id="cb1-60"></span>
|
||
<span id="cb1-61"></span>
|
||
<span id="cb1-62"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">myServicemain ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IO</span> ()</span>
|
||
<span id="cb1-63">myServicemain <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">do</span></span>
|
||
<span id="cb1-64"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- .env-Datei ins Prozess-Environment laden, falls noch nicht von außen gesetzt</span></span>
|
||
<span id="cb1-65"> void <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> loadFile <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Dotenv.Config</span> [<span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">".env"</span>] [] <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">False</span></span>
|
||
<span id="cb1-66"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Config holen (defaults + overrides aus dem Environment)</span></span>
|
||
<span id="cb1-67"> sc<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">@</span><span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ServerConfig</span>{<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">..</span>} <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"><-</span> decodeWithDefaults defConfig</span>
|
||
<span id="cb1-68"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Backend-Setup</span></span>
|
||
<span id="cb1-69"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- legt sowas wie Proxy-Server fest und wo man wie dran kommt. Benötigt für das Sprechen mit anderen Microservices</span></span>
|
||
<span id="cb1-70"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">let</span> defaultHTTPSSettings <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> UseDefaultHTTPSSettings.tlsManagerSettings { managerResponseTimeout <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> responseTimeoutMicro <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">1000</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">1000</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">*</span> myserviceMaxTimeout }</span>
|
||
<span id="cb1-71"> createBackend url proxy <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">do</span></span>
|
||
<span id="cb1-72"> manager <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"><-</span> newManager <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> managerSetProxy proxy</span>
|
||
<span id="cb1-73"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> defaultHTTPSSettings</span>
|
||
<span id="cb1-74"> url' <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"><-</span> parseBaseUrl url</span>
|
||
<span id="cb1-75"> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">return</span> (mkClientEnv manager url')</span>
|
||
<span id="cb1-76"> internalProxy <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">case</span> myserviceInternalProxyUrl <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">of</span></span>
|
||
<span id="cb1-77"> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> noProxy</span>
|
||
<span id="cb1-78"> url <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> useProxy <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">HTTP.Proxy</span> (fromString url) myserviceInternalProxyPort</span>
|
||
<span id="cb1-79"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- externalProxy = case myserviceExternalProxyUrl of</span></span>
|
||
<span id="cb1-80"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- "" -> noProxy</span></span>
|
||
<span id="cb1-81"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- url -> useProxy $ HTTP.Proxy (fromString url) myserviceExternalProxyPort</span></span>
|
||
<span id="cb1-82"></span>
|
||
<span id="cb1-83"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Definieren & Erzeugen der Funktionen um die anderen Services anzusprechen.</span></span>
|
||
<span id="cb1-84"> calls <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"><-</span> (,)</span>
|
||
<span id="cb1-85"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><$></span> createBackend myserviceAUri internalProxy</span>
|
||
<span id="cb1-86"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><*></span> createBackend myserviceBUri internalProxy</span>
|
||
<span id="cb1-87"></span>
|
||
<span id="cb1-88"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Logging-Setup</span></span>
|
||
<span id="cb1-89"> hSetBuffering stdout <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LineBuffering</span></span>
|
||
<span id="cb1-90"> hSetBuffering stderr <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LineBuffering</span></span>
|
||
<span id="cb1-91"></span>
|
||
<span id="cb1-92"></span>
|
||
<span id="cb1-93"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Infos holen, brauchen wir später</span></span>
|
||
<span id="cb1-94"> myName <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"><-</span> getHostName</span>
|
||
<span id="cb1-95"> today <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"><-</span> formatTime defaultTimeLocale <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"%F"</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> utctDay <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><$></span> getCurrentTime</span>
|
||
<span id="cb1-96"></span>
|
||
<span id="cb1-97"></span>
|
||
<span id="cb1-98"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- activeMQ-Transaktional-Queue zum schreiben nachher vorbereiten</span></span>
|
||
<span id="cb1-99"> amqPost <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"><-</span> newTQueueIO</span>
|
||
<span id="cb1-100"></span>
|
||
<span id="cb1-101"></span>
|
||
<span id="cb1-102"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- bracket a b c == erst a machen, ergebnis an c als variablen übergeben. Schmeisst c ne exception/wird gekillt/..., werden die variablen an b übergeben.</span></span>
|
||
<span id="cb1-103"> bracket</span>
|
||
<span id="cb1-104"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- logfiles öffnen</span></span>
|
||
<span id="cb1-105"> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LogFiles</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><$></span> openFile (<span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"/logs/myservice-"</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span>myName<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span><span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"-"</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span>today<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span><span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">".info"</span>) <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">AppendMode</span></span>
|
||
<span id="cb1-106"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><*></span> openFile (<span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">if</span> myserviceDebug <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">then</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"/logs/myservice-"</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span>myName<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span><span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"-"</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span>today<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span><span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">".debug"</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">else</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"/dev/null"</span>) <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">AppendMode</span></span>
|
||
<span id="cb1-107"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><*></span> openFile (<span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"/logs/myservice-"</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span>myName<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span><span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"-"</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span>today<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span><span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">".error"</span>) <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">AppendMode</span></span>
|
||
<span id="cb1-108"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><*></span> openFile (<span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"/logs/myservice-"</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span>myName<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span><span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"-"</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span>today<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span><span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">".timings"</span>) <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">AppendMode</span></span>
|
||
<span id="cb1-109"> )</span>
|
||
<span id="cb1-110"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- und bei exception/beendigung schlißen.h</span></span>
|
||
<span id="cb1-111"> (\(<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LogFiles</span> a b c d) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">mapM_</span> hClose [a,b,c,d])</span>
|
||
<span id="cb1-112"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> \logfiles <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">do</span></span>
|
||
<span id="cb1-113"></span>
|
||
<span id="cb1-114"></span>
|
||
<span id="cb1-115"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- logschreibe-funktionen aliasen; log ist hier abstrakt, iolog spezialisiert auf io.</span></span>
|
||
<span id="cb1-116"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">let</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">log</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> printLogFiles<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> logfiles ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">MonadIO</span> m <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=></span> [<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LogItem</span>] <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> m ()</span>
|
||
<span id="cb1-117"> iolog <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> printLogFilesIO<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> logfiles ::</span> [<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LogItem</span>] <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IO</span> ()</span>
|
||
<span id="cb1-118"></span>
|
||
<span id="cb1-119"></span>
|
||
<span id="cb1-120"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- H.myApiEndpointV1Post ist ein Handler (alle Handler werden mit alias H importiert) und in einer eigenen Datei</span></span>
|
||
<span id="cb1-121"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Per Default bekommen Handler sowas wie die server-config, die Funktionen um mit anderen Services zu reden, die AMQ-Queue um ins Kibana zu loggen und eine Datei-Logging-Funktion</span></span>
|
||
<span id="cb1-122"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Man kann aber noch viel mehr machen - z.b. gecachte Daten übergeben, eine Talk-Instanz, etc. pp.</span></span>
|
||
<span id="cb1-123"> server <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">MyServiceBackend</span>{ myApiEndpointV1Post <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> H.myApiEndpointV1Post sc calls amqPost <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">log</span></span>
|
||
<span id="cb1-124"> }</span>
|
||
<span id="cb1-125"> config <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">MS.Config</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"http://"</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">++</span> myserviceHost <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">++</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">":"</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">++</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">show</span> myservicePort <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">++</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"/"</span></span>
|
||
<span id="cb1-126"> iolog <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">pure</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Info</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"Using Server configuration:"</span></span>
|
||
<span id="cb1-127"> iolog <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">pure</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Info</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> pretty sc { myserviceActivemqPassword <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"******"</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Do NOT log the password ;)</span></span>
|
||
<span id="cb1-128"> , myserviceMongoPassword <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"******"</span></span>
|
||
<span id="cb1-129"> }</span>
|
||
<span id="cb1-130"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- alle Services starten (Hintergrund-Aktionen wie z.b. einen MongoDB-Dumper, einen Talk-Server oder wie hier die ActiveMQ</span></span>
|
||
<span id="cb1-131"> void <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> forkIO <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> keepActiveMQConnected sc iolog amqPost</span>
|
||
<span id="cb1-132"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- logging-Framework erzeugen</span></span>
|
||
<span id="cb1-133"> loggingMW <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"><-</span> loggingMiddleware</span>
|
||
<span id="cb1-134"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- server starten</span></span>
|
||
<span id="cb1-135"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">if</span> myserviceDebug</span>
|
||
<span id="cb1-136"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">then</span> runMyServiceMiddlewareServer config (cors (\_ <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Just</span> (simpleCorsResourcePolicy {corsRequestHeaders <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> [<span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"Content-Type"</span>]})) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> loggingMW <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> logStdout) server</span>
|
||
<span id="cb1-137"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">else</span> runMyServiceMiddlewareServer config (cors (\_ <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Just</span> (simpleCorsResourcePolicy {corsRequestHeaders <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> [<span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"Content-Type"</span>]}))) server</span>
|
||
<span id="cb1-138"></span>
|
||
<span id="cb1-139"></span>
|
||
<span id="cb1-140"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Sollte bald in die Library hs-stomp ausgelagert werden</span></span>
|
||
<span id="cb1-141"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ist ein Beispiel für einen ActiveMQ-Dumper</span></span>
|
||
<span id="cb1-142"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">keepActiveMQConnected ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ServerConfig</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> ([<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LogItem</span>] <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IO</span> ()) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">TQueue</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">BS.ByteString</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IO</span> ()</span>
|
||
<span id="cb1-143">keepActiveMQConnected sc<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">@</span><span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ServerConfig</span>{<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">..</span>} printLog var <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">do</span></span>
|
||
<span id="cb1-144"> res <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"><-</span> handle (\(<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">e ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">SomeException</span>) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">do</span></span>
|
||
<span id="cb1-145"> printLog <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">pure</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Error</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"Exception in AMQ-Thread: "</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">show</span> e</span>
|
||
<span id="cb1-146"> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">return</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Right</span> ()</span>
|
||
<span id="cb1-147"> ) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> AMQ.try <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">do</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- catches all AMQ-Exception that we can handle. All others bubble up.</span></span>
|
||
<span id="cb1-148"> printLog <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">pure</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Info</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"AMQ: connecting..."</span></span>
|
||
<span id="cb1-149"> withConnection myserviceActivemqHost myserviceActivemqPort [ <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">OAuth</span> myserviceActivemqUsername myserviceActivemqPassword</span>
|
||
<span id="cb1-150"> , <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">OTmo</span> (<span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">30</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">*</span><span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">1000</span>) <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">{- 30 sec timeout -}</span></span>
|
||
<span id="cb1-151"> ]</span>
|
||
<span id="cb1-152"> [] <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> \c <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">do</span></span>
|
||
<span id="cb1-153"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">let</span> oconv <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">return</span></span>
|
||
<span id="cb1-154"> printLog <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">pure</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Info</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"AMQ: connected"</span></span>
|
||
<span id="cb1-155"> withWriter c <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"Chaos-Logger for Kibana"</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"chaos.logs"</span> [] [] oconv <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> \writer <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">do</span></span>
|
||
<span id="cb1-156"> printLog <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">pure</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Info</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"AMQ: queue created"</span></span>
|
||
<span id="cb1-157"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">let</span> postfun <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> writeQ writer (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Type</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Application</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"json"</span>) []) []</span>
|
||
<span id="cb1-158"> void <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> race</span>
|
||
<span id="cb1-159"> (forever <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> atomically (readTQueue var) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">>>=</span> postfun)</span>
|
||
<span id="cb1-160"> (threadDelay (<span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">600</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">*</span><span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">1000</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">*</span><span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">1000</span>)) <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- wait 10 Minutes</span></span>
|
||
<span id="cb1-161"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- close writer</span></span>
|
||
<span id="cb1-162"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- close connection</span></span>
|
||
<span id="cb1-163"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- get outside of all try/handle/...-constructions befor recursing.</span></span>
|
||
<span id="cb1-164"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">case</span> res <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">of</span></span>
|
||
<span id="cb1-165"> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Left</span> ex <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">do</span></span>
|
||
<span id="cb1-166"> printLog <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">pure</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Error</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"AMQ: "</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">show</span> ex</span>
|
||
<span id="cb1-167"> keepActiveMQConnected sc printLog var</span>
|
||
<span id="cb1-168"> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Right</span> _ <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> keepActiveMQConnected sc printLog var</span>
|
||
<span id="cb1-169"></span>
|
||
<span id="cb1-170"></span>
|
||
<span id="cb1-171"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Beispiel für eine Custom-Logging-Middleware.</span></span>
|
||
<span id="cb1-172"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Hier werden z.B. alle 4xx-Status-Codes inkl. Payload ins stdout-Log geschrieben.</span></span>
|
||
<span id="cb1-173"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Nützlich, wenn die Kollegen ihre Requests nicht ordentlich schreiben können und der Server das Format zurecht mit einem BadRequest ablehnt ;)</span></span>
|
||
<span id="cb1-174"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">loggingMiddleware ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IO</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Middleware</span></span>
|
||
<span id="cb1-175">loggingMiddleware <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> liftIO <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> mkRequestLogger <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> def { outputFormat <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">CustomOutputFormatWithDetails</span> out }</span>
|
||
<span id="cb1-176"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">where</span></span>
|
||
<span id="cb1-177"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> out ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ZonedDate</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">WAI.Request</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Status</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Maybe</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Integer</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">NominalDiffTime</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> [<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">BS.ByteString</span>] <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Builder</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LogStr</span></span>
|
||
<span id="cb1-178"> out _ r status _ _ payload _</span>
|
||
<span id="cb1-179"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">|</span> statusCode status <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><</span> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">300</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span></span>
|
||
<span id="cb1-180"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">|</span> statusCode status <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">></span> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">399</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">&&</span> statusCode status <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><</span> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">500</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"Error code "</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span>toLogStr (statusCode status) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span><span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">" sent. Request-Payload was: "</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">mconcat</span> (toLogStr <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><$></span> payload) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"\n"</span></span>
|
||
<span id="cb1-181"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">|</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">otherwise</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> toLogStr (<span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">show</span> r) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"\n"</span></span></code></pre></div>
|
||
|
||
|
||
|
||
]]></description>
|
||
<category>Haskell</category>
|
||
<category>Code</category>
|
||
<guid>https://drezil.de/Coding/Haskell/Webapp-Example/Main.hs.html</guid>
|
||
<pubDate>Tue, 31 Mar 2020 22:00:00 GMT</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>Webapp-Example: MyService/Types.hs</title>
|
||
<link>https://drezil.de/Coding/Haskell/Webapp-Example/MyService_Types.hs.html</link>
|
||
<description><![CDATA[
|
||
|
||
|
||
|
||
|
||
|
||
<p>Anleitung siehe <a href="../../../Coding/Haskell/Webapp-Example/index.html">Webapp-Example</a>.</p>
|
||
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb1-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# OPTIONS_GHC -Wno-orphans #-}</span></span>
|
||
<span id="cb1-2"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# OPTIONS_GHC -Wno-name-shadowing #-}</span></span>
|
||
<span id="cb1-3"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE DeriveAnyClass #-}</span></span>
|
||
<span id="cb1-4"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE DeriveFunctor #-}</span></span>
|
||
<span id="cb1-5"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE DeriveGeneric #-}</span></span>
|
||
<span id="cb1-6"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE DerivingVia #-}</span></span>
|
||
<span id="cb1-7"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE DuplicateRecordFields #-}</span></span>
|
||
<span id="cb1-8"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE FlexibleContexts #-}</span></span>
|
||
<span id="cb1-9"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE FlexibleInstances #-}</span></span>
|
||
<span id="cb1-10"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE GADTs #-}</span></span>
|
||
<span id="cb1-11"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE LambdaCase #-}</span></span>
|
||
<span id="cb1-12"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE MultiParamTypeClasses #-}</span></span>
|
||
<span id="cb1-13"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE OverloadedStrings #-}</span></span>
|
||
<span id="cb1-14"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE RankNTypes #-}</span></span>
|
||
<span id="cb1-15"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE RecordWildCards #-}</span></span>
|
||
<span id="cb1-16"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">module</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">MyService.Types</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">where</span></span>
|
||
<span id="cb1-17"></span>
|
||
<span id="cb1-18"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.Aeson</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">FromJSON</span>, <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ToJSON</span>)</span>
|
||
<span id="cb1-19"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.Text</span></span>
|
||
<span id="cb1-20"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.Time.Clock</span></span>
|
||
<span id="cb1-21"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">GHC.Generics</span></span>
|
||
<span id="cb1-22"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">System.Envy</span></span>
|
||
<span id="cb1-23"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Text.PrettyPrint</span> (text)</span>
|
||
<span id="cb1-24"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Text.PrettyPrint.GenericPretty</span></span>
|
||
<span id="cb1-25"></span>
|
||
<span id="cb1-26"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Out hat hierfür keine Instanzen, daher kurz eine einfach Definition.</span></span>
|
||
<span id="cb1-27"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">instance</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Out</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Text</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">where</span></span>
|
||
<span id="cb1-28"> doc <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> text <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> unpack</span>
|
||
<span id="cb1-29"> docPrec i a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> text <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">showsPrec</span> i a <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span></span>
|
||
<span id="cb1-30"></span>
|
||
<span id="cb1-31"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">instance</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Out</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">UTCTime</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">where</span></span>
|
||
<span id="cb1-32"> doc <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> text <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">show</span></span>
|
||
<span id="cb1-33"> docPrec i a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> text <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">showsPrec</span> i a <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span></span>
|
||
<span id="cb1-34"></span>
|
||
<span id="cb1-35"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Der ServerConfig-Typ. Wird mit den defaults unten initialisiert, dann mit den Variablen aus der .env-Datei überschrieben und zum Schluss können Serveradmins diese via $MYSERVICE_FOO nochmal überschreiben.</span></span>
|
||
<span id="cb1-36"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">data</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ServerConfig</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ServerConfig</span></span>
|
||
<span id="cb1-37"> {<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceHost ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_HOST</span></span>
|
||
<span id="cb1-38"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myservicePort ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Int</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_PORT</span></span>
|
||
<span id="cb1-39"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceMaxTimeout ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Int</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_MAX_TIMEOUT</span></span>
|
||
<span id="cb1-40"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceInternalProxyUrl ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_INTERNAL_PROXY_URL</span></span>
|
||
<span id="cb1-41"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceInternalProxyPort ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Int</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_INTERNAL_PROXY_PORT</span></span>
|
||
<span id="cb1-42"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceExternalProxyUrl ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_EXTERNAL_PROXY_URL</span></span>
|
||
<span id="cb1-43"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceExternalProxyPort ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Int</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_EXTERNAL_PROXY_PORT</span></span>
|
||
<span id="cb1-44"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceActivemqHost ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_ACTIVEMQ_HOST</span></span>
|
||
<span id="cb1-45"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceActivemqPort ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Int</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_ACTIVEMQ_PORT</span></span>
|
||
<span id="cb1-46"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceActivemqUsername ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_ACTIVEMQ_USERNAME</span></span>
|
||
<span id="cb1-47"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceActivemqPassword ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_ACTIVEMQ_PASSWORD</span></span>
|
||
<span id="cb1-48"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceMongoUsername ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_MONGO_USERNAME</span></span>
|
||
<span id="cb1-49"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceMongoPassword ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_MONGO_PASSWORD</span></span>
|
||
<span id="cb1-50"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceDebug ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Bool</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_DEBUG</span></span>
|
||
<span id="cb1-51"> } <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">deriving</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Show</span>, <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Eq</span>, <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Generic</span>)</span>
|
||
<span id="cb1-52"></span>
|
||
<span id="cb1-53"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Default-Konfigurations-Instanz für diesen Service.</span></span>
|
||
<span id="cb1-54"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">instance</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">DefConfig</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ServerConfig</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">where</span></span>
|
||
<span id="cb1-55"> defConfig <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ServerConfig</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"0.0.0.0"</span> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">8080</span> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">20</span></span>
|
||
<span id="cb1-56"> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span></span>
|
||
<span id="cb1-57"> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span></span>
|
||
<span id="cb1-58"> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span></span>
|
||
<span id="cb1-59"> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">0</span></span>
|
||
<span id="cb1-60"> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span></span>
|
||
<span id="cb1-61"> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">0</span></span>
|
||
<span id="cb1-62"> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span></span>
|
||
<span id="cb1-63"> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">0</span></span>
|
||
<span id="cb1-64"> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span></span>
|
||
<span id="cb1-65"> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span></span>
|
||
<span id="cb1-66"> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span></span>
|
||
<span id="cb1-67"> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span></span>
|
||
<span id="cb1-68"> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">False</span></span>
|
||
<span id="cb1-69"></span>
|
||
<span id="cb1-70"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Kann auch aus dem ENV gefüllt werden</span></span>
|
||
<span id="cb1-71"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">instance</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">FromEnv</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ServerConfig</span></span>
|
||
<span id="cb1-72"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Und hübsch ausgegeben werden.</span></span>
|
||
<span id="cb1-73"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">instance</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Out</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ServerConfig</span></span>
|
||
<span id="cb1-74"></span>
|
||
<span id="cb1-75"></span>
|
||
<span id="cb1-76"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">instance</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Out</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Response</span></span>
|
||
<span id="cb1-77"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">instance</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">FromBSON</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Repsonse</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- FromBSON-Instanz geht immer davon aus, dass alle keys da sind (ggf. mit null bei Nothing).</span></span></code></pre></div>
|
||
|
||
|
||
|
||
]]></description>
|
||
<category>Haskell</category>
|
||
<category>Code</category>
|
||
<guid>https://drezil.de/Coding/Haskell/Webapp-Example/MyService_Types.hs.html</guid>
|
||
<pubDate>Tue, 31 Mar 2020 22:00:00 GMT</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>Webapp-Development in Haskell</title>
|
||
<link>https://drezil.de/Coding/Haskell/Webapp-Example/</link>
|
||
<description><![CDATA[
|
||
|
||
|
||
|
||
|
||
|
||
<section id="definition-der-api" class="level2">
|
||
<h2 class="anchored" data-anchor-id="definition-der-api">Definition der API</h2>
|
||
<p>Erster Schritt ist immer ein wünsch-dir-was bei der Api-Defenition.</p>
|
||
<p>Die meisten Services haben offensichtliche Anforderungen (Schnittstellen nach draußen, Schnittstellen intern, …). Diese kann man immer sehr gut in einem <code>Request -> Response</code>-Model erfassen.</p>
|
||
<p>Um die Anforderungen und Möglichkeiten des jeweiligen Services sauber zu erfassen und automatisiert zu prüfen, dummy-implementationen zu bekommen und vieles andere mehr, empfiehlt es sich den <strong>Openapi-generator</strong> zu nutzen.</p>
|
||
<p>Diese Definition läuft über openapi-v3 und kann z.b. mit Echtzeit-Vorschau im <a href="http://editor.swagger.io/" class="uri">http://editor.swagger.io/</a> erspielen. Per Default ist der noch auf openapi-v2 (aka swagger), kann aber auch v3.</p>
|
||
<p>Nach der Definition, was man am Ende haben möchte, muss man sich entscheiden, in welcher Sprache man weiter entwickelt. Ich empfehle aus verschiedenen Gründen primär 2 Sprachen: Python-Microservices (weil die ML-Libraries sehr gut sind, allerdings Änderungen meist schwer sind und der Code wenig robust - meist nur 1 API-Endpunkt pro service) und Haskell (Stabilität, Performace, leicht zu ändern, gut anzupassen).</p>
|
||
<p>Im folgenden wird (aus offensichtlichen Gründen) nur auf das Haskell-Projekt eingegangen.</p>
|
||
</section>
|
||
<section id="startprojekt-in-haskell" class="level2">
|
||
<h2 class="anchored" data-anchor-id="startprojekt-in-haskell">Startprojekt in Haskell</h2>
|
||
<section id="erstellen-eines-neuen-projektes" class="level3">
|
||
<h3 class="anchored" data-anchor-id="erstellen-eines-neuen-projektes">Erstellen eines neuen Projektes</h3>
|
||
<p>Zunächst erstellen wir in normales Haskell-Projekt ohne Funktionalität & Firlefanz:</p>
|
||
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb1-1"><span class="ex" style="color: null;
|
||
background-color: null;
|
||
font-style: inherit;">stack</span> new myservice</span></code></pre></div>
|
||
<p>Dies erstellt ein neues Verzeichnis und das generelle scaffolding. Nach einer kurzen Anpassung der <code>stack.yaml</code> (resolver auf unserer setzen; aktuell: <code>lts-17.4</code>) fügen wir am Ende der Datei</p>
|
||
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb2-1"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">allow-newer</span><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">:</span><span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;"> </span><span class="ch" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">true</span></span>
|
||
<span id="cb2-2"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">ghc-options</span><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">:</span></span>
|
||
<span id="cb2-3"><span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">"$locals"</span><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">:</span><span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;"> -fwrite-ide-info</span></span></code></pre></div>
|
||
<p>ein. Anschließend organisieren™ wir uns noch eine gute <code>.gitignore</code> und initialisieren das git mittels <code>git init; git add .; git commit -m "initial scaffold"</code></p>
|
||
</section>
|
||
<section id="generierung-der-api" class="level3">
|
||
<h3 class="anchored" data-anchor-id="generierung-der-api">Generierung der API</h3>
|
||
<p>Da die API immer wieder neu generiert werden kann (und sollte!) liegt sich in einem unterverzeichnis des Hauptprojektes.</p>
|
||
<p>Initial ist es das einfachste ein leeres temporäres Verzeichnis woanders zu erstellen, die <code>api-doc.yml</code> hinein kopieren und folgendes ausführen:</p>
|
||
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb3-1"><span class="ex" style="color: null;
|
||
background-color: null;
|
||
font-style: inherit;">openapi-generator</span> generate <span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;">-g</span> haskell <span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;">-o</span> . <span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;">-i</span> api-doc.yml</span></code></pre></div>
|
||
<p>Dieses erstellt einem dann eine komplette library inkl. Datentypen. Wichtig: Der Name in der <code>api-doc</code> sollte vom Namen des Services (oben <code>myservice</code>) abweichen - entweder in Casing oder im Namen direkt. Suffixe wie API schneidet der Generator hier leider ab. (Wieso das ganze? Es entstehen nachher 2 libraries, <code>foo</code> & <code>fooAPI</code>. Da der generator das API abschneidet endet man mit foo & foo und der compiler meckert, dass er nicht weiß, welche lib gemeint ist).</p>
|
||
<p>danach: wie gewohnt <code>git init; git add .; git commit -m "initial"</code>. Auf dem Server der Wahl (github, gitea, gitlab, …) nun ein Repository erstellen (am Besten: <code>myserviceAPI</code> - nach Konvention ist alles auf API endend autogeneriert!) und den Anweisungen nach ein remote hinzufügen & pushen.</p>
|
||
<section id="wieder-zurück-im-haskell-service" class="level4">
|
||
<h4 class="anchored" data-anchor-id="wieder-zurück-im-haskell-service">Wieder zurück im Haskell-Service</h4>
|
||
<p>In unserem eigentlichen Service müssen wir nun die API einbinden. Dazu erstellen wir ein Verzeichnis <code>libs</code> (Konvention) und machen ein <code>git submodule add <repository-url> libs/myserviceAPI</code></p>
|
||
<p>Git hat nun die API in das submodul gepackt und wir können das oben erstellte temporäre Verzeichnis wieder löschen.</p>
|
||
<p>Anschließend müssen wir stack noch erklären, dass wir die API da nun liegen haben und passen wieder die <code>stack.yaml</code> an, indem wir das Verzeichnis unter packages hinzufügen.</p>
|
||
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb4-1"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">packages</span><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">:</span></span>
|
||
<span id="cb4-2"><span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">-</span><span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;"> .</span></span>
|
||
<span id="cb4-3"><span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">-</span><span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;"> libs/myserviceAPI</span><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"> # <<</span></span></code></pre></div>
|
||
<p>Nun können wir in der <code>package.yaml</code> (oder <code>myservice.cabal</code>, falls kein <code>hpack</code> verwendet wird) unter den dependencies unsere API hinzufügen (name wie die cabal-Datei in <code>libs/myserviceAPI</code>).</p>
|
||
</section>
|
||
</section>
|
||
<section id="einbinden-anderer-microservices" class="level3">
|
||
<h3 class="anchored" data-anchor-id="einbinden-anderer-microservices">Einbinden anderer Microservices</h3>
|
||
<p>Funktioniert komplett analog zu dem vorgehen oben (ohne das generieren natürlich :grin:). <code>stack.yaml</code> editieren und zu den packages hinzufügen:</p>
|
||
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb5-1"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">packages</span><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">:</span></span>
|
||
<span id="cb5-2"><span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">-</span><span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;"> .</span></span>
|
||
<span id="cb5-3"><span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">-</span><span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;"> libs/myserviceAPI</span></span>
|
||
<span id="cb5-4"><span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">-</span><span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;"> libs/myCoolMLServiceAPI</span></span></code></pre></div>
|
||
<p>in der <code>package.yaml</code> (oder der cabal) die dependencies hinzufügen und schon haben wir die Features zur Verfügung und können gegen diese Services reden.</p>
|
||
</section>
|
||
<section id="entfernen-von-anderen-technologienmicroservices" class="level3">
|
||
<h3 class="anchored" data-anchor-id="entfernen-von-anderen-technologienmicroservices">Entfernen von anderen Technologien/Microservices</h3>
|
||
<p>In git ist das entfernen von Submodules etwas frickelig, daher hier ein copy&paste der <a href="https://gist.github.com/myusuf3/7f645819ded92bda6677">GitHub-Antwort</a>:</p>
|
||
<div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb6-1"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">## Remove the submodule entry from .git/config</span></span>
|
||
<span id="cb6-2"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">git</span> submodule deinit <span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;">-f</span> path/to/submodule</span>
|
||
<span id="cb6-3"></span>
|
||
<span id="cb6-4"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">## Remove the submodule directory from the superproject's .git/modules directory</span></span>
|
||
<span id="cb6-5"><span class="ex" style="color: null;
|
||
background-color: null;
|
||
font-style: inherit;">rm-rf</span> .git/modules/path/to/submodule</span>
|
||
<span id="cb6-6"></span>
|
||
<span id="cb6-7"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">## Remove the entry in .gitmodules and remove the submodule directory located at path/to/submodule</span></span>
|
||
<span id="cb6-8"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">git</span> rm-f path/to/submodule</span></code></pre></div>
|
||
<p>Falls das nicht klappt, gibt es alternative Vorschläge unter dem Link oben.</p>
|
||
</section>
|
||
<section id="woher-weiss-ich-was-wo-liegt-dokumentation-halloo" class="level3">
|
||
<h3 class="anchored" data-anchor-id="woher-weiss-ich-was-wo-liegt-dokumentation-halloo">Woher weiss ich, was wo liegt? Dokumentation? Halloo??</h3>
|
||
<p>Keine Panik. Ein <code>stack haddock --open</code> hilft da. Das generiert die Dokumentation für alle in der <code>package.yaml</code> (oder cabal-file) eingetragenen dependencies inkl. aller upstream-dependencies. Man bekommt also eine komplette lokale Dokumentation von allem. Geöffnet wird dann die Paket-Startseite inkl. der direkten dependencies:</p>
|
||
<p>Es gibt 2 wichtige Pfade im Browser:</p>
|
||
<ul>
|
||
<li><code>...../all/index.html</code> - hier sind alle Pakete aufgeführt</li>
|
||
<li><code>...../index.html</code> - hier sind nur die direkten dependencies aufgeführt.</li>
|
||
</ul>
|
||
<p>Wenn man einen lokalen Webserver startet kann man mittels “s” auch die interaktive Suche öffnen (Suche nach Typen, Funktionen, Signaturen, etc.). In Bash mit <code>python3</code> geht das z.b. einfach über:</p>
|
||
<div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb7-1"><span class="bu" style="color: null;
|
||
background-color: null;
|
||
font-style: inherit;">cd</span> <span class="va" style="color: #111111;
|
||
background-color: null;
|
||
font-style: inherit;">$(</span><span class="ex" style="color: null;
|
||
background-color: null;
|
||
font-style: inherit;">stack</span> path <span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;">--local-doc-root</span><span class="va" style="color: #111111;
|
||
background-color: null;
|
||
font-style: inherit;">)</span></span>
|
||
<span id="cb7-2"><span class="ex" style="color: null;
|
||
background-color: null;
|
||
font-style: inherit;">python3</span> <span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;">-m</span> SimpleHTTPServer 8000</span>
|
||
<span id="cb7-3"><span class="ex" style="color: null;
|
||
background-color: null;
|
||
font-style: inherit;">firefox</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"http://localhost:8000"</span></span></code></pre></div>
|
||
</section>
|
||
<section id="implementation-des-services-und-start" class="level3">
|
||
<h3 class="anchored" data-anchor-id="implementation-des-services-und-start">Implementation des Services und Start</h3>
|
||
<section id="loaderbootstrapper" class="level4">
|
||
<h4 class="anchored" data-anchor-id="loaderbootstrapper">Loader/Bootstrapper</h4>
|
||
<p>Generelles Vorgehen:</p>
|
||
<ul>
|
||
<li><p>in <code>app/Main.hs</code>: Hier ist quasi immer nur eine Zeile drin: <code>main = myServiceMain</code></p>
|
||
<p>Grund: Applications tauchen nicht im Haddock auf. Also haben wir ein “src”-Modul, welches hier nur geladen & ausgeführt wird.</p></li>
|
||
<li><p>in <code>src/MyService.hs</code>: <code>myServiceMain :: IO ()</code> definieren</p></li>
|
||
</ul>
|
||
<p>Für die Main kann man prinzipiell eine Main andere Services copy/pasten. Im folgenden eine Annotierte main-Funktion - zu den einzelnen Voraussetzungen kommen wir im Anschluss.</p>
|
||
<p></p><p></p><details><summary>Main.hs anzeigen</summary><blockquote class="blockquote"><p></p>
|
||
<div class="sourceCode" id="cb8" data-code-fold="true" data-code-summary="Code anzeigen" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb8-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# OPTIONS_GHC -Wno-name-shadowing #-}</span></span>
|
||
<span id="cb8-2"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE FlexibleContexts #-}</span></span>
|
||
<span id="cb8-3"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE LambdaCase #-}</span></span>
|
||
<span id="cb8-4"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE OverloadedStrings #-}</span></span>
|
||
<span id="cb8-5"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE RankNTypes #-}</span></span>
|
||
<span id="cb8-6"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE RecordWildCards #-}</span></span>
|
||
<span id="cb8-7"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE ScopedTypeVariables #-}</span></span>
|
||
<span id="cb8-8"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">module</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">MyService</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">where</span></span>
|
||
<span id="cb8-9"></span>
|
||
<span id="cb8-10"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- generische imports aus den dependencies/base, nicht in der prelude</span></span>
|
||
<span id="cb8-11"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Codec.MIME.Type</span></span>
|
||
<span id="cb8-12"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Configuration.Dotenv</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">as</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Dotenv</span></span>
|
||
<span id="cb8-13"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Control.Concurrent</span> (forkIO, threadDelay)</span>
|
||
<span id="cb8-14"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Control.Concurrent.Async</span></span>
|
||
<span id="cb8-15"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Control.Concurrent.STM</span></span>
|
||
<span id="cb8-16"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Control.Monad</span></span>
|
||
<span id="cb8-17"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Control.Monad.Catch</span></span>
|
||
<span id="cb8-18"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Control.Monad.Except</span></span>
|
||
<span id="cb8-19"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Conversion</span></span>
|
||
<span id="cb8-20"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Conversion.Text</span> ()</span>
|
||
<span id="cb8-21"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.Binary.Builder</span></span>
|
||
<span id="cb8-22"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.String</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IsString</span> (..))</span>
|
||
<span id="cb8-23"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.Time</span></span>
|
||
<span id="cb8-24"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.Time.Clock</span></span>
|
||
<span id="cb8-25"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.Time.Format</span></span>
|
||
<span id="cb8-26"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.Default</span></span>
|
||
<span id="cb8-27"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Network.HostName</span></span>
|
||
<span id="cb8-28"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Network.HTTP.Client</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">as</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">HTTP</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">hiding</span></span>
|
||
<span id="cb8-29"> (withConnection)</span>
|
||
<span id="cb8-30"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Network.HTTP.Types</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Status</span>, statusCode)</span>
|
||
<span id="cb8-31"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Network.Mom.Stompl.Client.Queue</span></span>
|
||
<span id="cb8-32"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Network.Wai</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Middleware</span>)</span>
|
||
<span id="cb8-33"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Network.Wai.Logger</span></span>
|
||
<span id="cb8-34"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Network.Wai.Middleware.Cors</span></span>
|
||
<span id="cb8-35"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Network.Wai.Middleware.RequestLogger</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">OutputFormat</span> (..),</span>
|
||
<span id="cb8-36"> logStdout,</span>
|
||
<span id="cb8-37"> mkRequestLogger,</span>
|
||
<span id="cb8-38"> outputFormat)</span>
|
||
<span id="cb8-39"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Servant.Client</span> (mkClientEnv,</span>
|
||
<span id="cb8-40"> parseBaseUrl)</span>
|
||
<span id="cb8-41"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">System.Directory</span></span>
|
||
<span id="cb8-42"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">System.Envy</span></span>
|
||
<span id="cb8-43"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">System.IO</span></span>
|
||
<span id="cb8-44"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">System.Log.FastLogger</span></span>
|
||
<span id="cb8-45"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Text.PrettyPrint.GenericPretty</span></span>
|
||
<span id="cb8-46"></span>
|
||
<span id="cb8-47"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- generische imports, aber qualified, weil es sonst zu name-clashes kommt</span></span>
|
||
<span id="cb8-48"></span>
|
||
<span id="cb8-49"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">qualified</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.ByteString</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">as</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">BS</span></span>
|
||
<span id="cb8-50"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- import qualified Data.ByteString.Char8 as BS8</span></span>
|
||
<span id="cb8-51"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">qualified</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.ByteString.Lazy</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">as</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LBS</span></span>
|
||
<span id="cb8-52"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">qualified</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Network.HTTP.Client.TLS</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">as</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">UseDefaultHTTPSSettings</span> (tlsManagerSettings)</span>
|
||
<span id="cb8-53"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">qualified</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Network.Mom.Stompl.Client.Queue</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">as</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">AMQ</span></span>
|
||
<span id="cb8-54"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">qualified</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Network.Wai</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">as</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">WAI</span></span>
|
||
<span id="cb8-55"></span>
|
||
<span id="cb8-56"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Handler für den MyServiceBackend-Typen und Imports aus den Libraries</span></span>
|
||
<span id="cb8-57"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">MyService.Handler</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">as</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">H</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- handler der H.myApiEndpointV1Post implementiert</span></span>
|
||
<span id="cb8-58"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">MyService.Types</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- weitere Type (s. nächste box)</span></span>
|
||
<span id="cb8-59"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">MyServiceGen.API</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">as</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">MS</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- aus der generierten library</span></span>
|
||
<span id="cb8-60"></span>
|
||
<span id="cb8-61"></span>
|
||
<span id="cb8-62"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">myServicemain ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IO</span> ()</span>
|
||
<span id="cb8-63">myServicemain <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">do</span></span>
|
||
<span id="cb8-64"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- .env-Datei ins Prozess-Environment laden, falls noch nicht von außen gesetzt</span></span>
|
||
<span id="cb8-65"> void <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> loadFile <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Dotenv.Config</span> [<span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">".env"</span>] [] <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">False</span></span>
|
||
<span id="cb8-66"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Config holen (defaults + overrides aus dem Environment)</span></span>
|
||
<span id="cb8-67"> sc<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">@</span><span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ServerConfig</span>{<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">..</span>} <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"><-</span> decodeWithDefaults defConfig</span>
|
||
<span id="cb8-68"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Backend-Setup</span></span>
|
||
<span id="cb8-69"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- legt sowas wie Proxy-Server fest und wo man wie dran kommt. Benötigt für das Sprechen mit anderen Microservices</span></span>
|
||
<span id="cb8-70"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">let</span> defaultHTTPSSettings <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> UseDefaultHTTPSSettings.tlsManagerSettings { managerResponseTimeout <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> responseTimeoutMicro <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">1000</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">1000</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">*</span> myserviceMaxTimeout }</span>
|
||
<span id="cb8-71"> createBackend url proxy <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">do</span></span>
|
||
<span id="cb8-72"> manager <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"><-</span> newManager <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> managerSetProxy proxy</span>
|
||
<span id="cb8-73"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> defaultHTTPSSettings</span>
|
||
<span id="cb8-74"> url' <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"><-</span> parseBaseUrl url</span>
|
||
<span id="cb8-75"> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">return</span> (mkClientEnv manager url')</span>
|
||
<span id="cb8-76"> internalProxy <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">case</span> myserviceInternalProxyUrl <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">of</span></span>
|
||
<span id="cb8-77"> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> noProxy</span>
|
||
<span id="cb8-78"> url <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> useProxy <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">HTTP.Proxy</span> (fromString url) myserviceInternalProxyPort</span>
|
||
<span id="cb8-79"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- externalProxy = case myserviceExternalProxyUrl of</span></span>
|
||
<span id="cb8-80"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- "" -> noProxy</span></span>
|
||
<span id="cb8-81"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- url -> useProxy $ HTTP.Proxy (fromString url) myserviceExternalProxyPort</span></span>
|
||
<span id="cb8-82"></span>
|
||
<span id="cb8-83"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Definieren & Erzeugen der Funktionen um die anderen Services anzusprechen.</span></span>
|
||
<span id="cb8-84"> calls <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"><-</span> (,)</span>
|
||
<span id="cb8-85"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><$></span> createBackend myserviceAUri internalProxy</span>
|
||
<span id="cb8-86"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><*></span> createBackend myserviceBUri internalProxy</span>
|
||
<span id="cb8-87"></span>
|
||
<span id="cb8-88"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Logging-Setup</span></span>
|
||
<span id="cb8-89"> hSetBuffering stdout <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LineBuffering</span></span>
|
||
<span id="cb8-90"> hSetBuffering stderr <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LineBuffering</span></span>
|
||
<span id="cb8-91"></span>
|
||
<span id="cb8-92"></span>
|
||
<span id="cb8-93"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Infos holen, brauchen wir später</span></span>
|
||
<span id="cb8-94"> myName <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"><-</span> getHostName</span>
|
||
<span id="cb8-95"> today <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"><-</span> formatTime defaultTimeLocale <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"%F"</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> utctDay <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><$></span> getCurrentTime</span>
|
||
<span id="cb8-96"></span>
|
||
<span id="cb8-97"></span>
|
||
<span id="cb8-98"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- activeMQ-Transaktional-Queue zum schreiben nachher vorbereiten</span></span>
|
||
<span id="cb8-99"> amqPost <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"><-</span> newTQueueIO</span>
|
||
<span id="cb8-100"></span>
|
||
<span id="cb8-101"></span>
|
||
<span id="cb8-102"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- bracket a b c == erst a machen, ergebnis an c als variablen übergeben. Schmeisst c ne exception/wird gekillt/..., werden die variablen an b übergeben.</span></span>
|
||
<span id="cb8-103"> bracket</span>
|
||
<span id="cb8-104"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- logfiles öffnen</span></span>
|
||
<span id="cb8-105"> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LogFiles</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><$></span> openFile (<span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"/logs/myservice-"</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span>myName<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span><span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"-"</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span>today<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span><span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">".info"</span>) <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">AppendMode</span></span>
|
||
<span id="cb8-106"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><*></span> openFile (<span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">if</span> myserviceDebug <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">then</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"/logs/myservice-"</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span>myName<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span><span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"-"</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span>today<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span><span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">".debug"</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">else</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"/dev/null"</span>) <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">AppendMode</span></span>
|
||
<span id="cb8-107"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><*></span> openFile (<span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"/logs/myservice-"</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span>myName<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span><span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"-"</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span>today<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span><span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">".error"</span>) <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">AppendMode</span></span>
|
||
<span id="cb8-108"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><*></span> openFile (<span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"/logs/myservice-"</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span>myName<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span><span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"-"</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span>today<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span><span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">".timings"</span>) <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">AppendMode</span></span>
|
||
<span id="cb8-109"> )</span>
|
||
<span id="cb8-110"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- und bei exception/beendigung schlißen.h</span></span>
|
||
<span id="cb8-111"> (\(<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LogFiles</span> a b c d) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">mapM_</span> hClose [a,b,c,d])</span>
|
||
<span id="cb8-112"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> \logfiles <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">do</span></span>
|
||
<span id="cb8-113"></span>
|
||
<span id="cb8-114"></span>
|
||
<span id="cb8-115"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- logschreibe-funktionen aliasen; log ist hier abstrakt, iolog spezialisiert auf io.</span></span>
|
||
<span id="cb8-116"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">let</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">log</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> printLogFiles<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> logfiles ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">MonadIO</span> m <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=></span> [<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LogItem</span>] <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> m ()</span>
|
||
<span id="cb8-117"> iolog <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> printLogFilesIO<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> logfiles ::</span> [<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LogItem</span>] <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IO</span> ()</span>
|
||
<span id="cb8-118"></span>
|
||
<span id="cb8-119"></span>
|
||
<span id="cb8-120"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- H.myApiEndpointV1Post ist ein Handler (alle Handler werden mit alias H importiert) und in einer eigenen Datei</span></span>
|
||
<span id="cb8-121"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Per Default bekommen Handler sowas wie die server-config, die Funktionen um mit anderen Services zu reden, die AMQ-Queue um ins Kibana zu loggen und eine Datei-Logging-Funktion</span></span>
|
||
<span id="cb8-122"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Man kann aber noch viel mehr machen - z.b. gecachte Daten übergeben, eine Talk-Instanz, etc. pp.</span></span>
|
||
<span id="cb8-123"> server <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">MyServiceBackend</span>{ myApiEndpointV1Post <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> H.myApiEndpointV1Post sc calls amqPost <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">log</span></span>
|
||
<span id="cb8-124"> }</span>
|
||
<span id="cb8-125"> config <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">MS.Config</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"http://"</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">++</span> myserviceHost <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">++</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">":"</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">++</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">show</span> myservicePort <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">++</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"/"</span></span>
|
||
<span id="cb8-126"> iolog <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">pure</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Info</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"Using Server configuration:"</span></span>
|
||
<span id="cb8-127"> iolog <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">pure</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Info</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> pretty sc { myserviceActivemqPassword <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"******"</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Do NOT log the password ;)</span></span>
|
||
<span id="cb8-128"> , myserviceMongoPassword <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"******"</span></span>
|
||
<span id="cb8-129"> }</span>
|
||
<span id="cb8-130"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- alle Services starten (Hintergrund-Aktionen wie z.b. einen MongoDB-Dumper, einen Talk-Server oder wie hier die ActiveMQ</span></span>
|
||
<span id="cb8-131"> void <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> forkIO <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> keepActiveMQConnected sc iolog amqPost</span>
|
||
<span id="cb8-132"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- logging-Framework erzeugen</span></span>
|
||
<span id="cb8-133"> loggingMW <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"><-</span> loggingMiddleware</span>
|
||
<span id="cb8-134"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- server starten</span></span>
|
||
<span id="cb8-135"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">if</span> myserviceDebug</span>
|
||
<span id="cb8-136"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">then</span> runMyServiceMiddlewareServer config (cors (\_ <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Just</span> (simpleCorsResourcePolicy {corsRequestHeaders <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> [<span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"Content-Type"</span>]})) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> loggingMW <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> logStdout) server</span>
|
||
<span id="cb8-137"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">else</span> runMyServiceMiddlewareServer config (cors (\_ <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Just</span> (simpleCorsResourcePolicy {corsRequestHeaders <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> [<span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"Content-Type"</span>]}))) server</span>
|
||
<span id="cb8-138"></span>
|
||
<span id="cb8-139"></span>
|
||
<span id="cb8-140"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Sollte bald in die Library hs-stomp ausgelagert werden</span></span>
|
||
<span id="cb8-141"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ist ein Beispiel für einen ActiveMQ-Dumper</span></span>
|
||
<span id="cb8-142"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">keepActiveMQConnected ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ServerConfig</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> ([<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LogItem</span>] <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IO</span> ()) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">TQueue</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">BS.ByteString</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IO</span> ()</span>
|
||
<span id="cb8-143">keepActiveMQConnected sc<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">@</span><span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ServerConfig</span>{<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">..</span>} printLog var <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">do</span></span>
|
||
<span id="cb8-144"> res <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"><-</span> handle (\(<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">e ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">SomeException</span>) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">do</span></span>
|
||
<span id="cb8-145"> printLog <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">pure</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Error</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"Exception in AMQ-Thread: "</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">show</span> e</span>
|
||
<span id="cb8-146"> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">return</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Right</span> ()</span>
|
||
<span id="cb8-147"> ) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> AMQ.try <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">do</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- catches all AMQ-Exception that we can handle. All others bubble up.</span></span>
|
||
<span id="cb8-148"> printLog <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">pure</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Info</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"AMQ: connecting..."</span></span>
|
||
<span id="cb8-149"> withConnection myserviceActivemqHost myserviceActivemqPort [ <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">OAuth</span> myserviceActivemqUsername myserviceActivemqPassword</span>
|
||
<span id="cb8-150"> , <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">OTmo</span> (<span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">30</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">*</span><span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">1000</span>) <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">{- 30 sec timeout -}</span></span>
|
||
<span id="cb8-151"> ]</span>
|
||
<span id="cb8-152"> [] <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> \c <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">do</span></span>
|
||
<span id="cb8-153"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">let</span> oconv <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">return</span></span>
|
||
<span id="cb8-154"> printLog <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">pure</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Info</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"AMQ: connected"</span></span>
|
||
<span id="cb8-155"> withWriter c <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"Chaos-Logger for Kibana"</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"chaos.logs"</span> [] [] oconv <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> \writer <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">do</span></span>
|
||
<span id="cb8-156"> printLog <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">pure</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Info</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"AMQ: queue created"</span></span>
|
||
<span id="cb8-157"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">let</span> postfun <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> writeQ writer (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Type</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Application</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"json"</span>) []) []</span>
|
||
<span id="cb8-158"> void <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> race</span>
|
||
<span id="cb8-159"> (forever <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> atomically (readTQueue var) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">>>=</span> postfun)</span>
|
||
<span id="cb8-160"> (threadDelay (<span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">600</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">*</span><span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">1000</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">*</span><span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">1000</span>)) <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- wait 10 Minutes</span></span>
|
||
<span id="cb8-161"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- close writer</span></span>
|
||
<span id="cb8-162"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- close connection</span></span>
|
||
<span id="cb8-163"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- get outside of all try/handle/...-constructions befor recursing.</span></span>
|
||
<span id="cb8-164"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">case</span> res <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">of</span></span>
|
||
<span id="cb8-165"> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Left</span> ex <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">do</span></span>
|
||
<span id="cb8-166"> printLog <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">pure</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Error</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"AMQ: "</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">show</span> ex</span>
|
||
<span id="cb8-167"> keepActiveMQConnected sc printLog var</span>
|
||
<span id="cb8-168"> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Right</span> _ <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> keepActiveMQConnected sc printLog var</span>
|
||
<span id="cb8-169"></span>
|
||
<span id="cb8-170"></span>
|
||
<span id="cb8-171"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Beispiel für eine Custom-Logging-Middleware.</span></span>
|
||
<span id="cb8-172"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Hier werden z.B. alle 4xx-Status-Codes inkl. Payload ins stdout-Log geschrieben.</span></span>
|
||
<span id="cb8-173"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Nützlich, wenn die Kollegen ihre Requests nicht ordentlich schreiben können und der Server das Format zurecht mit einem BadRequest ablehnt ;)</span></span>
|
||
<span id="cb8-174"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">loggingMiddleware ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IO</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Middleware</span></span>
|
||
<span id="cb8-175">loggingMiddleware <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> liftIO <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> mkRequestLogger <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> def { outputFormat <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">CustomOutputFormatWithDetails</span> out }</span>
|
||
<span id="cb8-176"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">where</span></span>
|
||
<span id="cb8-177"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> out ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ZonedDate</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">WAI.Request</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Status</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Maybe</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Integer</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">NominalDiffTime</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> [<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">BS.ByteString</span>] <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Builder</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LogStr</span></span>
|
||
<span id="cb8-178"> out _ r status _ _ payload _</span>
|
||
<span id="cb8-179"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">|</span> statusCode status <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><</span> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">300</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span></span>
|
||
<span id="cb8-180"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">|</span> statusCode status <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">></span> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">399</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">&&</span> statusCode status <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><</span> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">500</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"Error code "</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span>toLogStr (statusCode status) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span><span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">" sent. Request-Payload was: "</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">mconcat</span> (toLogStr <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><$></span> payload) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"\n"</span></span>
|
||
<span id="cb8-181"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">|</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">otherwise</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> toLogStr (<span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">show</span> r) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"\n"</span></span></code></pre></div>
|
||
{{< dend >}}
|
||
</blockquote></details></section>
|
||
<section id="weitere-instanzen-und-definitionen-die-der-generator-noch-nicht-macht" class="level4">
|
||
<h4 class="anchored" data-anchor-id="weitere-instanzen-und-definitionen-die-der-generator-noch-nicht-macht">Weitere Instanzen und Definitionen, die der Generator (noch) nicht macht</h4>
|
||
<p>In der <code>Myservice.Types</code> werden ein paar hilfreiche Typen und Typ-Instanzen definiert. Im Folgenden geht es dabei um Dinge für:</p>
|
||
<ul>
|
||
<li><code>Envy</code>
|
||
<ul>
|
||
<li>Laden von <code>$ENV_VAR</code> in Datentypen</li>
|
||
<li>Definitionen für Default-Settings</li>
|
||
</ul></li>
|
||
<li><code>ServerConfig</code>
|
||
<ul>
|
||
<li>Definition der Server-Konfiguration & Benennung der Environment-Variablen</li>
|
||
</ul></li>
|
||
<li><code>ExtraTypes</code>
|
||
<ul>
|
||
<li>ggf. Paketweite extra-Typen, die der Generator nicht macht, weil sie nicht aus der API kommen (z.B. cache)</li>
|
||
</ul></li>
|
||
<li><code>Out</code>/<code>BSON</code>-Instanzen
|
||
<ul>
|
||
<li>Der API-Generator generiert nur wenige Instanzen automatisch (z.B. <code>aeson</code>), daher werden hier die fehlenden definiert.</li>
|
||
<li><code>BSON</code>: Kommunikation mit <code>MongoDB</code></li>
|
||
<li><code>Out</code>: pretty-printing im Log
|
||
<ul>
|
||
<li>Nur nötig, wenn man pretty-printing via <code>Out</code> statt über Generics wie z.b. <code>pretty-generic</code> oder die automatische Show-Instanz via <code>prerryShow</code> macht.</li>
|
||
</ul></li>
|
||
</ul></li>
|
||
</ul>
|
||
<p></p><p></p><details><summary>Types.hs anzeigen</summary><blockquote class="blockquote"><p></p>
|
||
<div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb9-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# OPTIONS_GHC -Wno-orphans #-}</span></span>
|
||
<span id="cb9-2"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# OPTIONS_GHC -Wno-name-shadowing #-}</span></span>
|
||
<span id="cb9-3"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE DeriveAnyClass #-}</span></span>
|
||
<span id="cb9-4"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE DeriveFunctor #-}</span></span>
|
||
<span id="cb9-5"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE DeriveGeneric #-}</span></span>
|
||
<span id="cb9-6"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE DerivingVia #-}</span></span>
|
||
<span id="cb9-7"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE DuplicateRecordFields #-}</span></span>
|
||
<span id="cb9-8"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE FlexibleContexts #-}</span></span>
|
||
<span id="cb9-9"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE FlexibleInstances #-}</span></span>
|
||
<span id="cb9-10"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE GADTs #-}</span></span>
|
||
<span id="cb9-11"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE LambdaCase #-}</span></span>
|
||
<span id="cb9-12"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE MultiParamTypeClasses #-}</span></span>
|
||
<span id="cb9-13"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE OverloadedStrings #-}</span></span>
|
||
<span id="cb9-14"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE RankNTypes #-}</span></span>
|
||
<span id="cb9-15"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE RecordWildCards #-}</span></span>
|
||
<span id="cb9-16"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">module</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">MyService.Types</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">where</span></span>
|
||
<span id="cb9-17"> </span>
|
||
<span id="cb9-18"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.Aeson</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">FromJSON</span>, <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ToJSON</span>)</span>
|
||
<span id="cb9-19"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.Text</span></span>
|
||
<span id="cb9-20"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.Time.Clock</span></span>
|
||
<span id="cb9-21"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">GHC.Generics</span></span>
|
||
<span id="cb9-22"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">System.Envy</span></span>
|
||
<span id="cb9-23"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Text.PrettyPrint</span> (text)</span>
|
||
<span id="cb9-24"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Text.PrettyPrint.GenericPretty</span></span>
|
||
<span id="cb9-25"> </span>
|
||
<span id="cb9-26"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Out hat hierfür keine Instanzen, daher kurz eine einfach Definition.</span></span>
|
||
<span id="cb9-27"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">instance</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Out</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Text</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">where</span></span>
|
||
<span id="cb9-28"> doc <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> text <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> unpack</span>
|
||
<span id="cb9-29"> docPrec i a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> text <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">showsPrec</span> i a <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span></span>
|
||
<span id="cb9-30"> </span>
|
||
<span id="cb9-31"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">instance</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Out</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">UTCTime</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">where</span></span>
|
||
<span id="cb9-32"> doc <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> text <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">show</span></span>
|
||
<span id="cb9-33"> docPrec i a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> text <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">showsPrec</span> i a <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span></span>
|
||
<span id="cb9-34"> </span>
|
||
<span id="cb9-35"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Der ServerConfig-Typ. Wird mit den defaults unten initialisiert, dann mit den Variablen aus der .env-Datei überschrieben und zum Schluss können Serveradmins diese via $MYSERVICE_FOO nochmal überschreiben.</span></span>
|
||
<span id="cb9-36"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">data</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ServerConfig</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ServerConfig</span></span>
|
||
<span id="cb9-37"> {<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceHost ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_HOST</span></span>
|
||
<span id="cb9-38"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myservicePort ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Int</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_PORT</span></span>
|
||
<span id="cb9-39"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceMaxTimeout ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Int</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_MAX_TIMEOUT</span></span>
|
||
<span id="cb9-40"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceInternalProxyUrl ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_INTERNAL_PROXY_URL</span></span>
|
||
<span id="cb9-41"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceInternalProxyPort ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Int</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_INTERNAL_PROXY_PORT</span></span>
|
||
<span id="cb9-42"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceExternalProxyUrl ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_EXTERNAL_PROXY_URL</span></span>
|
||
<span id="cb9-43"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceExternalProxyPort ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Int</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_EXTERNAL_PROXY_PORT</span></span>
|
||
<span id="cb9-44"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceActivemqHost ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_ACTIVEMQ_HOST</span></span>
|
||
<span id="cb9-45"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceActivemqPort ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Int</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_ACTIVEMQ_PORT</span></span>
|
||
<span id="cb9-46"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceActivemqUsername ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_ACTIVEMQ_USERNAME</span></span>
|
||
<span id="cb9-47"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceActivemqPassword ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_ACTIVEMQ_PASSWORD</span></span>
|
||
<span id="cb9-48"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceMongoUsername ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_MONGO_USERNAME</span></span>
|
||
<span id="cb9-49"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceMongoPassword ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_MONGO_PASSWORD</span></span>
|
||
<span id="cb9-50"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> myserviceDebug ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Bool</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ^ Environment: $MYSERVICE_DEBUG</span></span>
|
||
<span id="cb9-51"> } <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">deriving</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Show</span>, <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Eq</span>, <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Generic</span>)</span>
|
||
<span id="cb9-52"> </span>
|
||
<span id="cb9-53"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Default-Konfigurations-Instanz für diesen Service.</span></span>
|
||
<span id="cb9-54"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">instance</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">DefConfig</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ServerConfig</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">where</span></span>
|
||
<span id="cb9-55"> defConfig <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ServerConfig</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"0.0.0.0"</span> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">8080</span> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">20</span></span>
|
||
<span id="cb9-56"> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span></span>
|
||
<span id="cb9-57"> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span></span>
|
||
<span id="cb9-58"> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span></span>
|
||
<span id="cb9-59"> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">0</span></span>
|
||
<span id="cb9-60"> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span></span>
|
||
<span id="cb9-61"> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">0</span></span>
|
||
<span id="cb9-62"> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span></span>
|
||
<span id="cb9-63"> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">0</span></span>
|
||
<span id="cb9-64"> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span></span>
|
||
<span id="cb9-65"> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span></span>
|
||
<span id="cb9-66"> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span></span>
|
||
<span id="cb9-67"> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">""</span></span>
|
||
<span id="cb9-68"> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">False</span></span>
|
||
<span id="cb9-69"> </span>
|
||
<span id="cb9-70"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Kann auch aus dem ENV gefüllt werden</span></span>
|
||
<span id="cb9-71"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">instance</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">FromEnv</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ServerConfig</span></span>
|
||
<span id="cb9-72"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Und hübsch ausgegeben werden.</span></span>
|
||
<span id="cb9-73"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">instance</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Out</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ServerConfig</span></span>
|
||
<span id="cb9-74"> </span>
|
||
<span id="cb9-75"> </span>
|
||
<span id="cb9-76"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">instance</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Out</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Response</span></span>
|
||
<span id="cb9-77"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">instance</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">FromBSON</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Repsonse</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- FromBSON-Instanz geht immer davon aus, dass alle keys da sind (ggf. mit null bei Nothing).</span></span></code></pre></div>
|
||
{{< dend >}}
|
||
</blockquote></details></section>
|
||
<section id="was-noch-zu-tun-ist" class="level4">
|
||
<h4 class="anchored" data-anchor-id="was-noch-zu-tun-ist">Was noch zu tun ist</h4>
|
||
<p>Den Service implementieren. Einfach ein neues Modul aufmachen (z.B. <code>MyService.Handler</code> oder <code>MyService.DieserEndpunktbereich</code>/<code>MyService.JenerEndpunktbereich</code>) und dort die Funktion implementieren, die man in der <code>Main.hs</code> benutzt hat. In dem Handler habt ihr dann keinen Stress mehr mit Validierung, networking, logging, etc. pp. weil alles in der Main abgehandelt wurde und ihr nur noch den “Happy-Case” implementieren müsst. Beispiel für unseren Handler oben:</p>
|
||
<div class="sourceCode" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb10-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">myApiEndpointV1Post ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">MonadIO</span> m <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ServerConfig</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ClientEnv</span>,<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">ClientEnv</span>) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">TQueue</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">BS.ByteString</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> ([<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LogItem</span>] <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IO</span> ()) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Request</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> m <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Response</span></span>
|
||
<span id="cb10-2">myApiEndpointV1Post sc calls amqPost <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">log</span> req <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">do</span></span>
|
||
<span id="cb10-3"> liftIO <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">log</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> [<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Info</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"recieved "</span><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span>pretty req] <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- input-logging</span></span>
|
||
<span id="cb10-4"> liftIO <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> atomically <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> writeTQueue <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> LBS.toStrict <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"{\"hey Kibana, i recieved:\""</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span> A.encode (pretty req) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><></span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"}"</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- log in activeMQ/Kibana</span></span>
|
||
<span id="cb10-5"></span>
|
||
<span id="cb10-6"></span>
|
||
<span id="cb10-7"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">--- .... gaaaanz viel komplizierter code um die Response zu erhalten ;)</span></span>
|
||
<span id="cb10-8"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">let</span> ret <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Response</span> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">1337</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Nothing</span> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- dummy-response ;)</span></span>
|
||
<span id="cb10-9"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- gegeben wir haben eine gültige mongodb-pipe;</span></span>
|
||
<span id="cb10-10"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- mehr logik will ich in die Beispiele nicht packen.</span></span>
|
||
<span id="cb10-11"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Man kann die z.b. als weiteren Wert in einer TMVar (damit man sie ändern & updaten kann) an die Funktion übergeben.</span></span>
|
||
<span id="cb10-12"> liftIO <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> access pipe master <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"DatabaseName"</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">do</span></span>
|
||
<span id="cb10-13"> ifM (auth (myServiceMongoUsername sc) (myServiceMongoPassword sc)) (<span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">return</span> ()) (liftIO <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> printLog <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">pure</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Error</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"MongoDB: Login failed."</span>)</span>
|
||
<span id="cb10-14"> save <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"DatabaseCollection"</span> [<span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"_id"</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">=:</span> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">1337</span>, <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"entry"</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">=:</span> ret] <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- selbe id wie oben ;)</span></span>
|
||
<span id="cb10-15"> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">return</span> ret</span></code></pre></div>
|
||
<p>Diese dummy-Antwort führt auf, wie gut man die ganzen Sachen mischen kann.</p>
|
||
<ul>
|
||
<li>Logging in die Dateien/<code>stdout</code> - je nach Konfiguration</li>
|
||
<li>Logging von Statistiken in Kibana</li>
|
||
<li>Speichern der Antwort in der MongoDB</li>
|
||
<li>Generieren einer Serverantwort und ausliefern dieser über die Schnittstelle</li>
|
||
</ul>
|
||
</section>
|
||
<section id="tipps-tricks" class="level4">
|
||
<h4 class="anchored" data-anchor-id="tipps-tricks">Tipps & Tricks</h4>
|
||
<section id="dateien-die-statisch-ausgeliefert-werden-sollen" class="level5">
|
||
<h5 class="anchored" data-anchor-id="dateien-die-statisch-ausgeliefert-werden-sollen">Dateien, die statisch ausgeliefert werden sollen</h5>
|
||
<p>Hierzu erstellt man ein Verzeichnis <code>static/</code> (Konvention; ist im generator so generiert, dass das ausgeliefert wird). Packt man hier z.b. eine <code>index.html</code> rein, erscheint die, wenn man den Service ansurft.</p>
|
||
</section>
|
||
<section id="wie-bekomme-ich-diese-fancy-preview-hin" class="level5">
|
||
<h5 class="anchored" data-anchor-id="wie-bekomme-ich-diese-fancy-preview-hin">Wie bekomme ich diese fancy Preview hin?</h5>
|
||
<p>Der Editor, der ganz am Anfang zum Einsatz gekommen ist, braucht nur die <code>api-doc.yml</code> um diese Ansicht zu erzeugen. Daher empfiehlt sich hier ein angepasster Fork davon indem die Pfade in der index.html korrigiert sind. Am einfachsten (und von den meisten services so benutzt): In meiner Implementation liegt dann nach dem starten auf <a href="http://localhost:PORT/ui/" class="uri">http://localhost:PORT/ui/</a> und kann direkt dort getestet werden.</p>
|
||
</section>
|
||
<section id="wie-sorge-ich-für-bessere-warnungen-damit-der-compiler-meine-bugs-fängt" class="level5">
|
||
<h5 class="anchored" data-anchor-id="wie-sorge-ich-für-bessere-warnungen-damit-der-compiler-meine-bugs-fängt">Wie sorge ich für bessere Warnungen, damit der Compiler meine Bugs fängt?</h5>
|
||
<div class="sourceCode" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb11-1"><span class="ex" style="color: null;
|
||
background-color: null;
|
||
font-style: inherit;">stack</span> build <span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;">--file-watch</span> <span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;">--ghc-options</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">'-freverse-errors -W -Wall -Wcompat'</span> <span class="at" style="color: #657422;
|
||
background-color: null;
|
||
font-style: inherit;">--interleaved-output</span></span></code></pre></div>
|
||
<p>Was tut das?</p>
|
||
<ul>
|
||
<li><code>--file-watch</code>: automatisches (minimales) kompilieren bei dateiänderungen</li>
|
||
<li><code>--ghc-options</code>
|
||
<ul>
|
||
<li><code>-freverse-errors</code>: Fehlermeldungen in umgekehrter Reihenfolge (Erster Fehler ganz unten; wenig scrollen )</li>
|
||
<li><code>-W</code>: Warnungen an</li>
|
||
<li><code>-Wall</code>: Alle sinnvollen Warnungen an (im gegensatz zu <code>-Weverything</code>, was WIRKLICH alles ist )</li>
|
||
<li><code>-Wcompat</code>: Warnungen für Sachen, die in der nächsten Compilerversion kaputt brechen werden & vermieden werden sollten</li>
|
||
</ul></li>
|
||
<li><code>--interleaved-output</code>: stack-log direkt ausgeben & nicht in Dateien schreiben und die dann am ende zusammen cat'en.</li>
|
||
</ul>
|
||
<p>Um pro Datei Warnungen auszuschalten (z.B. weil man ganz sicher weiss, was man tut -.-): <code>{-# OPTIONS_GHC -Wno-whatsoever #-}</code> als pragma in die Datei.</p>
|
||
<p><strong>Idealerweise sollte das Projekt keine Warnungen erzeugen.</strong></p>
|
||
</section>
|
||
</section>
|
||
</section>
|
||
<section id="deployment" class="level3">
|
||
<h3 class="anchored" data-anchor-id="deployment">Deployment</h3>
|
||
<p>Als Beispiel sei hier ein einfaches Docker-Build mit Jenkins-CI gezeigt, weil ich das aus Gründen rumliegen hatte. Kann man analog in fast alle anderen CI übersetzen.</p>
|
||
<section id="docker" class="level4">
|
||
<h4 class="anchored" data-anchor-id="docker">Docker</h4>
|
||
<p>Die angehängten Scripte gehen von einer Standard-Einrichtung aus (statische Sachen in static, 2-3 händische Anpassungen auf das eigene Projekt nach auspacken). Nachher liegt dann auch unter static/version die gebaute Versionsnummer & kann abgerufen werden. In der <code>Dockerfile.release</code> und der <code>Jenkinsfile</code> müssen noch Anpassungen gemacht werden. Konkret:</p>
|
||
<ul>
|
||
<li>in der <code>Dockerfile.release</code>: alle <code><<<HIER>>></code>-Stellen sinnvoll befüllen</li>
|
||
<li>in der <code>Jenkinsfile</code> die defs für “servicename” und “servicebinary” ausfüllen. Binary ist das, was bei stack exec aufgerufen wird; name ist der Image-Name für das docker-repository.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="jenkins" class="level4">
|
||
<h4 class="anchored" data-anchor-id="jenkins">Jenkins</h4>
|
||
<p>Änderungen die dann noch gemacht werden müssen:</p>
|
||
<ul>
|
||
<li>git-repository URL anpassen</li>
|
||
<li>Environment-Vars anpassen ($BRANCH = test & live haben keine zusatzdinger im docker-image-repository; ansonsten hat das image $BRANCH im Namen)</li>
|
||
</ul>
|
||
<p>Wenn das fertig gebaut ist, liegt im test/live-repository ein docker-image namens <code>servicename:version</code>.</p>
|
||
</section>
|
||
</section>
|
||
<section id="omg-ich-muss-meine-api-ändern.-was-mache-ich-nun" class="level3">
|
||
<h3 class="anchored" data-anchor-id="omg-ich-muss-meine-api-ändern.-was-mache-ich-nun">OMG! Ich muss meine API ändern. Was mache ich nun?</h3>
|
||
<ol type="1">
|
||
<li>api-doc.yml bearbeiten, wie gewünscht</li>
|
||
<li>mittels generator die Api & submodule neu generieren</li>
|
||
<li>ggf. custom Änderungen übernehmen (:Gitdiffsplit hilft)</li>
|
||
<li>Alle Compilerfehler + Warnungen in der eigentlichen Applikation fixen</li>
|
||
<li>If it comipilez, ship it! (Besser nicht :grin:)</li>
|
||
</ol>
|
||
|
||
|
||
</section>
|
||
</section>
|
||
|
||
]]></description>
|
||
<category>Haskell</category>
|
||
<category>Tutorial</category>
|
||
<guid>https://drezil.de/Coding/Haskell/Webapp-Example/</guid>
|
||
<pubDate>Tue, 31 Mar 2020 22:00:00 GMT</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>Mental Health</title>
|
||
<link>https://drezil.de/Health/Issues.html</link>
|
||
<description><![CDATA[
|
||
|
||
|
||
|
||
|
||
|
||
<p>In modern times many people struggle with mental health issues - and a am by no means an exception. The main issue is, that most people just don’t talk about it and suffer alone, thinking they are alone, and everyone else is just doing fine in this hellscape of a modern society. At least that is what you see on several social media platforms like Instagram etc.</p>
|
||
<p>So even despite my exceptional<sup>1</sup> successes that can be seen in my <a href="../About/Work.html">work</a> i always struggled with issues even to the point of total breakdown. Of course i am also guilty of painting a rosy picture of me - just look at <a href="../About/Experience.html">a summary of my experiences</a> or the awesome <a href="../About/Extracurricular.html">things i did at university</a>. If you only read that it is hard to believe that i basically had to delay my studies from 2007 to 2010 because i wasn’t even really able to leave the house.</p>
|
||
<div class="no-row-height column-margin column-container"><div id="fn1"><p><sup>1</sup> citation needed</p></div></div><p>Only thanks to the not-that-awful system in Germany and massive financial help from my parents i was even able to pursue this way.</p>
|
||
<section id="what-are-my-issues" class="level2">
|
||
<h2 class="anchored" data-anchor-id="what-are-my-issues">What are my issues?</h2>
|
||
<p>Well.. after 15 long years of therapy i finally get a hang of all my issues. Many of those are related, some are just the result of coping mechanisms of ignoring other issues.</p>
|
||
<p>Currently i am successfully diagnosed with</p>
|
||
<ul>
|
||
<li>social anxiety</li>
|
||
<li>ADHD</li>
|
||
<li>transgenderism</li>
|
||
</ul>
|
||
<p>and i got a big suspician of</p>
|
||
<ul>
|
||
<li>autism</li>
|
||
</ul>
|
||
<p>All in all: when i feel well, am good rested and have nothing critical coming up i am more of what i would call a “high functioning Autist, but not THAT far on the spectrum”. But it is funny that while finding out who i really am, i met more people who basically had the same issue and a similar biography like mine. Some of them get the autism-diagnosis first, others the ADHD one - since until some time ago those diagnosis were mutually exclusive.</p>
|
||
<p>That’s also why many people like me are only really diagnosed as adults, because autism hides many effects of ADHD and vice-versa - depending on which one is currently dominating. It is basically 2 modes: do everything all at once and start everything that grabs your attention - or do a deep dive into a single thing. And the exact opposite: The ADHD part being bored by the autism-project, the autism-part is completely overwhelmed by the ADHD chaos. Both then leading to exhaustion, not being able to do anything .. and basically feeling guilty for the things you did not manage to finish.</p>
|
||
<p>Today i finally found myself. I currently have a great partner (with 3 kids) - and <strong>all</strong> of them have similar issues. Like i said: I best get along with similar people - and also fall in love with those.. and as AuDHD runs in the genes all offspring has a good chance of catching it to varies degrees, too.</p>
|
||
<p>I think the most important thing was the ADHD-Diagnosis - as this enabled me to get metylphenidate to basically get into a “3-4 hours focused as long as the pill works” and total chaos afterwards. This enables me to have productive days/times where i can do all the boring-work that my ADHD-Part wants to sit out and the autism part is overwhelmed from even starting.</p>
|
||
</section>
|
||
<section id="the-early-days" class="level2">
|
||
<h2 class="anchored" data-anchor-id="the-early-days">The early days</h2>
|
||
<p>To be continued …</p>
|
||
|
||
|
||
</section>
|
||
|
||
|
||
]]></description>
|
||
<category>Mental Health</category>
|
||
<category>Article</category>
|
||
<category>Struggles</category>
|
||
<guid>https://drezil.de/Health/Issues.html</guid>
|
||
<pubDate>Mon, 31 Dec 2018 23:00:00 GMT</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>Fortgeschrittene funktionale Programmierung in Haskell</title>
|
||
<link>https://drezil.de/Coding/Haskell/FFPiH.html</link>
|
||
<description><![CDATA[
|
||
|
||
|
||
|
||
|
||
|
||
<p>FFPiH ist eine Vorlesung, die ich zusammen mit einem Kommilitonen im Sommer 2015 erstmals erstellt und gehalten haben.</p>
|
||
<p>Insgesamt haben wir die Vorlesung 3x gehalten, wobei von der ersten zur zweiten Iteration der Inhalt massiv überarbeitet wurde und bei der Iteration von der zweiten zur dritten Vorlesung die Übungen komplett neu erstellt wurden.</p>
|
||
<p>Die gesamten Übungen sind unter anderem in der FFPiH-Organisation in meinem gitea hinterlegt: <a href="https://gitea.dresselhaus.cloud/FFPiH">https://gitea.dresselhaus.cloud/FFPiH</a></p>
|
||
<p>Einige der aktualisierten Übungen sind privat geschaltet, da diese iterativ aufeinander aufbauen und jeweils die Musterlösung der vorherigen enthalten.</p>
|
||
<section id="aufbau-der-vorlesung" class="level2">
|
||
<h2 class="anchored" data-anchor-id="aufbau-der-vorlesung">Aufbau der Vorlesung</h2>
|
||
<p>Vorausgesetzt wurde, dass die Studierenden das erste Semester abgeschlossen hatten und somit bereits leichte Grundlagen in Haskell kannten (aber z.b. Dinge wie Functor/Applicative/Monad noch nicht <em>wirklich</em> erklärt bekommen haben).</p>
|
||
<p>Stück für Stück werden die Studis dann zunächst in abstrakte Konstrukte eingeführt, aber diese werden dann schnell in die Praxis umgesetzt. Etwa mit dem Schreiben eines eigenen Parsers.</p>
|
||
<p>Schlussendlich gibt es dann einen “Rundumschlag” durch die gesamte Informatik. Erstellung eines Spieles (auf basis einer kleinen Grundlage), erstellung von WebApps mit Yesod, Parallelisierung und Nebenläufigkeit für rechenintensive Anwendungen inkl. synchronisation mittels STM.</p>
|
||
<p>Optional gab es weitere Übungen zu dingen wie “verteiltes Rechnen”.</p>
|
||
<p>Ziel hierbei war nicht, diese ganzen Themen in der Tiefe beizubringen, sondern aufzuzeigen, wie sie sehr schnell abstrakte Konstrukte, die ihnen ggf. 3 Semester später erst begegnen bugfrei benutzen können, da Haskell hier in sehr vielen Fällen einfach nur die “richtige” Lösung kompilieren lässt und alle gängigen Fallen schlicht ausschließt. Beispiel ist z.b. STM innerhalb von STM, Mischen von DB-Monade, Handler-Monade und Template-Engine in Yesod, Process () statt IO () in der Nutzung von CloudHaskell, etc. pp.</p>
|
||
</section>
|
||
<section id="studentisches-feedback" class="level2">
|
||
<h2 class="anchored" data-anchor-id="studentisches-feedback">Studentisches Feedback</h2>
|
||
<p>Sehr gutes Feedback von den Studenten bekamen wir insbesondere für Übungen wie:</p>
|
||
<p><a href="https://gitea.dresselhaus.cloud/FFPiH/uebung2017_2/src/branch/master/src/Aufgabe2.hs">Übung 2, Aufgabe 2</a>, weil hier durch “einfaches” umformen hin zu Abstraktionen und mit den Regeln dieser im ersten Fall die Laufzeit (vor Compileroptimierungen) von O(n²) auf O(0) ändert.</p>
|
||
<p><a href="https://gitea.dresselhaus.cloud/FFPiH/uebung2017-4">Übung 4</a>, welche ein komplett fertigen (sehr rudimentären und simplen) Dungeon-Crawler bereitstellt, der “nur” 1-2 bugs hat und “wie ein echtes Projekt” erweitert werden muss. Diese Übung hat sich dann über 4 weitere Übungen gestreckt, wobei folgende Aufgaben gelöst werden müssen:</p>
|
||
<ul>
|
||
<li>Einarbeitung in QuickCheck zur Behebung eines Bugs im Test</li>
|
||
<li>Umschreiben von explizitem Argument-Passing hin zu Monad-Transformers mit stateful <a href="../../Coding/Haskell/Lenses.html">Lenses</a></li>
|
||
<li>Continuation-Basierendes Event-System</li>
|
||
<li>Hinzufügen eines Parsers für Level, Items & deren Effekte und implementation dieser</li>
|
||
<li>Ändern des GUI-Parts von CLI auf 2D GL mittels gloss</li>
|
||
<li>Ändern von <code>StateT World</code> auf <code>RWST GameConfig Log World</code> und somit nutzen von individuellen Konfigurationen für z.b. Keybindings</li>
|
||
</ul>
|
||
|
||
|
||
</section>
|
||
|
||
]]></description>
|
||
<category>Lecture</category>
|
||
<category>Haskell</category>
|
||
<guid>https://drezil.de/Coding/Haskell/FFPiH.html</guid>
|
||
<pubDate>Sun, 31 Dec 2017 23:00:00 GMT</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>Lenses</title>
|
||
<link>https://drezil.de/Coding/Haskell/Lenses.html</link>
|
||
<description><![CDATA[
|
||
|
||
|
||
|
||
|
||
|
||
<section id="wofür-brauchen-wir-das-überhaupt" class="level2">
|
||
<h2 class="anchored" data-anchor-id="wofür-brauchen-wir-das-überhaupt">Wofür brauchen wir das überhaupt?</h2>
|
||
<p>Die Idee dahinter ist, dass man Zugriffsabstraktionen über Daten verknüpfen<br>
|
||
kann. Also einfachen Datenstruktur kann man einen Record mit der entsprechenden<br>
|
||
Syntax nehmen.</p>
|
||
<section id="beispiel" class="level3">
|
||
<h3 class="anchored" data-anchor-id="beispiel">Beispiel</h3>
|
||
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb1-1"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">data</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Person</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">P</span> {<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> name ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span></span>
|
||
<span id="cb1-2"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> addr ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Address</span></span>
|
||
<span id="cb1-3"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> salary ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Int</span> }</span>
|
||
<span id="cb1-4"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">data</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Address</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">A</span> {<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> road ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span></span>
|
||
<span id="cb1-5"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> city ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span></span>
|
||
<span id="cb1-6"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> postcode ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> }</span>
|
||
<span id="cb1-7"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- autogeneriert unten anderem: addr :: Person -> Address</span></span>
|
||
<span id="cb1-8"></span>
|
||
<span id="cb1-9"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> setName ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Person</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Person</span></span>
|
||
<span id="cb1-10"> setName n p <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> p { name <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> n } <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">--record update notation</span></span>
|
||
<span id="cb1-11"></span>
|
||
<span id="cb1-12"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> setPostcode ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Person</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Person</span></span>
|
||
<span id="cb1-13"> setPostcode pc p</span>
|
||
<span id="cb1-14"> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> p { addr <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> addr p { postcode <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> pc } }</span>
|
||
<span id="cb1-15"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- update of a record inside a record</span></span></code></pre></div>
|
||
</section>
|
||
<section id="problem" class="level3">
|
||
<h3 class="anchored" data-anchor-id="problem">Problem</h3>
|
||
<p>Problem mit diesem Code:</p>
|
||
<ul>
|
||
<li>für 1-Dimensionale Felder ist die record-syntax ok.</li>
|
||
<li>tiefere Ebenen nur umständlich zu erreichen</li>
|
||
<li>eigentlich wollen wir nur pe in p setzen, müssen aber über addr etc. gehen.</li>
|
||
<li>wir brauchen wissen über die “Zwischenstrukturen”, an denen wir nicht<br>
|
||
interessiert sind</li>
|
||
</ul>
|
||
</section>
|
||
<section id="was-wir-gern-hätten" class="level3">
|
||
<h3 class="anchored" data-anchor-id="was-wir-gern-hätten">Was wir gern hätten</h3>
|
||
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb2-1"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">data</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Person</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">P</span> {<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> name ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span></span>
|
||
<span id="cb2-2"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> addr ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Address</span></span>
|
||
<span id="cb2-3"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> salary ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Int</span> }</span>
|
||
<span id="cb2-4"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- a lens for each field</span></span>
|
||
<span id="cb2-5"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">lname ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens'</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Person</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span></span>
|
||
<span id="cb2-6"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">laddr ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens'</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Person</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Adress</span></span>
|
||
<span id="cb2-7"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">lsalary ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens'</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Person</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Int</span></span>
|
||
<span id="cb2-8"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- getter/setter for them</span></span>
|
||
<span id="cb2-9"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">view ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens'</span> s a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> a</span>
|
||
<span id="cb2-10"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">set ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens'</span> s a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s</span>
|
||
<span id="cb2-11"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- lens-composition</span></span>
|
||
<span id="cb2-12"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">composeL ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens'</span> s1 s2 <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens</span> s2 a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens'</span> s1 a</span></code></pre></div>
|
||
</section>
|
||
<section id="wie-uns-das-hilft" class="level3">
|
||
<h3 class="anchored" data-anchor-id="wie-uns-das-hilft">Wie uns das hilft</h3>
|
||
<p>Mit diesen Dingen (wenn wir sie hätten) könnte man dann</p>
|
||
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb3-1"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">data</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Person</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">P</span> {<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> name ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span></span>
|
||
<span id="cb3-2"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> addr ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Address</span></span>
|
||
<span id="cb3-3"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> salary ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Int</span> }</span>
|
||
<span id="cb3-4"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">data</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Address</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">A</span> {<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> road ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span></span>
|
||
<span id="cb3-5"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> city ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span></span>
|
||
<span id="cb3-6"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> postcode ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> }</span>
|
||
<span id="cb3-7"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">setPostcode ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Person</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Person</span></span>
|
||
<span id="cb3-8">setPostcode pc p</span>
|
||
<span id="cb3-9"> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> set (laddr <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">`composeL`</span> lpostcode) pc p</span></code></pre></div>
|
||
<p>machen und wäre fertig.</p>
|
||
</section>
|
||
</section>
|
||
<section id="trivialer-ansatz" class="level2">
|
||
<h2 class="anchored" data-anchor-id="trivialer-ansatz">Trivialer Ansatz</h2>
|
||
<section id="gettersetter-also-lens-methoden" class="level3">
|
||
<h3 class="anchored" data-anchor-id="gettersetter-also-lens-methoden">Getter/Setter also Lens-Methoden</h3>
|
||
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb4-1"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">data</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LensR</span> s a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">L</span> {<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> viewR ::</span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> a</span>
|
||
<span id="cb4-2"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> setR ::</span> a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s }</span>
|
||
<span id="cb4-3"></span>
|
||
<span id="cb4-4">composeL (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">L</span> v1 u1) (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">L</span> v2 u2)</span>
|
||
<span id="cb4-5"> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">L</span> (\s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> v2 (v1 s))</span>
|
||
<span id="cb4-6"> (\a s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> u1 (u2 a (v1 s)) s)</span></code></pre></div>
|
||
</section>
|
||
<section id="wieso-ist-das-schlecht" class="level3">
|
||
<h3 class="anchored" data-anchor-id="wieso-ist-das-schlecht">Wieso ist das schlecht?</h3>
|
||
<ul>
|
||
<li>extrem ineffizient</li>
|
||
</ul>
|
||
<p>Auslesen traversiert die Datenstruktur, dann wird die Function angewendet und<br>
|
||
zum setzen wird die Datenstruktur erneut traversiert:</p>
|
||
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb5-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">over ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LensR</span> s a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> (a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> a) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s</span>
|
||
<span id="cb5-2">over ln f s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> setR l (f (viewR l s)) s</span></code></pre></div>
|
||
<ul>
|
||
<li>Lösung: modify-funktion hinzufügen</li>
|
||
</ul>
|
||
<div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb6-1"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">data</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LensR</span> s a</span>
|
||
<span id="cb6-2"> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">L</span> {<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> viewR ::</span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> a</span>
|
||
<span id="cb6-3"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> setR ::</span> a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s</span>
|
||
<span id="cb6-4"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> mod ::</span> (a<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span>a) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s</span>
|
||
<span id="cb6-5"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> modM ::</span> (a<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span><span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Maybe</span> a) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Maybe</span> s</span>
|
||
<span id="cb6-6"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> modIO ::</span> (a<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span><span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IO</span> a) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IO</span> s }</span></code></pre></div>
|
||
<p>Neues Problem: Für jeden Spezialfall muss die Lens erweitert werden.</p>
|
||
</section>
|
||
<section id="something-in-common" class="level3">
|
||
<h3 class="anchored" data-anchor-id="something-in-common">Something in common</h3>
|
||
<p>Man kann alle Monaden abstrahieren. Functor reicht schon:</p>
|
||
<div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb7-1"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">data</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LensR</span> s a</span>
|
||
<span id="cb7-2"> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">L</span> {<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> viewR ::</span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> a</span>
|
||
<span id="cb7-3"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> setR ::</span> a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s</span>
|
||
<span id="cb7-4"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> mod ::</span> (a<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span>a) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s</span>
|
||
<span id="cb7-5"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> modF ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Functor</span> f <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=></span> (a<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span>f a) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> f s }</span></code></pre></div>
|
||
<p>Idee: Die 3 darüberliegenden durch modF ausdrücken.</p>
|
||
</section>
|
||
<section id="typ-einer-lens" class="level3">
|
||
<h3 class="anchored" data-anchor-id="typ-einer-lens">Typ einer Lens</h3>
|
||
<p>Wenn man das berücksichtigt, dann hat einen Lens folgenden Typ:</p>
|
||
<div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb8-1"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">type</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens'</span> s a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">forall</span> f<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Functor</span> f</span>
|
||
<span id="cb8-2"> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=></span> (a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> f a) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> f s</span></code></pre></div>
|
||
<p>Allerdings haben wir dann noch unseren getter/setter:</p>
|
||
<div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb9-1"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">data</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LensR</span> s a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">L</span> {<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> viewR ::</span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> a</span>
|
||
<span id="cb9-2"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> setR ::</span> a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s }</span></code></pre></div>
|
||
<p>Stellt sich raus: Die sind isomorph! Auch wenn die von den Typen her komplett<br>
|
||
anders aussehen.</p>
|
||
</section>
|
||
</section>
|
||
<section id="benutzen-einer-lens-also-setter" class="level2">
|
||
<h2 class="anchored" data-anchor-id="benutzen-einer-lens-also-setter">Benutzen einer Lens also Setter</h2>
|
||
<div class="sourceCode" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb10-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">set ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens'</span> s a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> (a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s)</span>
|
||
<span id="cb10-2">set ln a s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">--...</span>umm<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">...</span></span>
|
||
<span id="cb10-3"><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">--:</span>t ln <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=></span> (a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> f a) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> f s</span>
|
||
<span id="cb10-4"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- => get s out of f s to return it</span></span></code></pre></div>
|
||
<p>Wir können für f einfach die “Identity”-Monade nehmen, die wir nachher wegcasten<br>
|
||
können.</p>
|
||
<div class="sourceCode" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb11-1"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">newtype</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Identity</span> a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Identity</span> a</span>
|
||
<span id="cb11-2"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Id :: a -> Identity a</span></span>
|
||
<span id="cb11-3"></span>
|
||
<span id="cb11-4"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">runIdentity ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Identity</span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s</span>
|
||
<span id="cb11-5">runIdentity (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Identity</span> x) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> x</span>
|
||
<span id="cb11-6"></span>
|
||
<span id="cb11-7"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">instance</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Functor</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Identity</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">where</span></span>
|
||
<span id="cb11-8"> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">fmap</span> f (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Identity</span> x) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Identity</span> (f x)</span></code></pre></div>
|
||
<p>somit ist set einfach nur</p>
|
||
<div class="sourceCode" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb12-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">set ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens'</span> s a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> (a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s)</span>
|
||
<span id="cb12-2">set ln x s</span>
|
||
<span id="cb12-3"> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> runIdentity (ls set_fld s)</span>
|
||
<span id="cb12-4"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">where</span></span>
|
||
<span id="cb12-5"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> set_fld ::</span> a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Identity</span> a</span>
|
||
<span id="cb12-6"> set_fld _ <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Identity</span> x</span>
|
||
<span id="cb12-7"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- a was the OLD value.</span></span>
|
||
<span id="cb12-8"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- We throw that away and set the new value</span></span></code></pre></div>
|
||
<p>oder kürzer (für nerds wie den Author der Lens-Lib)</p>
|
||
<div class="sourceCode" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb13-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">set ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens'</span> s a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> (a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s)</span>
|
||
<span id="cb13-2">set ln x <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> runIdentity <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> ln (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Identity</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">const</span> x)</span></code></pre></div>
|
||
</section>
|
||
<section id="benutzen-einer-lens-also-modify" class="level2">
|
||
<h2 class="anchored" data-anchor-id="benutzen-einer-lens-also-modify">Benutzen einer Lens also Modify</h2>
|
||
<p>Dasselbe wie Set, nur dass wir den Parameter nicht entsorgen, sondern in die<br>
|
||
mitgelieferte Function stopfen.</p>
|
||
<div class="sourceCode" id="cb14" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb14-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">over ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens'</span> s a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> (a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> a) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s</span>
|
||
<span id="cb14-2">over ln f <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> runIdentity <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> ln (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Identity</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> f)</span></code></pre></div>
|
||
</section>
|
||
<section id="benutzen-einer-lens-also-getter" class="level2">
|
||
<h2 class="anchored" data-anchor-id="benutzen-einer-lens-also-getter">Benutzen einer Lens also Getter</h2>
|
||
<div class="sourceCode" id="cb15" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb15-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">view ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens'</span> s a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> (s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> a)</span>
|
||
<span id="cb15-2">view ln s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">--...</span>umm<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">...</span></span>
|
||
<span id="cb15-3"><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">--:</span>t ln <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=></span> (a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> f a) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> f s</span>
|
||
<span id="cb15-4"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- => get a out of the (f s) return-value</span></span>
|
||
<span id="cb15-5"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Wait, WHAT?</span></span></code></pre></div>
|
||
<p>Auch hier gibt es einen netten Funktor. Wir packen das “a” einfach in das “f”<br>
|
||
und werfen das “s” am End weg.</p>
|
||
<div class="sourceCode" id="cb16" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb16-1"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">newtype</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Const</span> v a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Const</span> v</span>
|
||
<span id="cb16-2"></span>
|
||
<span id="cb16-3"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">getConst ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Const</span> v a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> v</span>
|
||
<span id="cb16-4">getConst (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Const</span> x) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> x</span>
|
||
<span id="cb16-5"></span>
|
||
<span id="cb16-6"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">instance</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Functor</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Const</span> v) <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">where</span></span>
|
||
<span id="cb16-7"> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">fmap</span> f (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Const</span> x) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Const</span> x</span>
|
||
<span id="cb16-8"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- throw f away. Nothing changes our const!</span></span></code></pre></div>
|
||
<p>somit ergibt sich</p>
|
||
<div class="sourceCode" id="cb17" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb17-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">view ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens'</span> s a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> (s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> a)</span>
|
||
<span id="cb17-2">view ln s</span>
|
||
<span id="cb17-3"> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> getConst (ln <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Const</span> s)</span>
|
||
<span id="cb17-4"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Const :: s -> Const a s</span></span></code></pre></div>
|
||
<p>oder nerdig</p>
|
||
<div class="sourceCode" id="cb18" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb18-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">view ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens'</span> s a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> (s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> a)</span>
|
||
<span id="cb18-2">view ln <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> getConst <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> ln <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Const</span></span></code></pre></div>
|
||
</section>
|
||
<section id="lenses-bauen" class="level2">
|
||
<h2 class="anchored" data-anchor-id="lenses-bauen">Lenses bauen</h2>
|
||
<p>Nochmal kurz der Typ:</p>
|
||
<div class="sourceCode" id="cb19" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb19-1"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">type</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens'</span> s a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">forall</span> f<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Functor</span> f</span>
|
||
<span id="cb19-2"> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=></span> (a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> f a) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> f s</span></code></pre></div>
|
||
<p>Für unser Personen-Beispiel vom Anfang:</p>
|
||
<div class="sourceCode" id="cb20" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb20-1"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">data</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Person</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">P</span> {<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> _name ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span>,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> _salary ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Int</span> }</span>
|
||
<span id="cb20-2"></span>
|
||
<span id="cb20-3"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">name ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens'</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Person</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span></span>
|
||
<span id="cb20-4"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- name :: Functor f => (String -> f String)</span></span>
|
||
<span id="cb20-5"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- -> Person -> f Person</span></span>
|
||
<span id="cb20-6"></span>
|
||
<span id="cb20-7">name elt_fn (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">P</span> n s)</span>
|
||
<span id="cb20-8"> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">fmap</span> (\n' <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">P</span> n' s) (elt_fn n)</span>
|
||
<span id="cb20-9"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- fmap :: Functor f => (a->b) -> f a -> f b - der Funktor, der alles verknüpft</span></span>
|
||
<span id="cb20-10"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- \n' -> .. :: String -> Person - Funktion um das Element zu lokalisieren (WO wird ersetzt/gelesen/...)</span></span>
|
||
<span id="cb20-11"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- elt_fn n :: f String - Funktion um das Element zu verändern (setzen, ändern, ...)</span></span></code></pre></div>
|
||
<p>Die Lambda-Funktion ersetzt einfach den Namen. Häufig sieht man auch</p>
|
||
<div class="sourceCode" id="cb21" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb21-1">name elt_fn (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">P</span> n s)</span>
|
||
<span id="cb21-2"> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> (\n' <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">P</span> n' s) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><$></span> (elt_fn n)</span>
|
||
<span id="cb21-3"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- | Focus | |Function|</span></span></code></pre></div>
|
||
</section>
|
||
<section id="wie-funktioniert-das-intern" class="level2">
|
||
<h2 class="anchored" data-anchor-id="wie-funktioniert-das-intern">Wie funktioniert das intern?</h2>
|
||
<div class="sourceCode" id="cb22" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb22-1">view name (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">P</span> {_name<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span><span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"Fred"</span>, _salary<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">100</span>})</span>
|
||
<span id="cb22-2"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- inline view-function</span></span>
|
||
<span id="cb22-3"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> getConst (name <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Const</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">P</span> {_name<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span><span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"Fred"</span>, _salary<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">100</span>})</span>
|
||
<span id="cb22-4"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- inline name</span></span>
|
||
<span id="cb22-5"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> getConst (<span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">fmap</span> (\n' <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">P</span> n' <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">100</span>) (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Const</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"Fred"</span>))</span>
|
||
<span id="cb22-6"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- fmap f (Const x) = Const x - Definition von Const</span></span>
|
||
<span id="cb22-7"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> getConst (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Const</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"Fred"</span>)</span>
|
||
<span id="cb22-8"> <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- getConst (Const x) = x</span></span>
|
||
<span id="cb22-9"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"Fred"</span></span></code></pre></div>
|
||
<p>Dieser Aufruf hat KEINE Runtime-Kosten, weil der Compiler direkt die Address des<br>
|
||
Feldes einsetzen kann. Der gesamte Boilerplate-Code wird vom Compiler<br>
|
||
wegoptimiert.</p>
|
||
<p>Dies gilt für jeden Funktor mit newtype, da das nur ein Typalias ist.</p>
|
||
</section>
|
||
<section id="composing-lenses-und-deren-benutzung" class="level2">
|
||
<h2 class="anchored" data-anchor-id="composing-lenses-und-deren-benutzung">Composing Lenses und deren Benutzung</h2>
|
||
<p>Wie sehen denn die Typen aus?</p>
|
||
<p>Wir wollen ein</p>
|
||
<blockquote class="blockquote">
|
||
<p>Lens’ s1 s2 -> Lens’ s2 a -> Lens’ s1 a</p>
|
||
</blockquote>
|
||
<p>Wir haben 2 Lenses</p>
|
||
<blockquote class="blockquote">
|
||
<p>ln1 :: (s2 -> f s2) -> (s1 -> f s1)<br>
|
||
ln2 :: (a -> f a) -> (s2 -> f s2)</p>
|
||
</blockquote>
|
||
<p>wenn man scharf hinsieht, kann man die verbinden</p>
|
||
<blockquote class="blockquote">
|
||
<p>ln1 . ln2 :: (a -> f s) -> (s1 -> f s1)</p>
|
||
</blockquote>
|
||
<p>und erhält eine Lens. Sogar die Gewünschte!<br>
|
||
Somit ist Lens-Composition einfach nur Function-Composition (.).</p>
|
||
</section>
|
||
<section id="automatisieren-mit-template-haskell" class="level2">
|
||
<h2 class="anchored" data-anchor-id="automatisieren-mit-template-haskell">Automatisieren mit Template-Haskell</h2>
|
||
<p>Der Code um die Lenses zu bauen ist für records immer Identisch:</p>
|
||
<div class="sourceCode" id="cb23" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb23-1"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">data</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Person</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">P</span> {<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> _name ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span>,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> _salary ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Int</span> }</span>
|
||
<span id="cb23-2"></span>
|
||
<span id="cb23-3"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">name ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens'</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Person</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span></span>
|
||
<span id="cb23-4">name elt_fn (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">P</span> n s) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> (\n' <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">P</span> n' s) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><$></span> (elt_fn n)</span></code></pre></div>
|
||
<p>Daher kann man einfach</p>
|
||
<div class="sourceCode" id="cb24" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb24-1"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Control.Lens.TH</span></span>
|
||
<span id="cb24-2"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">data</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Person</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">P</span> {<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> _name ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span>,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> _salary ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Int</span> }</span>
|
||
<span id="cb24-3"></span>
|
||
<span id="cb24-4"><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span>(makeLenses '<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">'Person</span>)</span></code></pre></div>
|
||
<p>nehmen, was einem eine Lens für “name” und eine Lens für “salary” generiert.<br>
|
||
Mit anderen Templates kann man auch weitere Dinge steuern (etwa wofür Lenses<br>
|
||
generiert werden, welches Prefix (statt _) man haben will etc. pp.).</p>
|
||
<p>Will man das aber haben, muss man selbst in den Control.Lens.TH-Code schauen.</p>
|
||
</section>
|
||
<section id="lenses-für-den-beispielcode" class="level2">
|
||
<h2 class="anchored" data-anchor-id="lenses-für-den-beispielcode">Lenses für den Beispielcode</h2>
|
||
<div class="sourceCode" id="cb25" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb25-1"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Control.Lens.TH</span></span>
|
||
<span id="cb25-2"></span>
|
||
<span id="cb25-3"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">data</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Person</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">P</span> {<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> _name ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span></span>
|
||
<span id="cb25-4"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> _addr ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Address</span></span>
|
||
<span id="cb25-5"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> _salary ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Int</span> }</span>
|
||
<span id="cb25-6"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">data</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Address</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">A</span> {<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> _road ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span></span>
|
||
<span id="cb25-7"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> _city ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span></span>
|
||
<span id="cb25-8"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> _postcode ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> }</span>
|
||
<span id="cb25-9"></span>
|
||
<span id="cb25-10"><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span>(makeLenses '<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">'Person</span>)</span>
|
||
<span id="cb25-11"><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span>(makeLenses '<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">'Address</span>)</span>
|
||
<span id="cb25-12"></span>
|
||
<span id="cb25-13"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">setPostcode ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Person</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Person</span></span>
|
||
<span id="cb25-14">setPostcode pc p <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> set (addr <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> postcode) pc p</span></code></pre></div>
|
||
</section>
|
||
<section id="shortcuts-mit-line-noise" class="level2">
|
||
<h2 class="anchored" data-anchor-id="shortcuts-mit-line-noise">Shortcuts mit “Line-Noise”</h2>
|
||
<div class="sourceCode" id="cb26" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb26-1"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- ...</span></span>
|
||
<span id="cb26-2"></span>
|
||
<span id="cb26-3"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">setPostcode ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Person</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Person</span></span>
|
||
<span id="cb26-4">setPostcode pc p <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> addr <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> postcode <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.~</span> pc <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> p</span>
|
||
<span id="cb26-5"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- | Focus |set|to what|in where</span></span>
|
||
<span id="cb26-6"></span>
|
||
<span id="cb26-7"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">getPostcode ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Person</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span></span>
|
||
<span id="cb26-8">getPostcode p <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> p <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">^.</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> addr <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> postcode</span>
|
||
<span id="cb26-9"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- |from|get| Focus |</span></span></code></pre></div>
|
||
<p>Es gibt drölf-zillionen weitere Infix-Operatoren (für Folds,<br>
|
||
Listenkonvertierungen, -traversierungen, …)</p>
|
||
</section>
|
||
<section id="virtuelle-felder" class="level2">
|
||
<h2 class="anchored" data-anchor-id="virtuelle-felder">Virtuelle Felder</h2>
|
||
<p>Man kann mit Lenses sogar Felder emulieren, die gar nicht da sind. Angenommen<br>
|
||
folgender Code:</p>
|
||
<div class="sourceCode" id="cb27" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb27-1"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">data</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Temp</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">T</span> {<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> _fahrenheit ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Float</span> }</span>
|
||
<span id="cb27-2"></span>
|
||
<span id="cb27-3"><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span>(makeLenses '<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">'Temp</span>)</span>
|
||
<span id="cb27-4"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- liefert Lens: fahrenheit :: Lens Temp Float</span></span>
|
||
<span id="cb27-5"></span>
|
||
<span id="cb27-6"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">centigrade ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Temp</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Float</span></span>
|
||
<span id="cb27-7">centigrade centi_fn (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">T</span> faren)</span>
|
||
<span id="cb27-8"> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> (\centi' <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">T</span> (cToF centi'))</span>
|
||
<span id="cb27-9"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><$></span> (centi_fn (fToC faren))</span>
|
||
<span id="cb27-10"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- cToF & fToC as Converter-Functions defined someplace else</span></span></code></pre></div>
|
||
<p>Hiermit kann man dann auch Funktionen, die auf Grad-Celsius rechnen auf Daten<br>
|
||
anwenden, die eigenlich nur Fahrenheit speichern, aber eine Umrechnung<br>
|
||
bereitstellen. Analog kann man auch einen Zeit-Datentypen definieren, der<br>
|
||
intern mit Sekunden rechnet (und somit garantiert frei von Fehlern wie -3<br>
|
||
Minuten oder 37 Stunden ist)</p>
|
||
</section>
|
||
<section id="non-record-strukturen" class="level2">
|
||
<h2 class="anchored" data-anchor-id="non-record-strukturen">Non-Record Strukturen</h2>
|
||
<p>Das ganze kann man auch parametrisieren und auf Non-Record-Strukturen anwenden.<br>
|
||
Beispielhaft an einer Map verdeutlicht:</p>
|
||
<div class="sourceCode" id="cb28" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb28-1"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- from Data.Lens.At</span></span>
|
||
<span id="cb28-2"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">at ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Ord</span> k <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=></span> k <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens'</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Map</span> k v) (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Maybe</span> v)</span>
|
||
<span id="cb28-3"></span>
|
||
<span id="cb28-4"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- oder identisch, wenn man die Lens' auflöst:</span></span>
|
||
<span id="cb28-5"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">at ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Ord</span> k, <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">forall</span> f<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Functor</span> f <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=></span> k <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Maybe</span> v <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> f <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Maybe</span> v) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Map</span> k v <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> f <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Map</span> k v</span>
|
||
<span id="cb28-6"></span>
|
||
<span id="cb28-7">at k mb_fn m</span>
|
||
<span id="cb28-8"> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> wrap <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><$></span> (mb_fn mv)</span>
|
||
<span id="cb28-9"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">where</span></span>
|
||
<span id="cb28-10"> mv <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> Map.lookup k m</span>
|
||
<span id="cb28-11"></span>
|
||
<span id="cb28-12"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> wrap ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Maybe</span> v <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Map</span> k v</span>
|
||
<span id="cb28-13"> wrap (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Just</span> v') <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> Map.insert k v' m</span>
|
||
<span id="cb28-14"> wrap <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Nothing</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">case</span> mv <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">of</span></span>
|
||
<span id="cb28-15"> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Nothing</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> m</span>
|
||
<span id="cb28-16"> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Just</span> _ <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> Map.delete k m</span>
|
||
<span id="cb28-17"></span>
|
||
<span id="cb28-18"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- mb_fn :: Maybe v -> f Maybe v</span></span></code></pre></div>
|
||
</section>
|
||
<section id="weitere-beispiele" class="level2">
|
||
<h2 class="anchored" data-anchor-id="weitere-beispiele">Weitere Beispiele</h2>
|
||
<ul>
|
||
<li><p>Bitfields auf Strukturen die Bits haben (Ints, …) in Data.Bits.Lens</p></li>
|
||
<li><p>Web-scraper in Package hexpat-lens</p>
|
||
<div class="sourceCode" id="cb29" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb29-1">p <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">^..</span> _HTML' <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> to allNodes</span>
|
||
<span id="cb29-2"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">traverse</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> named <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"a"</span></span>
|
||
<span id="cb29-3"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">traverse</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> ix <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"href"</span></span>
|
||
<span id="cb29-4"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> filtered isLocal</span>
|
||
<span id="cb29-5"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> to trimSpaces</span></code></pre></div>
|
||
<p>Zieht alle externen Links aus dem gegebenen HTML-Code in p um weitere ziele<br>
|
||
fürs crawlen zu finden.</p></li>
|
||
</ul>
|
||
</section>
|
||
<section id="erweiterungen" class="level2">
|
||
<h2 class="anchored" data-anchor-id="erweiterungen">Erweiterungen</h2>
|
||
<p>Bisher hatten wir Lenses nur auf Funktoren F. Die nächstmächtigere Klasse ist<br>
|
||
Applicative.</p>
|
||
<div class="sourceCode" id="cb30" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb30-1"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">type</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Traversal'</span> s a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">forall</span> f<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Applicative</span> f</span>
|
||
<span id="cb30-2"> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=></span> (a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> f a) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> (s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> f s)</span></code></pre></div>
|
||
<p>Da wir den Container identisch lassen (weder s noch a wurde angefasst) muss sich<br>
|
||
etwas anderes ändern. Statt eines einzelnen Focus erhalten wir viele Foci.</p>
|
||
<p>Was ist ein Applicative überhaupt? Eine schwächere Monade (nur 1x Anwendung und<br>
|
||
kein Bind - dafür kann man die beliebig oft hintereinanderhängen).</p>
|
||
<div class="sourceCode" id="cb31" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb31-1"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">class</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Functor</span> f <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Applicative</span> f <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">where</span></span>
|
||
<span id="cb31-2"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> pure ::</span> a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> f a</span>
|
||
<span id="cb31-3"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> (<*>) ::</span> f (a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> b) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> f a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> f b</span>
|
||
<span id="cb31-4"></span>
|
||
<span id="cb31-5"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- Monade als Applicative:</span></span>
|
||
<span id="cb31-6"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">pure</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">return</span></span>
|
||
<span id="cb31-7">mf <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><*></span> mx <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">do</span> { f <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"><-</span> mf; x <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"><-</span> mx; <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">return</span> (f x) }</span></code></pre></div>
|
||
<p>Recap: Was macht eine Lens:</p>
|
||
<div class="sourceCode" id="cb32" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb32-1"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">data</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Adress</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">A</span> {<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> _road ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span></span>
|
||
<span id="cb32-2"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> _city ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span></span>
|
||
<span id="cb32-3"> ,<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> _postcode ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> }</span>
|
||
<span id="cb32-4"></span>
|
||
<span id="cb32-5"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">road ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens'</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Adress</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span></span>
|
||
<span id="cb32-6">road elt_fn (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">A</span> r c p) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> (\r' <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">A</span> r' c p) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><$></span> (elt_fn r)</span>
|
||
<span id="cb32-7"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- | "Hole" | | Thing to put in|</span></span></code></pre></div>
|
||
<p>Wenn man nun road & city gleichzeitig bearbeiten will:</p>
|
||
<div class="sourceCode" id="cb33" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb33-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">addr_strs ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Traversal'</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Address</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span></span>
|
||
<span id="cb33-2">addr_strs elt_fn (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">A</span> r c p)</span>
|
||
<span id="cb33-3"> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">...</span> (\r' c' <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">A</span> r' c' p) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">..</span> (elt_fn r) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">..</span> (elt_fn c) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">..</span></span>
|
||
<span id="cb33-4"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- | function with 2 "Holes"| first Thing | second Thing</span></span></code></pre></div>
|
||
<p>fmap kann nur 1 Loch stopfen, aber nicht mit n Löchern umgehen. Applicative mit<br>
|
||
<*> kann das.<br>
|
||
Somit gibt sich</p>
|
||
<div class="sourceCode" id="cb34" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb34-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">addr_strs ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Traversal'</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Address</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span></span>
|
||
<span id="cb34-2">addr_strs elt_fn (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">A</span> r c p)</span>
|
||
<span id="cb34-3"> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">pure</span> (\r' c' <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">A</span> r' c' p) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><*></span> (elt_fn r) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><*></span> (elt_fn c)</span>
|
||
<span id="cb34-4"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- lift in Appl. | function with 2 "Holes"| first Thing | second Thing</span></span>
|
||
<span id="cb34-5"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- oder kürzer</span></span>
|
||
<span id="cb34-6"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">addr_strs ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Traversal'</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Address</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span></span>
|
||
<span id="cb34-7">addr_strs elt_fn (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">A</span> r c p)</span>
|
||
<span id="cb34-8"> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> (\r' c' <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">A</span> r' c' p) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><$></span> (elt_fn r) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><*></span> (elt_fn c)</span>
|
||
<span id="cb34-9"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- pure x <*> y == x <$> y</span></span></code></pre></div>
|
||
<p>Wie würd eine modify-funktion aussehen?</p>
|
||
<div class="sourceCode" id="cb35" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb35-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">over ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens'</span> s a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> (a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> a) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s</span>
|
||
<span id="cb35-2">over ln f <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> runIdentity <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> ln (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Identity</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> f)</span>
|
||
<span id="cb35-3"></span>
|
||
<span id="cb35-4"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">over ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Traversal'</span> s a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> (a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> a) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s</span>
|
||
<span id="cb35-5">over ln f <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> runIdentity <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> ln (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Identity</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> f)</span></code></pre></div>
|
||
<p>Der Code ist derselbe - nur der Typ ist generischer. Auch die anderen Dinge<br>
|
||
funktioniert diese Erweiterung (für Identity und Const muss man noch ein paar<br>
|
||
dummy-Instanzen schreiben um sie von Functor auf Applicative oder Monad zu heben</p>
|
||
<ul>
|
||
<li>konkret reicht hier die Instanzierung von Monoid). In der Lens-Library ist<br>
|
||
daher meist Monad m statt Functor f gefordert.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="wozu-dienen-die-erweiterungen" class="level2">
|
||
<h2 class="anchored" data-anchor-id="wozu-dienen-die-erweiterungen">Wozu dienen die Erweiterungen?</h2>
|
||
<p>Man kann mit Foci sehr selektiv vorgehen. Auch kann man diese durch Funktionen<br>
|
||
steuern. Beispisweise eine Function anwenden auf</p>
|
||
<ul>
|
||
<li>Jedes 2. Listenelement</li>
|
||
<li>Alle graden Elemente in einem Baum</li>
|
||
<li>Alle Namen in einer Tabelle, deren Gehalt > 10.000€ ist</li>
|
||
</ul>
|
||
<p>Traversals und Lenses kann man trivial kombinieren (<code>lens . lens</code> => <code>lens</code>,<br>
|
||
<code>lens . traversal</code> => <code>traversal</code> etc.)</p>
|
||
</section>
|
||
<section id="wie-es-in-lens-wirklich-aussieht" class="level2">
|
||
<h2 class="anchored" data-anchor-id="wie-es-in-lens-wirklich-aussieht">Wie es in Lens wirklich aussieht</h2>
|
||
<p>In diesem Artikel wurde nur auf Monomorphic Lenses eingegangen. In der richtigen<br>
|
||
Library ist eine Lens</p>
|
||
<div class="sourceCode" id="cb36" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb36-1"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">type</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens'</span> s a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens</span> s s a a</span>
|
||
<span id="cb36-2"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">type</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Lens</span> s t a b <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">forall</span> f<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Functor</span> f <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=></span> (a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> f b) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> (s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> f t)</span></code></pre></div>
|
||
<p>sodass sich auch die Typen ändern können um z.B. automatisch einen Konvertierten<br>
|
||
(sicheren) Typen aus einer unsicheren Datenstruktur zu geben.</p>
|
||
<p>Die modify-Funktion over ist auch</p>
|
||
<div class="sourceCode" id="cb37" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb37-1"><span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">></span><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> over ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Profunctor</span> p <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Setting</span> p s t a b <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> p a b <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> t</span></code></pre></div>
|
||
<blockquote class="blockquote">
|
||
<p><em>Edward is deeply in thrall to abstractionitis</em> - Simon Peyton Jones</p>
|
||
</blockquote>
|
||
<p>Lens alleine definiert 39 newtypes, 34 data-types und 194 Typsynonyme…<br>
|
||
Ausschnitt</p>
|
||
<div class="sourceCode" id="cb38" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb38-1"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- traverseOf :: Functor f => Iso s t a b -> (a -> f b) -> s -> f t</span></span>
|
||
<span id="cb38-2"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- traverseOf :: Functor f => Lens s t a b -> (a -> f b) -> s -> f t</span></span>
|
||
<span id="cb38-3"><span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- traverseOf :: Applicative f => Traversal s t a b -> (a -> f b) -> s -> f t</span></span>
|
||
<span id="cb38-4"></span>
|
||
<span id="cb38-5"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">traverseOf ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Over</span> p f s t a b <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> p a (f b) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> s <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> f t</span></code></pre></div>
|
||
<p>dafuq?</p>
|
||
|
||
|
||
</section>
|
||
|
||
]]></description>
|
||
<category>Article</category>
|
||
<category>Haskell</category>
|
||
<guid>https://drezil.de/Coding/Haskell/Lenses.html</guid>
|
||
<pubDate>Sun, 31 Dec 2017 23:00:00 GMT</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>Monoid? Da war doch was…</title>
|
||
<link>https://drezil.de/Coding/Haskell/Code Snippets/Monoid.html</link>
|
||
<description><![CDATA[
|
||
|
||
|
||
|
||
|
||
|
||
<p>Stellen wir uns vor, dass wir eine Funktion schreiben, die einen String bekommt (mehrere Lines mit ACSII-Text) und dieses Wort-für-Wort rückwärts ausgeben soll. Das ist ein einfacher Einzeiler:</p>
|
||
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb1-1"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">module</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Main</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">where</span></span>
|
||
<span id="cb1-2"></span>
|
||
<span id="cb1-3"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">System.Environment</span> (getArgs)</span>
|
||
<span id="cb1-4"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.Monoid</span> (mconcat)</span>
|
||
<span id="cb1-5"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.Functor</span> ((<$>))</span>
|
||
<span id="cb1-6"></span>
|
||
<span id="cb1-7">main <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">do</span></span>
|
||
<span id="cb1-8"> ls <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"><-</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">readFile</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">=<<</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">head</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><$></span> getArgs</span>
|
||
<span id="cb1-9"> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">mconcat</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><$></span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">mapM</span> (<span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">putStrLn</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">unwords</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">reverse</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">words</span>) (<span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">lines</span> ls) <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">--die eigentliche Funktion, ls ist das argument.</span></span></code></pre></div>
|
||
<p>Was passiert hier an Vodoo? Und was machen die ganzen wilden Zeichen da?</p>
|
||
<p>Gehen wir die Main zeilenweise durch: Wir lesen die Datei, die im ersten Kommandozeilen-Argument gegeben wird. getArgs hat folgende Signatur:</p>
|
||
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb2-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">getArgs ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IO</span> [<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span>]</span></code></pre></div>
|
||
<p>Wir bekommen als eine Liste der Argumente. Wir wollen nur das erste. Also machen wir head getArgs. Allerdings fliegt uns dann ein Fehler. head sieht nämlich so aus:</p>
|
||
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb3-1"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">head</span><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> ::</span> [a] <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> a</span></code></pre></div>
|
||
<p>Irgendwie müssen wird as <strong>in</strong> das IO bekommen. Hierzu gibt es fmap. Somit ist</p>
|
||
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb4-1"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">fmap</span><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> head ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IO</span> [a] <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IO</span> a</span></code></pre></div>
|
||
<p>Ein inline-Alias (um die Funktion links und das Argument rechts zu schreiben und sich ne Menge Klammern zu sparen) ist <$>. Somit ist schlussendlich der Inhalt der Datei aus dem ersten Argument (lazy) in ls.</p>
|
||
<p>Eine andere Möglichkeit sich das (in diesem Fall) zu merken, bzw. drauf zu kommen ist, dass [] AUCH ein Funktor (sogar eine Monade) ist. Man könnte das also auch so schreiben:</p>
|
||
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb5-1"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">head</span><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> ::</span> [] a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> a</span>
|
||
<span id="cb5-2"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">head</span><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Functor</span> f <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=></span> [] (f a) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> f a <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- das "a" geschickt ersetzt zur Verdeutlichung</span></span>
|
||
<span id="cb5-3"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">getArgs ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IO</span> [] <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span></span>
|
||
<span id="cb5-4"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">fmap</span><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> head ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Functor</span> f <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=></span> f [] a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> f a</span></code></pre></div>
|
||
<p>fmap “packt” die Funktion quasi 1 Umgebung (Funktor, Monade, ..) weiter rein - Sei es nun in Maybe, Either oder irgendwas anderes.</p>
|
||
<p>Alternatives (ausführliches) Beispiel am Ende.</p>
|
||
<p>Wenn wir uns die Signatur ansehen, dann haben wir nun</p>
|
||
<div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb6-1"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">head</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><$></span><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> getArgs ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IO</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span></span></code></pre></div>
|
||
<p>readFile will aber nun ein String haben. Man kann nun</p>
|
||
<div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb7-1">f <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"><-</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">head</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><$></span> getArgs</span>
|
||
<span id="cb7-2">ls <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"><-</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">readFile</span> f</span></code></pre></div>
|
||
<p>kann man auch “inline” mit =<< die Sachen “auspacken”.</p>
|
||
<p>Die 2. Zeile lesen wir nun einfach “von hinten”, wie man das meistens tun sollte. Hier ist ein</p>
|
||
<div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb8-1"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">lines</span><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> ls ::</span> [<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span>]</span></code></pre></div>
|
||
<p>was uns den Inhalt der Datei zeilenweise gibt. Mit jeder Zeile möchten wir nun folgendes machen:</p>
|
||
<ol type="1">
|
||
<li>nach Wörtern trennen (words)</li>
|
||
<li>Wörter in der reihenfolge umkehren (reverse)</li>
|
||
<li>Wörter wider zu einer Zeile zusammensetzen (unwords)</li>
|
||
<li>diese Zeile ausgeben (putStrLn)</li>
|
||
</ol>
|
||
<p>Wenn wir uns die Signatur ansehen:</p>
|
||
<div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb9-1">(<span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">putStrLn</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">unwords</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">reverse</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">words</span>)<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IO</span> ()</span></code></pre></div>
|
||
<p>Das mag im ersten Moment verwirren, daher noch die Signaturen der Einzelfunktionen:</p>
|
||
<div class="sourceCode" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb10-1"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">words</span><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> [<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span>]</span>
|
||
<span id="cb10-2"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">reverse</span><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> ::</span> [a] <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> [a]</span>
|
||
<span id="cb10-3"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">unwords</span><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> ::</span> [<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span>] <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span></span>
|
||
<span id="cb10-4"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">putStrLn</span><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IO</span> ()</span></code></pre></div>
|
||
<p>Da wir am Ende in der IO-Monade landen müssen wir das auf unsere Zeilen mit mapM statt map anwenden. Dies sorgt auch dafür, dass die Liste der reihe nach durchgegangen wird. mapM mit unserer Funktion schaut dann so aus:</p>
|
||
<div class="sourceCode" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb11-1"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">mapM</span> (<span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">putStrLn</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">unwords</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">reverse</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">words</span>)<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> ::</span> [<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">String</span>] <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> [<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IO</span> ()]</span></code></pre></div>
|
||
<p>eek! Das [IO ()] sieht ekelig aus. Wir haben eine Liste von IO-gar nichts. Das können wir eigentlich entsorgen. Da wir innerhalb der main-Funktion in einer IO-Monade sind, wollen wir IO () anstatt [IO ()] zurück haben.</p>
|
||
<p>Wenn wir uns jetzt erinnern, dass [] auch nur eine Monade ist und dass jede Monade ein Monoid ist, dann ist die Lösung einfach. Monoide haben eine “append”-funktion (mappend oder (<>) genannt). Wenn wir “nichts” an “nichts” anhängen, dann erhalten wir …. <em>Trommelwirbel</em> “nichts”! Wir müssen die [IO ()]-Liste also “nur noch” mit mappend falten. Hierzu gibt es schon eine vorgefertigte Funktion:</p>
|
||
<div class="sourceCode" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb12-1"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">mconcat</span><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> ::</span> [a] <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> a</span>
|
||
<span id="cb12-2"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">mconcat</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">foldr</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">mappend</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">mempty</span></span></code></pre></div>
|
||
<p>Was genau die gewünschte Faltung macht. Wir müssen nun wieder fmap nehmen, da wir die Liste selbst falten wollen - und nicht map, welches auf den IO () innerhalb der Liste arbeiten würde. Durch die Faltung fällt die Liste nun auf IO () zusammen.</p>
|
||
<p>Viel Voodoo in wenig Code, aber wenn man sich dran gewöhnt hat, sind Monaden in Monaden auch nicht schlimm. Man muss sich immer nur richtig “rein” fmap’en.</p>
|
||
<hr>
|
||
<p>Kleinen Tipp gab es noch: mapM_ macht genau das, was oben mit mconcat erreicht werden sollte. Somit kann man auch</p>
|
||
<div class="sourceCode" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb13-1"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">mapM_</span> (<span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">putStrLn</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">unwords</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">reverse</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">.</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">words</span>) (<span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">lines</span> ls)</span></code></pre></div>
|
||
<p>schreiben. Ich hab es aber mal wegen der klarheit oben so gelassen.</p>
|
||
<section id="alternatives-fmap-beispiel" class="level2">
|
||
<h2 class="anchored" data-anchor-id="alternatives-fmap-beispiel">Alternatives fmap-Beispiel</h2>
|
||
<p>Nehmen wir als alternatives Beispiel mal an:</p>
|
||
<div class="sourceCode" id="cb14" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb14-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">a ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IO</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Maybe</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">State</span> t</span></code></pre></div>
|
||
<p>Um Funktionen vom Typ</p>
|
||
<div class="sourceCode" id="cb15" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb15-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">f ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IO</span> a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">IO</span> a</span>
|
||
<span id="cb15-2">f a <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- valide</span></span></code></pre></div>
|
||
<p>zu nehmen, brauchen wir nichts machen. Bei</p>
|
||
<div class="sourceCode" id="cb16" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb16-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">f' ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Maybe</span> a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Maybe</span> a</span></code></pre></div>
|
||
<p>brauchen wir 1 fmap, also ein</p>
|
||
<div class="sourceCode" id="cb17" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb17-1">f' a <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- error</span></span>
|
||
<span id="cb17-2">f' <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><$></span> a</span></code></pre></div>
|
||
<p>um eine Funktion</p>
|
||
<div class="sourceCode" id="cb18" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb18-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">f'' ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">State</span> t <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">State</span> t</span></code></pre></div>
|
||
<p>zu benutzen folglich:</p>
|
||
<div class="sourceCode" id="cb19" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb19-1">f'' a <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- error</span></span>
|
||
<span id="cb19-2">f'' <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><$></span> a <span class="co" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">-- error</span></span>
|
||
<span id="cb19-3"><span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">fmap</span> f'' <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><$></span> a</span></code></pre></div>
|
||
|
||
|
||
</section>
|
||
|
||
]]></description>
|
||
<category>Haskell</category>
|
||
<category>Tutorial</category>
|
||
<guid>https://drezil.de/Coding/Haskell/Code Snippets/Monoid.html</guid>
|
||
<pubDate>Thu, 31 Dec 2015 23:00:00 GMT</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>*-Morpisms</title>
|
||
<link>https://drezil.de/Coding/Haskell/Code Snippets/Morphisms.html</link>
|
||
<description><![CDATA[
|
||
|
||
|
||
|
||
|
||
|
||
<div class="callout callout-style-default callout-note callout-titled">
|
||
<div class="callout-header d-flex align-content-center">
|
||
<div class="callout-icon-container">
|
||
<i class="callout-icon"></i>
|
||
</div>
|
||
<div class="callout-title-container flex-fill">
|
||
Note
|
||
</div>
|
||
</div>
|
||
<div class="callout-body-container callout-body">
|
||
<p>Backup eines Blogposts eines Kommilitonen</p>
|
||
</div>
|
||
</div>
|
||
<p>This weekend I spend some time on Morphisms.</p>
|
||
<p>Knowing that this might sound daunting to many dabbling Haskellers (like I am), I decided to write a real short MergeSort hylomorphism quickstarter.</p>
|
||
<hr>
|
||
<p>For those who need a refresher: MergeSort works by creating a balanced binary tree from the input list and directly collapsing it back into itself while treating the children as sorted lists and merging these with an O(n) algorithm.</p>
|
||
<hr>
|
||
<p>First the usual prelude:</p>
|
||
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb1-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE DeriveFunctor #-}</span></span>
|
||
<span id="cb1-2"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">{-# LANGUAGE TypeFamilies #-}</span></span>
|
||
<span id="cb1-3"></span>
|
||
<span id="cb1-4"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.Functor.Foldable</span></span>
|
||
<span id="cb1-5"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">import</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Data.List</span> (splitAt, unfoldr)</span></code></pre></div>
|
||
<hr>
|
||
<p>We will use a binary tree like this. Note that there is no explicit recursion used, but <code>NodeF</code> has two <em>holes</em>. These will eventually filled later.</p>
|
||
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb2-1"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">data</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">TreeF</span> c f <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">EmptyF</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">|</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LeafF</span> c <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">|</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">NodeF</span> f f</span>
|
||
<span id="cb2-2"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">deriving</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Eq</span>, <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Show</span>, <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Functor</span>)</span></code></pre></div>
|
||
<hr>
|
||
<p>Aside: We could use this as a <em>normal</em> binary tree by wrapping it in <code>Fix</code>: <code>type Tree a = Fix (TreeF a)</code> But this would require us to write our tree like <code>Fix (NodeF (Fix (LeafF 'l')) (Fix (LeafF 'r')))</code> which would get tedious fast. Luckily Edward build a much better way to do this into <em>recursion-schemes</em>. I will touch on this later.</p>
|
||
<hr>
|
||
<p>Without further ado we start to write a Coalgebra, which in my book is just a scary name for “function that is used to construct datastructures”.</p>
|
||
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb3-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">unflatten ::</span> [a] <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">TreeF</span> a [a]</span>
|
||
<span id="cb3-2">unflatten ( []) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">EmptyF</span></span>
|
||
<span id="cb3-3">unflatten (x<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">:</span>[]) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LeafF</span> x</span>
|
||
<span id="cb3-4">unflatten ( xs) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">NodeF</span> l r <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">where</span> (l,r) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">splitAt</span> (<span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">length</span> xs <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">`div`</span> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">2</span>) xs</span></code></pre></div>
|
||
<p>From the type signature it’s immediately obvious, that we take a list of ’a’s and use it to create a part of our tree.</p>
|
||
<p>The nice thing is that due to the fact that we haven’t commited to a type in our tree nodes we can just put lists in there.</p>
|
||
<hr>
|
||
<p>Aside: At this point we could use this Coalgebra to construct (unsorted) binary trees from lists:</p>
|
||
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb4-1">example1 <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> ana unflatten [<span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">1</span>,<span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">3</span>] <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">==</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Fix</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">NodeF</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Fix</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LeafF</span> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">1</span>)) (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Fix</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LeafF</span> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">3</span>)))</span></code></pre></div>
|
||
<hr>
|
||
<p>On to our sorting, tree-collapsing Algebra. Which again is just a creepy word for “function that is used to deconstruct datastructures”.</p>
|
||
<p>The function <code>mergeList</code> is defined below and just merges two sorted lists into one sorted list in O(n), I would probably take this from the <code>ordlist</code> package if I were to implement this <em>for real</em>.</p>
|
||
<p>Again we see that we can just construct our sorted output list from a <code>TreeF</code> that apparently contains just lists.</p>
|
||
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb5-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">flatten ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Ord</span> a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=></span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">TreeF</span> a [a] <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> [a]</span>
|
||
<span id="cb5-2">flatten <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">EmptyF</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> []</span>
|
||
<span id="cb5-3">flatten (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LeafF</span> c) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> [c]</span>
|
||
<span id="cb5-4">flatten (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">NodeF</span> l r) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> mergeLists l r</span></code></pre></div>
|
||
<hr>
|
||
<p>Aside: We could use a Coalgebra to deconstruct trees:</p>
|
||
<div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb6-1">example2 <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> cata flatten (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Fix</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">NodeF</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Fix</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LeafF</span> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">3</span>)) (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Fix</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LeafF</span> <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">1</span>)))) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">==</span> [<span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">1</span>,<span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">3</span>]</span></code></pre></div>
|
||
<hr>
|
||
<p>Now we just combine the Coalgebra and the Algebra with one from the functions from Edwards <code>recursion-schemes</code> library:</p>
|
||
<div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb7-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">mergeSort ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Ord</span> a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=></span> [a] <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> [a]</span>
|
||
<span id="cb7-2">mergeSort <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> hylo flatten unflatten</span>
|
||
<span id="cb7-3"></span>
|
||
<span id="cb7-4">example3 <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> mergeSort [<span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">5</span>,<span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">2</span>,<span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">7</span>,<span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">9</span>,<span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">1</span>,<span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">4</span>] <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">==</span> [<span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">1</span>,<span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">2</span>,<span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">4</span>,<span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">5</span>,<span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">7</span>,<span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">9</span>]</span></code></pre></div>
|
||
<hr>
|
||
<p>What have we gained?</p>
|
||
<p>We have implemented a MergeSort variant in 9 lines of code, not counting the <code>mergeLists</code> function below. Not bad, but <a href="http://en.literateprograms.org/Merge_sort_(Haskell)">this implementation</a> is not much longer.</p>
|
||
<p>On the other hand the morphism based implementation cleanly describes what happens during construction and deconstruction of our intermediate structure.</p>
|
||
<p>My guess is that, as soon as the algortihms get more complex, this will really make a difference.</p>
|
||
<hr>
|
||
<p>At this point I wasn’t sure if this was useful or remotely applicable. Telling someone “I spend a whole weekend learning about Hylomorphism” isn’t something the cool developer kids do.</p>
|
||
<p>It appeared to me that maybe I should have a look at the Core to see what the compiler finally comes up with (edited for brevity):</p>
|
||
<div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb8-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> mergeSort ::</span> [<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Integer</span>] <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> [<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Integer</span>]</span>
|
||
<span id="cb8-2"> mergeSort <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span></span>
|
||
<span id="cb8-3"> \ (<span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">x ::</span> [<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Integer</span>]) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span></span>
|
||
<span id="cb8-4"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">case</span> x <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">of</span> wild {</span>
|
||
<span id="cb8-5"> [] <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> [];</span>
|
||
<span id="cb8-6"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">:</span> x1 ds <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span></span>
|
||
<span id="cb8-7"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">case</span> ds <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">of</span> _ {</span>
|
||
<span id="cb8-8"> [] <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">:</span> x1 ([]);</span>
|
||
<span id="cb8-9"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">:</span> ipv ipv1 <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span></span>
|
||
<span id="cb8-10"> unfoldr</span>
|
||
<span id="cb8-11"> lvl9</span>
|
||
<span id="cb8-12"> (<span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">let</span> {</span>
|
||
<span id="cb8-13"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;"> p ::</span> ([<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Integer</span>], [<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Integer</span>])</span>
|
||
<span id="cb8-14"> p <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span></span>
|
||
<span id="cb8-15"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">case</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span>wlenAcc wild <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">0</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">of</span> ww { __DEFAULT <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span></span>
|
||
<span id="cb8-16"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">case</span> divInt<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">#</span> ww <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">2</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">of</span> ww4 { __DEFAULT <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span></span>
|
||
<span id="cb8-17"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">case</span> tagToEnum<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">#</span> (<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><#</span> ww4 <span class="dv" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">0</span>) <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">of</span> _ {</span>
|
||
<span id="cb8-18"> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">False</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span></span>
|
||
<span id="cb8-19"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">case</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span>wsplitAt<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">#</span> ww4 wild <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">of</span> _ { (<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">#</span> ww2, ww3 <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">#</span>) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> (ww2, ww3) };</span>
|
||
<span id="cb8-20"> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">True</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> ([], wild)</span>
|
||
<span id="cb8-21"> }</span>
|
||
<span id="cb8-22"> }</span>
|
||
<span id="cb8-23"> } } <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">in</span></span>
|
||
<span id="cb8-24"> (<span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">case</span> p <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">of</span> _ { (x2, ds1) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> mergeSort x2 },</span>
|
||
<span id="cb8-25"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">case</span> p <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">of</span> _ { (ds1, y) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> mergeSort y }))</span>
|
||
<span id="cb8-26"> }</span>
|
||
<span id="cb8-27"> }</span>
|
||
<span id="cb8-28"> end <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Rec</span> }</span></code></pre></div>
|
||
<p>While I am not really competent in reading Core and this is actually the first time I bothered to try, it is immediately obvious that there is no trace of any intermediate tree structure.</p>
|
||
<p>This is when it struck me. I was dazzled and amazed. And am still. Although we are writing our algorithm as if we are working on a real tree structure the library and the compiler are able to just remove the whole intermediate step.</p>
|
||
<hr>
|
||
<p>Aftermath:</p>
|
||
<p>In the beginning I promised a way to work on non-functor data structures. Actually that was how I began to work with the <code>recursion-schemes</code> library.</p>
|
||
<p>We are able to create a ‘normal’ version of our tree from above:</p>
|
||
<div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb9-1"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">data</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Tree</span> c <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Empty</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">|</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Leaf</span> c <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">|</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Node</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Tree</span> c) (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Tree</span> c)</span>
|
||
<span id="cb9-2"> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">deriving</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Eq</span>, <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Show</span>)</span></code></pre></div>
|
||
<p>But we can not use this directly with our (Co-)Algebras. Luckily Edward build a little bit of type magic into the library:</p>
|
||
<div class="sourceCode" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb10-1"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">type</span> <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">instance</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Base</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Tree</span> c) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">TreeF</span> c)</span>
|
||
<span id="cb10-2"></span>
|
||
<span id="cb10-3"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">instance</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Unfoldable</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Tree</span> c) <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">where</span></span>
|
||
<span id="cb10-4"> embed <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">EmptyF</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Empty</span></span>
|
||
<span id="cb10-5"> embed (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LeafF</span> c) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Leaf</span> c</span>
|
||
<span id="cb10-6"> embed (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">NodeF</span> l r) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Node</span> l r</span>
|
||
<span id="cb10-7"></span>
|
||
<span id="cb10-8"><span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">instance</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Foldable</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Tree</span> c) <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">where</span></span>
|
||
<span id="cb10-9"> project <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Empty</span> <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">EmptyF</span></span>
|
||
<span id="cb10-10"> project (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Leaf</span> c) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">LeafF</span> c</span>
|
||
<span id="cb10-11"> project (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Node</span> l r) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">NodeF</span> l r</span></code></pre></div>
|
||
<p>Without going into detail by doing this we establish a relationship between <code>Tree</code> and <code>TreeF</code> and teach the compiler how to translate between these types.</p>
|
||
<p>Now we can use our Alebra on our non functor type:</p>
|
||
<div class="sourceCode" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb11-1">example4 <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> cata flatten (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Node</span> (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Leaf</span> <span class="ch" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">'l'</span>) (<span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Leaf</span> <span class="ch" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">'r'</span>)) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
|
||
background-color: null;
|
||
font-style: inherit;">"lr"</span></span></code></pre></div>
|
||
<p>The great thing about this is that, looking at the Core output again, there is no traces of the <code>TreeF</code> structure to be found. As far as I can tell, the algorithm is working directly on our <code>Tree</code> type.</p>
|
||
<hr>
|
||
<p>Literature:</p>
|
||
<ul>
|
||
<li><a href="https://www.fpcomplete.com/user/bartosz/understanding-algebras">Understanding F-Algebras</a></li>
|
||
<li><a href="http://www.timphilipwilliams.com/slides.html">Recursion Schemes by Example</a></li>
|
||
<li><a href="http://comonad.com/reader/2009/recursion-schemes/">Recursion Schemes: A Field Guide</a></li>
|
||
<li><a href="http://stackoverflow.com/questions/6941904/recursion-schemes-for-dummies">This StackOverflow question</a></li>
|
||
</ul>
|
||
<hr>
|
||
<p>Appendix:</p>
|
||
<div class="sourceCode" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb12-1"><span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">mergeLists ::</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Ord</span> a <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=></span> [a] <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> [a] <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">-></span> [a]</span>
|
||
<span id="cb12-2">mergeLists <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
|
||
background-color: null;
|
||
font-style: inherit;">curry</span> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">$</span> unfoldr c <span class="kw" style="color: #003B4F;
|
||
background-color: null;
|
||
font-weight: bold;
|
||
font-style: inherit;">where</span></span>
|
||
<span id="cb12-3"> c ([], []) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Nothing</span></span>
|
||
<span id="cb12-4"> c ([], y<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">:</span>ys) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Just</span> (y, ([], ys))</span>
|
||
<span id="cb12-5"> c (x<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">:</span>xs, []) <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Just</span> (x, (xs, []))</span>
|
||
<span id="cb12-6"> c (x<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">:</span>xs, y<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">:</span>ys) <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">|</span> x <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;"><=</span> y <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Just</span> (x, (xs, y<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">:</span>ys))</span>
|
||
<span id="cb12-7"> <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">|</span> x <span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">></span> y <span class="ot" style="color: #003B4F;
|
||
background-color: null;
|
||
font-style: inherit;">=</span> <span class="dt" style="color: #AD0000;
|
||
background-color: null;
|
||
font-style: inherit;">Just</span> (y, (x<span class="op" style="color: #5E5E5E;
|
||
background-color: null;
|
||
font-style: inherit;">:</span>xs, ys))</span></code></pre></div>
|
||
|
||
|
||
|
||
]]></description>
|
||
<category>Haskell</category>
|
||
<category>Tutorial</category>
|
||
<category>Archived</category>
|
||
<guid>https://drezil.de/Coding/Haskell/Code Snippets/Morphisms.html</guid>
|
||
<pubDate>Thu, 31 Dec 2015 23:00:00 GMT</pubDate>
|
||
</item>
|
||
<item>
|
||
<title></title>
|
||
<link>https://drezil.de/Coding/Haskell/Advantages.html</link>
|
||
<description><![CDATA[
|
||
|
||
|
||
|
||
|
||
|
||
<section id="talks-und-posts-zu-haskell" class="level1">
|
||
<h1>Talks und Posts zu Haskell</h1>
|
||
<p>Gründe Haskell zu nutzen und wo Vorteile liegen.</p>
|
||
<section id="talks" class="level2">
|
||
<h2 class="anchored" data-anchor-id="talks">Talks</h2>
|
||
<ul>
|
||
<li><a href="https://www.youtube.com/watch?v=hlyQjK1qjw8">The Future is parallel</a></li>
|
||
<li><a href="https://skillsmatter.com/skillscasts/4251-lenses-compositional-data-access-and-manipulation">Lenses</a> (Registrierung nötig - kostenfrei), siehe auch: <a href="../../Coding/Haskell/Lenses.html">Lenses</a></li>
|
||
<li><a href="https://www.youtube.com/watch?v=ZR3Jirqk6W8">Running a Startup on Haskell</a></li>
|
||
<li><a href="https://www.youtube.com/watch?v=TS1lpKBMkgg">We’re doing it all wrong</a> - A Long-Term Scala-Compiler-Developer quits his job after years and tells why Scala is a mess.</li>
|
||
<li><a href="https://www.youtube.com/watch?v=b0EF0VTs9Dc">Monads explained in Javascript</a></li>
|
||
<li><a href="http://vimeo.com/95694918">Vinyl Records</a> with <a href="https://github.com/VinylRecords/BayHac2014-Talk">Slides</a></li>
|
||
<li><a href="http://begriffs.com/posts/2015-06-17-thinking-with-laziness.html">Thinking with Laziness</a></li>
|
||
</ul>
|
||
</section>
|
||
<section id="bücherpaper" class="level2">
|
||
<h2 class="anchored" data-anchor-id="bücherpaper">Bücher/Paper</h2>
|
||
<ul>
|
||
<li><a href="https://research.microsoft.com/en-us/um/people/simonpj/papers/stm/">Papers on STM</a></li>
|
||
<li><a href="https://research.microsoft.com/en-us/um/people/simonpj/papers/marktoberdorf/">Tackling the awkward squad</a></li>
|
||
<li><a href="http://chimera.labs.oreilly.com/books/1230000000929/pr01.html">Parallel and Concurrent Programming in Haskell</a></li>
|
||
<li><a href="http://scholar.google.de/scholar?cluster=7602244452224287116&hl=de&as_sdt=0,5">Slides of a Quickcheck-Talk</a></li>
|
||
<li><a href="https://www.fpcomplete.com/user/bartosz/understanding-algebras">Understanding F-Algebras</a> schöne Erklärung. Man könnte danach anfangen den <a href="../../Coding/Haskell/Code Snippets/Morphisms.html">Morphismen-zoo</a> zu verstehen…</li>
|
||
<li><a href="https://github.com/kqr/gists/blob/master/articles/gentle-introduction-monad-transformers.md">Monad Transformers</a></li>
|
||
</ul>
|
||
</section>
|
||
<section id="funny-talks" class="level2">
|
||
<h2 class="anchored" data-anchor-id="funny-talks">Funny Talks</h2>
|
||
<ul>
|
||
<li><a href="https://www.youtube.com/watch?v=o6L6XeNdd_k">Tom LaGatta on Category-Theory</a></li>
|
||
<li><a href="https://www.youtube.com/watch?v=9EGYSb9vov8">Unifying Structured Recursion Schemes</a> aka. [[Morphisms|The Morphism-Zoo]]</li>
|
||
<li><a href="https://www.youtube.com/watch?v=IRGKkiGG5CY">Hole-Driven-Development Teaser (Enthusiasticon, raichoo)</a></li>
|
||
</ul>
|
||
</section>
|
||
<section id="unsortedunseen" class="level2">
|
||
<h2 class="anchored" data-anchor-id="unsortedunseen">Unsorted/Unseen</h2>
|
||
<ul>
|
||
<li><a href="http://insights.pwning.de/_edit/Haskell/Why_Haskell_is_superior">Functional Reactive Programming</a></li>
|
||
<li><a href="http://vimeo.com/84104226">Diagrams: Declarative Vector Graphics in Haskell</a></li>
|
||
<li><a href="https://www.youtube.com/watch?v=cefnmjtAolY">Lenses, Folds & Traversels by Edward Kmett</a></li>
|
||
<li><a href="https://www.youtube.com/watch?v=cB8DapKQz-I&list=WL&index=10">Discrimination is Wrong by Edward Kmett</a></li>
|
||
</ul>
|
||
</section>
|
||
<section id="tutorials" class="level2">
|
||
<h2 class="anchored" data-anchor-id="tutorials">Tutorials</h2>
|
||
<ul>
|
||
<li><a href="https://www.fpcomplete.com/school/starting-with-haskell/haskell-fast-hard/haskell-fast-hard-part-1">Haskell fast and hard</a></li>
|
||
<li><a href="http://blog.functorial.com/posts/2015-12-06-Counterexamples.html">Counterexamples for Typeclasses</a></li>
|
||
</ul>
|
||
|
||
|
||
</section>
|
||
</section>
|
||
|
||
]]></description>
|
||
<category>Haskell</category>
|
||
<category>Links</category>
|
||
<guid>https://drezil.de/Coding/Haskell/Advantages.html</guid>
|
||
<pubDate>Wed, 30 Sep 2015 22:00:00 GMT</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>Wie lerne ich richtig an der Uni?</title>
|
||
<link>https://drezil.de/Uni/Lernerfolg_an_der_Uni.html</link>
|
||
<description><![CDATA[
|
||
|
||
|
||
|
||
|
||
|
||
<p>Dies ist eine gute Frage. Da ich im laufe der Zeit einige Antworten gesammelt habe, wollte ich diese mal hier niederschreiben. Vorweg eine Warnung: <strong>All das hier spiegelt nur meine persönlichen Erfahrungen aus Gesprächen wieder. Es kann sein, dass die z.B. für euren Fachbereich nicht gilt.</strong> Da wir das nun aus dem Weg haben, geht es auch gleich los.</p>
|
||
<section id="uni-ist-nicht-schule" class="level2">
|
||
<h2 class="anchored" data-anchor-id="uni-ist-nicht-schule">Uni ist nicht Schule</h2>
|
||
<p>Einige mögen sagen: “duh!”, aber es ist erschreckend, wie viele Leute meinen, dass ihnen die Uni etwas schuldet oder das Dozenten und Tutoren dafür verantwortlich sind, dass man hier etwas lernt. Studium ist eine komplett freiwillige Veranstaltung. Man kann jederzeit sagen: “Passt mir nicht. Ich gehe.” An der Uni wird erwartet, dass man sich ggf. einarbeitet, wenn man etwas nicht weiss; dass man Sekundärliteratur fragt (z.B. in Mathe auch mal in Bücher schaut um eine andere Erklärung zu bekommen, als der Prof an die Tafel geklatscht hat).</p>
|
||
</section>
|
||
<section id="etwas-lerntheorie" class="level2">
|
||
<h2 class="anchored" data-anchor-id="etwas-lerntheorie">Etwas Lerntheorie</h2>
|
||
<p>Es gibt einen sehr schönen <a href="https://www.youtube.com/watch?v=Z8KcCU-p8QA">Talk</a> von Edwand Kmett in dem er über seine Erfahrungen berichtet. Kurzum: Man lernt durch stete Wiederholung. Und der beste Moment etwas zu wiederholen ist, kurz bevor man es vergisst. Das stimmt ziemlich genau mit meiner Erfahrung überein.</p>
|
||
<section id="auswendig-lernen" class="level3">
|
||
<h3 class="anchored" data-anchor-id="auswendig-lernen">Auswendig lernen</h3>
|
||
<p>Grade die oben genannte Theorie steht beim Auswendiglernen im Vordergrund. Wenn man etwas langfristig auswendig lernen will (Fremdsprachen, etc.), dann gibt es hierzu Software, die herausfindet, wann es der beste Zeitpunkt ist, dich wieder abzufragen: <a href="http://ankisrs.net/">Anki</a> gibt es für jede Platform kostenlos (außer iPhone - hier 25$, weil Apple so viel Geld für das einstellen im AppStore haben will). Anki ist dazu gedacht, dass man zu jedem Thema einen Stapel hat (z.b. Klausurfragen, Sprachen, …) und jeden Tag lernt. Nach einiger Zeit wird die vorhersage der Lernzeit ziemlich genau. Anfangs beantwortet man noch viele Fragen täglich, aber je häufiger man die Antworten kennt, desto weiter hinten landen sie im Stapel. Schlussendlich kommt dieselbe Frage dann nur noch 1x/Monat oder noch seltener.</p>
|
||
<p>Ich benutze dies insbesondere zum Auswendiglernen von Fakten, Formeln, Fachbegriffen etc. Bei Mathe bietet sich zum Beispiel an einen Stapel mit allen Definitionen zu haben; in der Biologie eine Liste der Schema und Kreisläufe etc.</p>
|
||
<p>Man kann auch einen Hardcore-Lernmarathon machen. Meine letzten beiden Klausuren waren nur auf “bestehen” - also ohne Note. Ich habe mir eine alte Klausur organisiert (mehr genaues unten) und dann daraus Karten erstellt. Dies hat nur wenige Stunden gedauert (2-3 verteilt auf 2 Tage). Damit habe ich dann am Tag vor der Klausur 2x gelernt (1x nach dem Aufstehen, 1x vorm schlafengehen; jeweils nach 30 Minuten hatte ich alle Fragen min. 1x korrekt beantwortet). Am Morgen der Klausur hab ich die Fragen vor dem Aufstehen noch einmal durchgemacht (wieder 25-30 min), habe mir zur Klausur fertig gemacht und bin 30 Min vor der Klausur die Fragen nochmals durchgegangen (15-30 min), aber konnte sie mittlerweile alle auswendig. Insgesamt habe ich mit Anki so für die Klausur effektiv 2h gelernt (+2-3h für das erstellen der Karten), habe die Klausur geschrieben und mit einer 3.0 bestanden (also wäre 3.0 gewesen, wenn es nicht unbenotet gewesen wäre). Kommilitonen, die sich (nach eigener Aussage) 1-2 Wochen auf die Klausur vorbereitet haben und eine Note wollten, schnitten teilweise schlechter ab (viele aber auch viel besser).</p>
|
||
</section>
|
||
<section id="methodik-lernen" class="level3">
|
||
<h3 class="anchored" data-anchor-id="methodik-lernen">Methodik lernen</h3>
|
||
<p>Im Gegensatz zum plumpen auswendig lernen gibt es dann auch Anforderungen, wo es darum geht Methoden und Anwendungen zu verstehen. Inbesondere ist dies in Vorbereitung auf z.B. mündliche Prüfungen der Fall. Hier steht eher die Theorie im Vordergrund.</p>
|
||
<p>Um solche Konzepte zu verstehen braucht es leider Zeit. Hier hilft kein 48h-Lernmarathon um das “mal eben” auf die Kette zu kriegen. Am besten bereitet man sich das gesamte Semester über vor (haha! Als ob! :p). Das “Geheimnis” hier liegt in einer Kombination der Ansätze. Zum einen muss man natürlich verstehen, worum es geht. Hier hilft es Definitionen und Fachbegriffe z.B. mit Anki zu lernen. Allerdings muss man sich zusätzlich noch nach jeder(!) Vorlesung hinsetzen und versuchen den Inhalt zu verdauen. Dies können nur 10 Minuten sein oder auch 2h. Hier kommen dann Dinge zum Tragen, wie Sekundärliteratur, Wikipedia, Google, … Man muss die Zusammenhänge einmal verstehen - da kommt man nicht drumherum. ABER: Unser Gehirn arbeitet Assoziativ. Zusammenhänge sind meist logisch oder krass widersprüchlich. Hieraus kann man dann z.B. “Stichwortketten” bauen, von denen man nur das erste auswendig lernt und von da aus sich an den Rest “erinnert”.</p>
|
||
<p>Kleines Beispiel aus der Welt der Mathematik:</p>
|
||
<pre class="plain"><code>Vektorraum -> Ist zu einer Basis definiert
|
||
-> Basis ist die größtmögliche Zahl lin. unabh. Vektoren. Lin. Hülle der Basis ist der VR
|
||
-> Lin. Hülle ist jede Lin.-Komb. von Vektoren
|
||
-> Hat eine Vektoraddition und skalare Multiplikation
|
||
-> Wird über einem Körper aufgespannt
|
||
-> Körper sind 2 abelsche Gruppen mit Distributivgesetz
|
||
-> abelsche Gruppe ist Menge mit K.A.I.N.
|
||
-> ....</code></pre>
|
||
<p>So kann man sich über 5-6 Stichwörter fast am gesamten Stoff der Vorlesung entlanghangeln und merkt schnell, wo es hakt. Hier kann man dann nochmal gezielt nachhaken. Auch kann man bei so einer Struktur aus jedem “a -> b -> c” Anki-Karten machen mit “a” auf der Vorderseite, “b” auf der Rückseite bzw. “b” auf der Vorderseite und “c” auf der Rückseite und so gezielt diese “Ketten” trainieren. Grade in einer mündlichen Prüfung hangeln sich Prüfer ebenfalls an diesen Ketten entlang.</p>
|
||
</section>
|
||
</section>
|
||
<section id="vorbereiten-auf-eine-klausur" class="level2">
|
||
<h2 class="anchored" data-anchor-id="vorbereiten-auf-eine-klausur">Vorbereiten auf eine Klausur</h2>
|
||
<ul>
|
||
<li>Herausfinden, um was für eine Art von Klausur es sich handelt
|
||
<ul>
|
||
<li>Ankreuzklausur?</li>
|
||
<li>Auswendiglern-Klausur?</li>
|
||
<li>Praktische Klausur (z.b. fast 1:1 Übungsaufgaben, feste Schema, ..)?</li>
|
||
<li>Open-Book?</li>
|
||
<li>Annotation von Grafiken?</li>
|
||
</ul></li>
|
||
<li>Klausuren von der Fachschaft organisieren
|
||
<ul>
|
||
<li>Falls keine Vorhanden: Altfachschaftler fragen, wie die Klausur bei ihnen war</li>
|
||
<li>Neue Klausur mit in die FS bringen, falls möglich (z.b. schreiend rausrennen und Klausur dabei mitnehmen, bevor man offiziell registriert wurde)</li>
|
||
</ul></li>
|
||
</ul>
|
||
<p>Je nach Klausurtyp dann mit Anki stumpf Karten machen und auswendig lernen (z.b. Ankreuzklausur, Grafik-annotations-Klausur, ..) oder Übungsaufgaben/Altklausuren durchrechnen</p>
|
||
</section>
|
||
<section id="vorbereiten-auf-eine-mündliche-prüfung" class="level2">
|
||
<h2 class="anchored" data-anchor-id="vorbereiten-auf-eine-mündliche-prüfung">Vorbereiten auf eine mündliche Prüfung</h2>
|
||
<ul>
|
||
<li>Protokolle aus der Fachschaft organisieren
|
||
<ul>
|
||
<li>Häufig gegen Pfand, dass man bei Abgabe eines Protokolls wieder bekommt</li>
|
||
<li>Wenn keins vorhanden für die nachfolgede Generation eins ausfüllen</li>
|
||
</ul></li>
|
||
</ul>
|
||
<p>Wenn ihr einen Reihe von Protokollen vorliegen habt, dann schreibt alle Fragen heraus und notiert, wie häufig diese Frage gestellt wurde. So findet ihr heraus, auf welche Punkte der Prüfer besonders Wert legt (z.B. häufig sein eigenes Forschungsfeld). Diese Fragen dann restlos klären und zu Anki-Karten verarbeiten. Das reicht meistens für ein Bestehen. Wenn ihr auf eine gute Note wert legt, dann solltet ihr auch noch die Vorlesung, wie im Bereich “Methodik lernen” erwähnt, nacharbeiten. Insbesondere helfen hier die Assoziationsketten weiter den Stoff auch in der Prüfung in der richtigen Reihenfolge abzurufen. Vielleicht erkennt ihr solche Ketten schon aus den Prüfungsprotokollen und könnt euch ausmalen, wie man z.b. von da aus auf andere Themen der Vorlesung kommt (die z.b. neu sind oder überarbeitet wurden).</p>
|
||
<section id="unterschiede-mündliche-bachelormaster-prüfungen" class="level3">
|
||
<h3 class="anchored" data-anchor-id="unterschiede-mündliche-bachelormaster-prüfungen">Unterschiede mündliche Bachelor/Master-Prüfungen</h3>
|
||
<p>Einige Dozenten machen unterschiedliche Anforderungen, ob sie einen Bachelor oder einen Master-Studenten prüfen. Abgesehen von der anderen Prüfungszeit (15-30min bei bachelor, 25-45 bei Master) ist hier auch das Vorgehen anders. Bei einem Bachelor wird klassischerweise alles oberflächlich abgefragt und nur wenig in die Tiefe gegangen. Bei einem Master wir nur noch stichpunktartig gefragt, dafür aber bis ins Detail.</p>
|
||
<p>Beispiel: Ich hatte eine mündliche Masterprüfung, bei der in der Vorlesung 7 verschiedene Themen behandelt wurden. In der Prüfung wurden dann nur die Themenübersicht abgefragt und bei 2 Themen komplett in die Tiefe gegangen - inkl. Formeln, Bedeutung, Übertragung auf in der Vorlesung nicht angesprochene Aspekte etc. Die anderen 5 Themen kamen nicht dran. Bei meinen Bachelorprüfungen war das eher umgekehrt: Hier wurde sich grob an der Vorlesung entlang gehangelt und zumindest alles einmal kurz angetestet, ob die zentralen Inhalte der Vorlesung verstanden wurden.</p>
|
||
<p>Dies hat häufig auch damit zu tun, dass man im Bachelor eher Grundlagen hört und somit ein grobes Verständnis aller Dinge wichtig ist, während im Master auf die Aneignung von Tiefenwissen ankommt.</p>
|
||
</section>
|
||
</section>
|
||
<section id="prüfungsangt" class="level2">
|
||
<h2 class="anchored" data-anchor-id="prüfungsangt">Prüfungsangt</h2>
|
||
<p>Zu guter Letzt noch ein paar Worte zum Thema Prüfungsangst. Es ist normal, dass man vor einer Prüfung angespannt ist. Es ist nicht normal, wenn die Anspannung so ausartet, dass man sich übergibt, Krämpfe bekommt oder ähnlich starke Symptome zeigt. Ich leide selbst an solchen Problemen und habe mich schon mehrfach vor Prüfungen übergeben. Eine klassische Konfrontationstherapie funktioniert aufgrund der Seltenheit der Prüfungen nicht oder nur sehr schwer. Ich habe mich an meinen Arzt gewendet und habe nun genau für solche Situationen ein Medikament. 1-2h vor einer Prüfung nehme ich das und komme in einen komischen Zustand. Ich merke zwar noch, dass ich Angespannt bin und eigentlich Angst hätte, aber es “stört” mich nicht wirklich. Es versetzt mich nicht in Panik oder sonstwas. Es schaltet mein Gehirn nicht aus oder hat andere negative Effekte. Natürlich geht das auch mit Nachteilen einher: ein paar Tage keinen Alkohol, kein Auto fahren, etc. - Aber meist ist das ja nur 2-3x/Semester der Fall. Wenn man nicht so stark betroffen ist, dann ist davon allerdings abzuraten. Das Medikament gleicht die Panik durch Gelassenheit aus - wenn man keine Panik hat, dann wird man hierdurch so “gelassen” dass man mehrere Stunden einschläft - was in einer Prüfung vielleicht nicht ganz so gut ist ;)</p>
|
||
<p>Es gibt auch zahlreiche Regularien und Rechtsansprüche, die ihr bei sowas habt. Ihr habt zum Beispiel (sofern ein (Amts?-)Arzt eine Prüfungsangst bestätigt hat) Anspruch auf mehr Prüfungszeit, die Prüfung alleine abzulegen (z.b. bei einem Mitarbeiter, während andere im Hörsaal schreiben), eine mündliche durch eine schriftliche zu tauschen (oder umgekehrt), etc. Das kann man individuell mit dem Prüfer absprechen. Ich weiss nicht, wie das in anderen Fakultäten läuft - aber in der Technischen Fakultät hat fast jeder Prüfer dafür volles Verständnis (einige litten sogar früher selbst an sowas).</p>
|
||
<p>Die kostenlose psychologische Beratung an der Uni (aka. “Das rote Sofa” im X) bietet hier auch Hilfestellung bei und vermittelt in schwereren Fällen auch gleich noch eine Therapie/Ärzte. Hier kann man z.b. Prüfungssimulationen abhalten oder sich Hilfe holen, wenn ein Dozent sich querstellt. Die Mitarbeiter begleiten einen z.B. auch zu einer Prüfung (nach Absprache mit dem Veranstalter), falls das hilft, etc.</p>
|
||
<p>Es ist keine Schande so ein Problem zu haben und es gibt genug, die sich damit rumschlagen. Aber man ist hier an der Uni auch nicht alleine damit. Es gibt zahlreiche Hilfsangebote.</p>
|
||
</section>
|
||
<section id="schlusswort" class="level2">
|
||
<h2 class="anchored" data-anchor-id="schlusswort">Schlusswort</h2>
|
||
<p>Viel Erfolg bei euren Prüfungen. Falls euch dieser Artikel geholfen hat oder ihr noch Anregungen/Verbessenguswünsche habt, schreibt mir einfach.</p>
|
||
|
||
|
||
</section>
|
||
|
||
]]></description>
|
||
<category>Article</category>
|
||
<category>Experience</category>
|
||
<category>Uni</category>
|
||
<guid>https://drezil.de/Uni/Lernerfolg_an_der_Uni.html</guid>
|
||
<pubDate>Wed, 31 Dec 2014 23:00:00 GMT</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>Die Bielefeld-Verschwörung</title>
|
||
<link>https://drezil.de/Stuff/Bielefeldverschwoerung.html</link>
|
||
<description><![CDATA[
|
||
|
||
|
||
|
||
|
||
|
||
<p>Kopie des vermutlichen Originals von (vermutlich) Achim Held aus 1994.</p>
|
||
<hr>
|
||
<p><strong>Warnung:</strong> Diese Seite enthält Material, von dem SIE nicht wollen, dass es bekannt wird. Speichern Sie diese Seite nicht auf Ihrer lokalen Platte ab, denn sonst sind Sie auch dran, wenn SIE plötzlich bei Ihnen vor der Tür stehen; und das passiert schneller als man denkt. Auch sollten Sie versuchen, alle Hinweise darauf, dass Sie diese Seite jemals gelesen haben, zu vernichten. Tragen Sie diese Seite auf keinen Fall in ihre Hotlist/Bookmarks/etc… ein!</p>
|
||
<p>Vielen Dank für die Beachtung aller Sicherheitsvorschriften.</p>
|
||
<section id="die-geschichte-der-entdeckung" class="level2">
|
||
<h2 class="anchored" data-anchor-id="die-geschichte-der-entdeckung">Die Geschichte der Entdeckung</h2>
|
||
<p>Vor einigen Jahren fiel es einigen Unerschrockenen zum ersten Mal auf, dass in den Medien immer wieder von einer Stadt namens ‘Bielefeld’ die Rede war, dass aber niemand jemanden aus Bielefeld kannte, geschweige denn selbst schon einmal dort war. Zuerst hielten sie dies für eine belanglose Sache, aber dann machte es sie doch neugierig. Sie unterhielten sich mit anderen darüber, ohne zu ahnen, dass dies bereits ein Fehler war: Aus heutiger Sicht steht fest, dass jemand geplaudert haben muss, denn sofort darauf wurden SIE aktiv. Plötzlich tauchten Leute auf, die vorgaben, schon einmal in Bielefeld gewesen zu sein; sogar Personen, die vormals noch laut Zweifel geäußert hatten, berichteten jetzt davon, sich mit eigenen Augen von der Existenz vergewissert zu haben - immer hatten diese Personen bei ihren Berichten einen seltsam starren Blick. Doch da war es schon zu spät - die Saat des Zweifels war gesät. Weitere Personen stießen zu der Kerngruppe der Zweifler, immer noch nicht sicher, was oder wem man da auf der Spur war.</p>
|
||
<p>Dann, im Oktober 1993, der Durchbruch: Auf der Fahrt von Essen nach Kiel auf der A2 erhielten vier der hartnäckigsten Streiter für die Aufdeckung der Verschwörung ein Zeichen: Jemand hatte auf allen Schildern den Namen ‘Bielefeld’ mit orangem Klebeband durchgestrichen. Da wußte die Gruppe: Man ist nicht alleine, es gibt noch andere, im Untergrund arbeitende Zweifler, womöglich über ganz Deutschland verteilt, die auch vor spektakulären Aktionen nicht zurückschrecken. Von da an war uns klar: Wir müssen diese Scharade aufdecken, koste es, was es wolle! Das Ausmaß der Verschwörung</p>
|
||
<p>Der Aufwand, mit dem die Täuschung der ganzen Welt betrieben wird, ist enorm. Die Medien, von denen ja bekannt ist, dass sie unter IHRER Kontrolle stehen, berichten tagaus, tagein von Bielefeld, als sei dies eine Stadt wie jede andere, um der Bevölkerung das Gefühl zu geben, hier sei alles ganz normal. Aber auch handfestere Beweise werden gefälscht: SIE kaufen hunderttausende von Autos, versehen sie mit gefälschten ’BI-’Kennzeichen und lassen diese durch ganz Deutschland fahren. SIE stellen, wie bereits oben geschildert, entlang der Autobahnen große Schilder auf, auf denen Bielefeld erwähnt wird. SIE veröffentlichen Zeitungen, die angeblich in Bielefeld gedruckt werden. Anscheinend haben SIE auch die Deutsche Post AG in Ihrer Hand, denn auch im PLZB findet man einen Eintrag für Bielefeld; und ebenso wird bei der Telekom ein komplettes Ortsnetz für Bielefeld simuliert. Einige Leute behaupten sogar in Bielefeld studiert zu haben und können auch gut gefälschte Diplome u.ä. der angeblich existenten Uni Bielefeld vorweisen. Auch Bundeskanzler Gerhard Schröder behauptet, 1965 das “Westfalen-Kolleg” in Bielefeld besucht zu haben, wie seinem Lebenslauf unter dem Link Bildungsweg zu entnehmen ist.</p>
|
||
<p>Aber auch vor dem Internet machen SIE nicht halt. SIE vergeben Mail-Adressen für die Domain uni-bielefeld.de, und SIE folgen auch den neuesten Trends: SIE bieten im WWW eine <a href="https://bielefeld.de">“Stadtinfo über Bielefeld”</a> an, sogar mit Bildern; das Vorgarten-Foto, das dem Betrachter als “Botanischer Garten” verkauft werden sollte, ist nach der Entlarvung auf dieser Seite jedoch inzwischen wieder entfernt worden. Aber auch die noch vorhandenen Bilder sind sogar für den Laien als Fotomontagen zu erkennen. Wir sind noch nicht dahinter gekommen, wo der Rechner steht, auf dem die Domains .bielefeld.de und uni-bielefeld.de gefälscht werden; wir arbeiten daran. Inzwischen wurde auch von einem IHRER Agenten - der Täter ist uns bekannt - versucht, diese WWW-Seite zu sabotieren, ich konnte den angerichteten Schaden jedoch zum Glück wieder beheben.</p>
|
||
<p>Ein anonymer Informant, der ganz offensichtlich zu IHNEN zu gehören scheint oder zumindest gute Kontakte zu IHNEN hat, hat mich kürzlich in einer Mail auf die nächste Stufe IHRER Planung hingewiesen: “Ich schätze, spätestens in 10 Jahren wird es heißen: Bielefeld muss Hauptstadt werden.” Was das bedeutet, muss ja wohl nicht extra betont werden.</p>
|
||
<p>Die schrecklichste Maßnahme, die SIE ergriffen haben, ist aber zweifelsohne immer noch die Gehirnwäsche, der immer wieder harmlose Menschen unterzogen werden, die dann anschließend auch die Existenz von Bielefeld propagieren. Immer wieder verschwinden Menschen, gerade solche, die sich öffentlich zu ihren Bielefeldzweifeln bekannt haben, nur um dann nach einiger Zeit wieder aufzutauchen und zu behaupten, sie seien in Bielefeld gewesen. Womöglich wurden einige Opfer sogar mit Telenosestrahlen behandelt. Diesen armen Menschen konnten wir bisher nicht helfen. Wir haben allerdings inzwischen einen Verdacht, wo diese Gehirnwäsche durchgeführt wird: Im sogenannten Bielefeld-Zentrum, wobei SIE sogar die Kaltblütigkeit besitzen, den Weg zu diesem Ort des Schreckens von der Autobahn aus mit großen Schildern auszuschildern. Wir sind sprachlos, welchen Einfluß SIE haben.</p>
|
||
<p>Inzwischen sind - wohl auch durch mehrere Berichte in den wenigen nicht von IHNEN kontrollierten Medien - mehr und mehr Leute wachsamer geworden und machen uns auf weitere Aspekte der Verschwörung aufmerksam. So berichtet zum Beispiel Holger Blaschka:</p>
|
||
<p>“Auch der DFB ist in diesen gewaltigen Skandal verwickelt, spielt in der ersten Liga doch ein Verein, den SIE Arminia Bielefeld getauft haben, der innert 2 Jahren aus dem Nichts der Amateur-Regionen im bezahlten Fußball auftauchte und jetzt im Begriff ist, sich zu IHRER besten Waffe gegen all die Zweifler zu entwickeln. Den Gästefans wird vorgetäuscht mit ihren Bussen nach Bielefeld zu kommen, wo sie von IHNEN abgefangen werden, um direkt ins Stadion geleitet zu werden. Es besteht keine Chance sich die Stadt näher anzuschauen, und auch die Illusion des Heimpublikums wird durch eine größere Menge an bezahlten Statisten aufrechterhalten. Selbst ehemalige Top-Spieler, die Ihren Leistungszenit bei weitem überschritten haben, werden zu diesem Zweck von IHNEN mißbraucht. Mit genialen Manövern, u.a. vorgetäuschten Faustschlägen und Aufständen gegen das Präsidium eines baldigen Drittligisten wurde von langer Hand die wohl aufwendigste Täuschung aller Zeiten inszeniert. Es gibt noch mehr Beweise: Das sich im Rohbau befindende Stadion, das gefälschte und verpanschte Bier und nicht zuletzt die Tatsache, dass dieser Verein nur einen Sponsor hat. SIE, getarnt als Modefirma Gerry Weber.”</p>
|
||
</section>
|
||
<section id="was-steckt-dahinter" class="level2">
|
||
<h2 class="anchored" data-anchor-id="was-steckt-dahinter">Was steckt dahinter?</h2>
|
||
<p>Dies ist die Frage, auf die wir auch nach jahrelangen Untersuchungen immer noch keine befriedigende Antwort geben können. Allerdings gibt es einige Indizien, die auf bestimmte Gruppierungen hinweisen:</p>
|
||
<ul>
|
||
<li>Es könnte eine Gruppe um den Sternenbruder und Weltenlehrer Ashtar Sheran dahinterstecken, die an der Stelle, an der Bielefeld liegen soll, ihre Landung vorbereiten, die - einschlägiger Fachliteratur zufolge - kurz bevorsteht. Zu dieser Gruppe sollen auch Elvis und Kurt Cobain gehören, die beide - vom schwedischen Geheimdienst gedeckt - noch am Leben sind.</li>
|
||
<li>An der Stelle, an der Bielefeld liegen soll, hält die CIA John F. Kennedy seit dem angeblichen Attentat versteckt, damit er nichts über die vorgetäuschte Mondlandung der NASA erzählen kann. Inwieweit die Reichsflugscheibenmacht von ihrer Mond- oder Marsbasis aus da mitspielt, können wir nicht sagen, da alle Beweise beim Abschuß der schwer bewaffneten Marssonde Observer vernichtet wurden. Informationen hierüber besitzt vielleicht der Vatikan, der seit den 50er Jahren regelmäßig mit tachyonenangetriebenen Schiffen zum Mars fliegt.</li>
|
||
<li>Der MOSSAD in Zusammenarbeit mit dem OMEGA-Sektor planen an dieser Stelle die Errichtung eines geheimen Forschungslabors, weil sich genau an diesem Ort zwei noch nicht dokumentierte Ley-Linien kreuzen. Dort könnte auch der Jahrtausende alte Tunnel nach Amerika und Australien (via Atlantis) seinen Eingang haben. Wichtige Mitwisser, namentlich Uwe Barschel und Olof Palme, wurden von den mit dem MOSSAD zusammenarbeitenden Geheimdiensten, darunter der Stasi und der weniger bekannten ‘Foundation’, frühzeitig ausgeschaltet.</li>
|
||
<li>An der Stelle liegt die Höhle eines der schlafenden Drachen aus dem Vierten Zeitalter, die auf das Erwachen der Magie am 24. Dezember 2011 (siehe hierzu den Maya-Kalender) warten. Beschützt wird diese Stelle von den Rittern des Ordenskreuzes AAORRAC, die sich inzwischen mit der Herstellung von programmiertem Wasser beschäftigen - nach einen Rezept, das sie unter brutaler Folter von Ann Johnson bekommen haben. Diese hatte es bekanntlich von hohen Lichtwesen aus dem All erhalten, um die Menschheit vor außerirdischen Implantaten bis Stufe 3 zu schützen.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="was-können-wir-tun" class="level2">
|
||
<h2 class="anchored" data-anchor-id="was-können-wir-tun">Was können wir tun?</h2>
|
||
<p>Zum einen können wir alle an den Bundestag, das Europaparlament und die UNO schreiben, um endlich zu erreichen, dass SIE nicht mehr von den Politikern gedeckt werden. Da aber zu befürchten ist, dass SIE die Politik - so wie auch das organisierte Verbrechen und die großen Weltreligionen - unter Kontrolle haben, sind die Erfolgschancen dieses Weges doch eher zweifelhaft.</p>
|
||
<p>Eine weitere Möglichkeit besteht darin, dass sich alle Bielefeldzweifler treffen und gemeinsam durch transzendentale Meditation (TM) soviel positive Ausstrahlung erzeugen, dass der Schwindel auffliegt. Eine ähnliche Vorgehensweise hat in Washington, D.C. für eine Senkung der Verbrechensrate um über 20% gesorgt. Besonders effektiv ist dies im Zusammenwirken mit Hopi-Kerzen im Ohr und Yogischem Schweben.</p>
|
||
<p>Ab und zu nimmt in einer der eigentlich von IHNEN kontrollierten Zeitungen ein Redakteur allen Mut zusammen und riskiert es, in einer der Ausgaben zumindest andeutungsweise auf die Verschwörung hinzuweisen. So wurde in der FAZ Bielefeld als “Die Mutter aller Un-Städte” bezeichnet, und die taz überschrieb einen Artikel mit “Das Bermuda-Dreieck bei Bielefeld”. Auf Nachfrage bekommt man dann natürlich zu hören, das habe man alles ganz anders gemeint, bei der taz hieß es sogar, es hätte in Wirklichkeit “Bitterfeld” heißen sollen, aber für einen kurzen Moment wurden die Leser darauf aufmerksam gemacht, dass mit Bielefeld etwas nicht stimmt. An dem Mut dieser Redakteure, über deren weiteres Schicksal uns leider nichts bekannt ist, sollten wir uns alle ein Beispiel nehmen.</p>
|
||
<p>Das, was wir alle aber für uns im kleinen tun können, ist folgendes: Kümmert euch um die bedauernswerten Opfer der Gehirnwäsche, umsorgt sie, macht ihnen behutsam klar, dass sie einer Fehlinformation unterliegen. Und, bekennt euch alle immer offen, damit SIE merken, dass wir uns nicht länger täuschen lassen: <strong>Bielefeld gibt es nicht!!!</strong></p>
|
||
|
||
|
||
</section>
|
||
|
||
]]></description>
|
||
<category>Article</category>
|
||
<category>Fun</category>
|
||
<category>Archived</category>
|
||
<guid>https://drezil.de/Stuff/Bielefeldverschwoerung.html</guid>
|
||
<pubDate>Fri, 31 Dec 1993 23:00:00 GMT</pubDate>
|
||
</item>
|
||
</channel>
|
||
</rss>
|