From e3166c01aeff8c19ac45067b57d56227c62c66ff Mon Sep 17 00:00:00 2001 From: Nicole Dresselhaus Date: Fri, 13 Jun 2025 10:22:59 +0200 Subject: [PATCH] obsidian submodule, regenerated page --- Obsidian-Articles | 2 +- Writing/_metadata.yml | 7 + _quarto.yml | 8 +- dist/About/Experience.html | 6 + dist/About/Extracurricular.html | 6 + dist/About/Work.html | 6 + dist/About/index.html | 6 + dist/Coding/Haskell/Advantages.html | 6 + dist/Coding/Haskell/Code Snippets/Monoid.html | 6 + .../Haskell/Code Snippets/Morphisms.html | 6 + dist/Coding/Haskell/FFPiH.html | 6 + dist/Coding/Haskell/Lenses.html | 6 + .../Haskell/Webapp-Example/Main.hs.html | 6 + .../Webapp-Example/MyService_Types.hs.html | 6 + dist/Coding/Haskell/Webapp-Example/index.html | 6 + dist/Health/Issues.html | 6 + .../Artikelbewertung durch LLM.html | 1191 +++++++++++++++++ dist/Opinions/Don't train your own LLM.html | 6 + dist/Opinions/Editors.html | 6 + dist/Opinions/Keyboard-Layout.html | 6 + dist/Stuff/Bielefeldverschwoerung.html | 6 + dist/Uni/Lernerfolg_an_der_Uni.html | 6 + dist/Writing/Obsidian-RAG.html | 6 + dist/Writing/documentation.html | 6 + dist/Writing/ner4all-case-study.html | 6 + dist/index.html | 69 +- dist/index.xml | 138 +- dist/listings.json | 1 + dist/search.json | 1107 +++++++-------- dist/sitemap.xml | 128 +- index.qmd | 1 + pandoc-filter/obsidian-callouts.lua | 46 + 32 files changed, 2210 insertions(+), 614 deletions(-) create mode 100644 dist/Obsidian-Articles/Artikelbewertung durch LLM.html create mode 100644 pandoc-filter/obsidian-callouts.lua diff --git a/Obsidian-Articles b/Obsidian-Articles index 5ff227d..0bff1f3 160000 --- a/Obsidian-Articles +++ b/Obsidian-Articles @@ -1 +1 @@ -Subproject commit 5ff227d54562a8ea682e28334f7d39d5cfef0eb0 +Subproject commit 0bff1f37eb12458aae20f31f4b47c12939c854a7 diff --git a/Writing/_metadata.yml b/Writing/_metadata.yml index 2d2746d..d3322c0 100644 --- a/Writing/_metadata.yml +++ b/Writing/_metadata.yml @@ -1,3 +1,10 @@ google-scholar: true execute: enable: false + +format: + html: + citation-location: document + reference-location: margin # fußnoten im Margin (falls gewünscht) + mermaid: + theme: default diff --git a/_quarto.yml b/_quarto.yml index 6f7c19d..45cb3e0 100644 --- a/_quarto.yml +++ b/_quarto.yml @@ -4,6 +4,9 @@ project: - thumbs output-dir: dist +filters: + - pandoc-filter/obsidian-callouts.lua + website: title: "Nicole Dresselhaus" site-url: https://drezil.de @@ -25,7 +28,10 @@ website: contents: - section: "Serious" contents: - - auto: "Writing" + - section: "Writing" + contents: + - auto: Writing/* + - auto: Obsidian-Articles/* - auto: "Coding" - auto: "Health" - auto: "Uni" diff --git a/dist/About/Experience.html b/dist/About/Experience.html index 5597486..492bb18 100644 --- a/dist/About/Experience.html +++ b/dist/About/Experience.html @@ -343,6 +343,12 @@ ul.task-list li input[type="checkbox"] { RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze + + diff --git a/dist/About/Extracurricular.html b/dist/About/Extracurricular.html index c062d4a..a21f754 100644 --- a/dist/About/Extracurricular.html +++ b/dist/About/Extracurricular.html @@ -343,6 +343,12 @@ ul.task-list li input[type="checkbox"] { RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze + + diff --git a/dist/About/Work.html b/dist/About/Work.html index 8e3a943..b0ea0e5 100644 --- a/dist/About/Work.html +++ b/dist/About/Work.html @@ -342,6 +342,12 @@ ul.task-list li input[type="checkbox"] { RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze + + diff --git a/dist/About/index.html b/dist/About/index.html index 2eb6c83..e39573e 100644 --- a/dist/About/index.html +++ b/dist/About/index.html @@ -341,6 +341,12 @@ ul.task-list li input[type="checkbox"] { RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze + + diff --git a/dist/Coding/Haskell/Advantages.html b/dist/Coding/Haskell/Advantages.html index f6ed02f..a35f177 100644 --- a/dist/Coding/Haskell/Advantages.html +++ b/dist/Coding/Haskell/Advantages.html @@ -344,6 +344,12 @@ ul.task-list li input[type="checkbox"] { RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze + + diff --git a/dist/Coding/Haskell/Code Snippets/Monoid.html b/dist/Coding/Haskell/Code Snippets/Monoid.html index 8a19504..0d39af4 100644 --- a/dist/Coding/Haskell/Code Snippets/Monoid.html +++ b/dist/Coding/Haskell/Code Snippets/Monoid.html @@ -379,6 +379,12 @@ pre > code.sourceCode > span > a:first-child::before { text-decoration: underlin RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze + + diff --git a/dist/Coding/Haskell/Code Snippets/Morphisms.html b/dist/Coding/Haskell/Code Snippets/Morphisms.html index fc972cf..f77e20b 100644 --- a/dist/Coding/Haskell/Code Snippets/Morphisms.html +++ b/dist/Coding/Haskell/Code Snippets/Morphisms.html @@ -381,6 +381,12 @@ Knowing that this might sound daunting to many dabbling Haskellers (like I am), RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze + + diff --git a/dist/Coding/Haskell/FFPiH.html b/dist/Coding/Haskell/FFPiH.html index feeeee5..82a47c7 100644 --- a/dist/Coding/Haskell/FFPiH.html +++ b/dist/Coding/Haskell/FFPiH.html @@ -344,6 +344,12 @@ ul.task-list li input[type="checkbox"] { RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze + + diff --git a/dist/Coding/Haskell/Lenses.html b/dist/Coding/Haskell/Lenses.html index 78e1007..77d7d5a 100644 --- a/dist/Coding/Haskell/Lenses.html +++ b/dist/Coding/Haskell/Lenses.html @@ -379,6 +379,12 @@ pre > code.sourceCode > span > a:first-child::before { text-decoration: underlin RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze + + diff --git a/dist/Coding/Haskell/Webapp-Example/Main.hs.html b/dist/Coding/Haskell/Webapp-Example/Main.hs.html index 2e06b01..7056e29 100644 --- a/dist/Coding/Haskell/Webapp-Example/Main.hs.html +++ b/dist/Coding/Haskell/Webapp-Example/Main.hs.html @@ -379,6 +379,12 @@ pre > code.sourceCode > span > a:first-child::before { text-decoration: underlin RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze + + diff --git a/dist/Coding/Haskell/Webapp-Example/MyService_Types.hs.html b/dist/Coding/Haskell/Webapp-Example/MyService_Types.hs.html index 407d9ee..397adb1 100644 --- a/dist/Coding/Haskell/Webapp-Example/MyService_Types.hs.html +++ b/dist/Coding/Haskell/Webapp-Example/MyService_Types.hs.html @@ -379,6 +379,12 @@ pre > code.sourceCode > span > a:first-child::before { text-decoration: underlin RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze + + diff --git a/dist/Coding/Haskell/Webapp-Example/index.html b/dist/Coding/Haskell/Webapp-Example/index.html index 3eb698c..7e67dae 100644 --- a/dist/Coding/Haskell/Webapp-Example/index.html +++ b/dist/Coding/Haskell/Webapp-Example/index.html @@ -379,6 +379,12 @@ pre > code.sourceCode > span > a:first-child::before { text-decoration: underlin RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze + + diff --git a/dist/Health/Issues.html b/dist/Health/Issues.html index 7d8dbda..66ec7e9 100644 --- a/dist/Health/Issues.html +++ b/dist/Health/Issues.html @@ -344,6 +344,12 @@ ul.task-list li input[type="checkbox"] { RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze + + diff --git a/dist/Obsidian-Articles/Artikelbewertung durch LLM.html b/dist/Obsidian-Articles/Artikelbewertung durch LLM.html new file mode 100644 index 0000000..33d3a25 --- /dev/null +++ b/dist/Obsidian-Articles/Artikelbewertung durch LLM.html @@ -0,0 +1,1191 @@ + + + + + + + + + + +Artikelbewertung durch ein Large Language Model (LLM) - Ein Beispiel aus der Praxis – Nicole Dresselhaus + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ + +
+ + + +
+ + +
+
+

Artikelbewertung durch ein Large Language Model (LLM) - Ein Beispiel aus der Praxis

+
+
Praxis
+
Beispiel
+
ML
+
Experience
+
+
+ +
+
+ Aktuelle Wege ein LLM dazu zu “motivieren”, was man möchte. +
+
+ + +
+ +
+
Autor:in
+
+

Nicole Dresselhaus

+
+
+ +
+
Veröffentlichungsdatum
+
+

13. Juni 2025

+
+
+ + +
+ +
+
+
Zusammenfassung
+ Damit das LLMs keinen “mist” bauen, wird in diesem Beispiel eine Antwortstruktur vorgegeben, der einfach zu folgen ist. Trotzdem bleibt das immer noch nur “fancy next word prediction” - aber immerhin besser, als alles, was man sonst in 1 Minute aus dem Ärmel schüttelt. +
+
+ + +
+ + +
+

Hintergrund

+

Untenstehend ein Praxisbeispiel, wie ich momentan ein LLM aufrufe um sinnvolle(!) Antworten zu bekommen. Das ganze läuft lokal via ollama.
+In den Kontext setzt das avante-plugin1 ein großes system-prompt und fügt dort die gesamte Datei (in diesem Fall ein sehr langer markdown-text) ein.

+

1 Neovim-Plugin, was den Editor quasi zu einem cursor.ai oder einem VS-Code mit allen Späßen aufbohrt

Insbesondere sieht man hier: Damit das LLM keinen “mist” baut oder zu sehr halluziniert, wird eine Antwortstruktur vorgegeben, der einfach zu folgen ist. Trotzdem ist das immer noch nur “fancy next word prediction” - aber immerhin besser, als alles, was man sonst in 1 Minute aus dem Ärmel schüttelt.

+
+
+

LLM-Interaktion

+
+
+
+ +
+
+Call Metadata +
+
+
+
    +
  • Datetime: 2025-06-05 13:47:11
  • +
  • Model: ollama/cogito:14b
  • +
  • Selected files: +
      +
    • background/BACKGROUND.md
    • +
  • +
+
+
+
+
+
+ +
+
+Warnung +
+
+
+

Enable deep thinking subroutine.

+

Welche Absätze gleichen sich inhaltlich zu stark? Welche Absätze sollten in andere Kapitel wandern?

+

Gehe den Text im Geiste Absatz für Absatz durch und zeig mir, dass du ihn gelesen hast.

+

Ein einfaches: "<Absatztitel>: ok" oder "<Absatztitel>: xx gefunden" reicht um pro Absatz eine Aussage zu treffen.

+
+
+
+
+
+ +
+
+🤔 Thought content +
+
+
+
    +
  1. Einleitung: Einführung in die Bedeutung der Dokumentation - ok
  2. +
  3. Inhaltliche Anforderungen an die Dokumentation: +
      +
    • Hier werden spezifische Inhalte definiert, die dokumentiert werden müssen
    • +
    • Keine Überschneidung mit anderen Abschnitten
    • +
  4. +
  5. Format und Struktur der Dokumentation: +
      +
    • Fokussiert auf die Formate und Strukturen der Dokumentation
    • +
    • Keine Überschneidung mit Inhaltlichen Anforderungen
    • +
  6. +
  7. Was macht eine gute Dokumentation aus: +
      +
    • Hier werden allgemeine Prinzipien für gute Dokumentation erläutert
    • +
    • Teile könnten in “Inhaltliche Anforderungen” oder “Format und Struktur” übernommen werden
    • +
  8. +
  9. (Teil-)automatisierte Dokumentationswerkzeuge: +
      +
    • Beschreibt spezifische Tools zur Automatisierung der Dokumentation
    • +
    • Keine Überschneidung mit anderen Abschnitten
    • +
  10. +
  11. Best Practices, Vorlagen und Checklisten: +
      +
    • Enthält praktische Ratschläge und Werkzeuge
    • +
    • Teile könnten in “Format und Struktur” oder “Was macht eine gute Dokumentation aus” integriert werden
    • +
  12. +
  13. Implementierung aller Vorschläge als ready-to-use Repository: +
      +
    • Beschreibt die Umsetzung der Empfehlungen
    • +
    • Keine Überschneidung mit anderen Abschnitten
    • +
  14. +
  15. Fazit: +
      +
    • Zusammenfassung und Schlussfolgerung
    • +
    • Keine Überschneidung mit anderen Abschnitten
    • +
  16. +
+
+
+
+
+
+ +
+
+Repsonse +
+
+
+

Einleitung: ok

+

Inhaltliche Anforderungen an die Dokumentation: ok

+

Format und Struktur der Dokumentation: ok

+

Was macht eine gute Dokumentation aus: “Nutzungshilfen außerhalb der Dokumentation” könnte in “Format und Struktur” übernommen werden

+

(Teil-)automatisierte Dokumentationswerkzeuge: ok

+

Best Practices, Vorlagen und Checklisten: “Checkliste für die Mindest-Dokumentation” könnte in “Inhaltliche Anforderungen” integriert werden

+

Implementierung aller Vorschläge als ready-to-use Repository: ok

+

Fazit: ok

+
+
+ + +
+ + +
+ +
+ + + + + \ No newline at end of file diff --git a/dist/Opinions/Don't train your own LLM.html b/dist/Opinions/Don't train your own LLM.html index e700051..5c795f9 100644 --- a/dist/Opinions/Don't train your own LLM.html +++ b/dist/Opinions/Don't train your own LLM.html @@ -343,6 +343,12 @@ ul.task-list li input[type="checkbox"] { RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze + + diff --git a/dist/Opinions/Editors.html b/dist/Opinions/Editors.html index fd2b458..88617d3 100644 --- a/dist/Opinions/Editors.html +++ b/dist/Opinions/Editors.html @@ -344,6 +344,12 @@ ul.task-list li input[type="checkbox"] { RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze + + diff --git a/dist/Opinions/Keyboard-Layout.html b/dist/Opinions/Keyboard-Layout.html index a3962fb..b0a6590 100644 --- a/dist/Opinions/Keyboard-Layout.html +++ b/dist/Opinions/Keyboard-Layout.html @@ -344,6 +344,12 @@ ul.task-list li input[type="checkbox"] { RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze + + diff --git a/dist/Stuff/Bielefeldverschwoerung.html b/dist/Stuff/Bielefeldverschwoerung.html index 4f4d0ad..73e162f 100644 --- a/dist/Stuff/Bielefeldverschwoerung.html +++ b/dist/Stuff/Bielefeldverschwoerung.html @@ -344,6 +344,12 @@ ul.task-list li input[type="checkbox"] { RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze + + diff --git a/dist/Uni/Lernerfolg_an_der_Uni.html b/dist/Uni/Lernerfolg_an_der_Uni.html index 31bd1a4..1a18628 100644 --- a/dist/Uni/Lernerfolg_an_der_Uni.html +++ b/dist/Uni/Lernerfolg_an_der_Uni.html @@ -344,6 +344,12 @@ ul.task-list li input[type="checkbox"] { RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze + + diff --git a/dist/Writing/Obsidian-RAG.html b/dist/Writing/Obsidian-RAG.html index 1ea1f93..8ba4c7f 100644 --- a/dist/Writing/Obsidian-RAG.html +++ b/dist/Writing/Obsidian-RAG.html @@ -394,6 +394,12 @@ div.csl-indent { RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze + + diff --git a/dist/Writing/documentation.html b/dist/Writing/documentation.html index 8cd8f8c..04e468e 100644 --- a/dist/Writing/documentation.html +++ b/dist/Writing/documentation.html @@ -396,6 +396,12 @@ Nutzer*innen zu helfen, die theoretischen Grundlagen nachvollziehbar zu machen. RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze + + diff --git a/dist/Writing/ner4all-case-study.html b/dist/Writing/ner4all-case-study.html index e4168d9..32c0e91 100644 --- a/dist/Writing/ner4all-case-study.html +++ b/dist/Writing/ner4all-case-study.html @@ -440,6 +440,12 @@ maintaining data privacy and reproducibility. RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze + + diff --git a/dist/index.html b/dist/index.html index 9856da9..ab74821 100644 --- a/dist/index.html +++ b/dist/index.html @@ -406,6 +406,12 @@ window.Quarto = { RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze + + @@ -637,7 +643,7 @@ window.Quarto = {
  • Letzte Posts
  • -
    Categories
    Archived
    Article
    Best Practices
    Case-study
    Code
    Experience
    Fun
    Haskell
    Lecture
    Links
    Mental Health
    ML
    NER
    RAG
    Struggles
    Tutorial
    Uni
    +
    Categories
    Archived
    Article
    Beispiel
    Best Practices
    Case-study
    Code
    Experience
    Fun
    Haskell
    Lecture
    Links
    Mental Health
    ML
    NER
    Praxis
    RAG
    Struggles
    Tutorial
    Uni
    @@ -672,7 +678,40 @@ window.Quarto = {
    - - - - - - - - - - - - - -
    +

    diff --git a/dist/index.xml b/dist/index.xml index f90e785..4440ef7 100644 --- a/dist/index.xml +++ b/dist/index.xml @@ -10,7 +10,143 @@ Ramblings of a madwoman quarto-1.7.23 -Wed, 04 Jun 2025 22:00:00 GMT +Thu, 12 Jun 2025 22:00:00 GMT + + Artikelbewertung durch ein Large Language Model (LLM) - Ein Beispiel aus der Praxis + https://drezil.de/Obsidian-Articles/Artikelbewertung durch LLM.html + +

    Hintergrund

    +

    Untenstehend ein Praxisbeispiel, wie ich momentan ein LLM aufrufe um sinnvolle(!) Antworten zu bekommen. Das ganze läuft lokal via ollama.
    +In den Kontext setzt das avante-plugin1 ein großes system-prompt und fügt dort die gesamte Datei (in diesem Fall ein sehr langer markdown-text) ein.

    +

    1 Neovim-Plugin, was den Editor quasi zu einem cursor.ai oder einem VS-Code mit allen Späßen aufbohrt

    Insbesondere sieht man hier: Damit das LLM keinen “mist” baut oder zu sehr halluziniert, wird eine Antwortstruktur vorgegeben, der einfach zu folgen ist. Trotzdem ist das immer noch nur “fancy next word prediction” - aber immerhin besser, als alles, was man sonst in 1 Minute aus dem Ärmel schüttelt.

    + +
    +

    LLM-Interaktion

    +
    +
    +
    + +
    +
    +Call Metadata +
    +
    +
    +
      +
    • Datetime: 2025-06-05 13:47:11
    • +
    • Model: ollama/cogito:14b
    • +
    • Selected files: +
        +
      • background/BACKGROUND.md
      • +
    • +
    +
    +
    +
    +
    +
    + +
    +
    +Warnung +
    +
    +
    +

    Enable deep thinking subroutine.

    +

    Welche Absätze gleichen sich inhaltlich zu stark? Welche Absätze sollten in andere Kapitel wandern?

    +

    Gehe den Text im Geiste Absatz für Absatz durch und zeig mir, dass du ihn gelesen hast.

    +

    Ein einfaches: "<Absatztitel>: ok" oder "<Absatztitel>: xx gefunden" reicht um pro Absatz eine Aussage zu treffen.

    +
    +
    +
    +
    +
    + +
    +
    +🤔 Thought content +
    +
    +
    +
      +
    1. Einleitung: Einführung in die Bedeutung der Dokumentation - ok
    2. +
    3. Inhaltliche Anforderungen an die Dokumentation: +
        +
      • Hier werden spezifische Inhalte definiert, die dokumentiert werden müssen
      • +
      • Keine Überschneidung mit anderen Abschnitten
      • +
    4. +
    5. Format und Struktur der Dokumentation: +
        +
      • Fokussiert auf die Formate und Strukturen der Dokumentation
      • +
      • Keine Überschneidung mit Inhaltlichen Anforderungen
      • +
    6. +
    7. Was macht eine gute Dokumentation aus: +
        +
      • Hier werden allgemeine Prinzipien für gute Dokumentation erläutert
      • +
      • Teile könnten in “Inhaltliche Anforderungen” oder “Format und Struktur” übernommen werden
      • +
    8. +
    9. (Teil-)automatisierte Dokumentationswerkzeuge: +
        +
      • Beschreibt spezifische Tools zur Automatisierung der Dokumentation
      • +
      • Keine Überschneidung mit anderen Abschnitten
      • +
    10. +
    11. Best Practices, Vorlagen und Checklisten: +
        +
      • Enthält praktische Ratschläge und Werkzeuge
      • +
      • Teile könnten in “Format und Struktur” oder “Was macht eine gute Dokumentation aus” integriert werden
      • +
    12. +
    13. Implementierung aller Vorschläge als ready-to-use Repository: +
        +
      • Beschreibt die Umsetzung der Empfehlungen
      • +
      • Keine Überschneidung mit anderen Abschnitten
      • +
    14. +
    15. Fazit: +
        +
      • Zusammenfassung und Schlussfolgerung
      • +
      • Keine Überschneidung mit anderen Abschnitten
      • +
    16. +
    +
    +
    +
    +
    +
    + +
    +
    +Repsonse +
    +
    +
    +

    Einleitung: ok

    +

    Inhaltliche Anforderungen an die Dokumentation: ok

    +

    Format und Struktur der Dokumentation: ok

    +

    Was macht eine gute Dokumentation aus: “Nutzungshilfen außerhalb der Dokumentation” könnte in “Format und Struktur” übernommen werden

    +

    (Teil-)automatisierte Dokumentationswerkzeuge: ok

    +

    Best Practices, Vorlagen und Checklisten: “Checkliste für die Mindest-Dokumentation” könnte in “Inhaltliche Anforderungen” integriert werden

    +

    Implementierung aller Vorschläge als ready-to-use Repository: ok

    +

    Fazit: ok

    +
    +
    + + +
    + + + ]]>
    + Praxis + Beispiel + ML + Experience + https://drezil.de/Obsidian-Articles/Artikelbewertung durch LLM.html + Thu, 12 Jun 2025 22:00:00 GMT +
    Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities) https://drezil.de/Writing/documentation.html diff --git a/dist/listings.json b/dist/listings.json index 850bef1..2b68704 100644 --- a/dist/listings.json +++ b/dist/listings.json @@ -2,6 +2,7 @@ { "listing": "/index.html", "items": [ + "/Obsidian-Articles/Artikelbewertung durch LLM.html", "/Writing/documentation.html", "/Writing/ner4all-case-study.html", "/Writing/Obsidian-RAG.html", diff --git a/dist/search.json b/dist/search.json index 7a93ec6..2a91ec9 100644 --- a/dist/search.json +++ b/dist/search.json @@ -1,4 +1,18 @@ [ + { + "objectID": "index.html", + "href": "index.html", + "title": "Nicole Dresselhaus", + "section": "", + "text": "Unsortierte Einsichten und Erfahrungen. Archiviert zum verlinken, späteren Überdenken oder Diskutieren.\nKeine Garantie auf Richtigkeit oder Trollfreiheit :>" + }, + { + "objectID": "index.html#letzte-posts", + "href": "index.html#letzte-posts", + "title": "Nicole Dresselhaus", + "section": "Letzte Posts", + "text": "Letzte Posts" + }, { "objectID": "Writing/Obsidian-RAG.html", "href": "Writing/Obsidian-RAG.html", @@ -260,489 +274,133 @@ ] }, { - "objectID": "Uni/Lernerfolg_an_der_Uni.html", - "href": "Uni/Lernerfolg_an_der_Uni.html", - "title": "Wie lerne ich richtig an der Uni?", + "objectID": "Stuff/Bielefeldverschwoerung.html", + "href": "Stuff/Bielefeldverschwoerung.html", + "title": "Die Bielefeld-Verschwörung", "section": "", - "text": "Dies ist eine gute Frage. Da ich im laufe der Zeit einige Antworten gesammelt habe, wollte ich diese mal hier niederschreiben. Vorweg eine Warnung: 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. Da wir das nun aus dem Weg haben, geht es auch gleich los.", + "text": "Kopie des vermutlichen Originals von (vermutlich) Achim Held aus 1994.\nWarnung: 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!\nVielen Dank für die Beachtung aller Sicherheitsvorschriften.", "crumbs": [ "Home", - "Serious", - "Uni", - "Wie lerne ich richtig an der Uni?" + "Fun", + "Stuff", + "Die Bielefeld-Verschwörung" ] }, { - "objectID": "Uni/Lernerfolg_an_der_Uni.html#uni-ist-nicht-schule", - "href": "Uni/Lernerfolg_an_der_Uni.html#uni-ist-nicht-schule", - "title": "Wie lerne ich richtig an der Uni?", - "section": "Uni ist nicht Schule", - "text": "Uni ist nicht Schule\nEinige 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).", + "objectID": "Stuff/Bielefeldverschwoerung.html#die-geschichte-der-entdeckung", + "href": "Stuff/Bielefeldverschwoerung.html#die-geschichte-der-entdeckung", + "title": "Die Bielefeld-Verschwörung", + "section": "Die Geschichte der Entdeckung", + "text": "Die Geschichte der Entdeckung\nVor 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.\nDann, 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\nDer 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.\nAber 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 “Stadtinfo über Bielefeld” 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.\nEin 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.\nDie 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.\nInzwischen 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:\n“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.”", "crumbs": [ "Home", - "Serious", - "Uni", - "Wie lerne ich richtig an der Uni?" + "Fun", + "Stuff", + "Die Bielefeld-Verschwörung" ] }, { - "objectID": "Uni/Lernerfolg_an_der_Uni.html#etwas-lerntheorie", - "href": "Uni/Lernerfolg_an_der_Uni.html#etwas-lerntheorie", - "title": "Wie lerne ich richtig an der Uni?", - "section": "Etwas Lerntheorie", - "text": "Etwas Lerntheorie\nEs gibt einen sehr schönen Talk 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.\n\nAuswendig lernen\nGrade 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: Anki 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.\nIch 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.\nMan 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).\n\n\nMethodik lernen\nIm 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.\nUm 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”.\nKleines Beispiel aus der Welt der Mathematik:\nVektorraum -> Ist zu einer Basis definiert\n -> Basis ist die größtmögliche Zahl lin. unabh. Vektoren. Lin. Hülle der Basis ist der VR\n -> Lin. Hülle ist jede Lin.-Komb. von Vektoren\n -> Hat eine Vektoraddition und skalare Multiplikation\n -> Wird über einem Körper aufgespannt\n -> Körper sind 2 abelsche Gruppen mit Distributivgesetz\n -> abelsche Gruppe ist Menge mit K.A.I.N.\n -> ....\nSo 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.", + "objectID": "Stuff/Bielefeldverschwoerung.html#was-steckt-dahinter", + "href": "Stuff/Bielefeldverschwoerung.html#was-steckt-dahinter", + "title": "Die Bielefeld-Verschwörung", + "section": "Was steckt dahinter?", + "text": "Was steckt dahinter?\nDies 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:\n\nEs 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.\nAn 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.\nDer 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.\nAn 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.", "crumbs": [ "Home", - "Serious", - "Uni", - "Wie lerne ich richtig an der Uni?" + "Fun", + "Stuff", + "Die Bielefeld-Verschwörung" ] }, { - "objectID": "Uni/Lernerfolg_an_der_Uni.html#vorbereiten-auf-eine-klausur", - "href": "Uni/Lernerfolg_an_der_Uni.html#vorbereiten-auf-eine-klausur", - "title": "Wie lerne ich richtig an der Uni?", - "section": "Vorbereiten auf eine Klausur", - "text": "Vorbereiten auf eine Klausur\n\nHerausfinden, um was für eine Art von Klausur es sich handelt\n\nAnkreuzklausur?\nAuswendiglern-Klausur?\nPraktische Klausur (z.b. fast 1:1 Übungsaufgaben, feste Schema, ..)?\nOpen-Book?\nAnnotation von Grafiken?\n\nKlausuren von der Fachschaft organisieren\n\nFalls keine Vorhanden: Altfachschaftler fragen, wie die Klausur bei ihnen war\nNeue Klausur mit in die FS bringen, falls möglich (z.b. schreiend rausrennen und Klausur dabei mitnehmen, bevor man offiziell registriert wurde)\n\n\nJe nach Klausurtyp dann mit Anki stumpf Karten machen und auswendig lernen (z.b. Ankreuzklausur, Grafik-annotations-Klausur, ..) oder Übungsaufgaben/Altklausuren durchrechnen", + "objectID": "Stuff/Bielefeldverschwoerung.html#was-können-wir-tun", + "href": "Stuff/Bielefeldverschwoerung.html#was-können-wir-tun", + "title": "Die Bielefeld-Verschwörung", + "section": "Was können wir tun?", + "text": "Was können wir tun?\nZum 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.\nEine 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.\nAb 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.\nDas, 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: Bielefeld gibt es nicht!!!", "crumbs": [ "Home", - "Serious", - "Uni", - "Wie lerne ich richtig an der Uni?" + "Fun", + "Stuff", + "Die Bielefeld-Verschwörung" ] }, { - "objectID": "Uni/Lernerfolg_an_der_Uni.html#vorbereiten-auf-eine-mündliche-prüfung", - "href": "Uni/Lernerfolg_an_der_Uni.html#vorbereiten-auf-eine-mündliche-prüfung", - "title": "Wie lerne ich richtig an der Uni?", - "section": "Vorbereiten auf eine mündliche Prüfung", - "text": "Vorbereiten auf eine mündliche Prüfung\n\nProtokolle aus der Fachschaft organisieren\n\nHäufig gegen Pfand, dass man bei Abgabe eines Protokolls wieder bekommt\nWenn keins vorhanden für die nachfolgede Generation eins ausfüllen\n\n\nWenn 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).\n\nUnterschiede mündliche Bachelor/Master-Prüfungen\nEinige 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.\nBeispiel: 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.\nDies 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.", - "crumbs": [ - "Home", - "Serious", - "Uni", - "Wie lerne ich richtig an der Uni?" - ] - }, - { - "objectID": "Uni/Lernerfolg_an_der_Uni.html#prüfungsangt", - "href": "Uni/Lernerfolg_an_der_Uni.html#prüfungsangt", - "title": "Wie lerne ich richtig an der Uni?", - "section": "Prüfungsangt", - "text": "Prüfungsangt\nZu 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 ;)\nEs 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).\nDie 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.\nEs 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.", - "crumbs": [ - "Home", - "Serious", - "Uni", - "Wie lerne ich richtig an der Uni?" - ] - }, - { - "objectID": "Uni/Lernerfolg_an_der_Uni.html#schlusswort", - "href": "Uni/Lernerfolg_an_der_Uni.html#schlusswort", - "title": "Wie lerne ich richtig an der Uni?", - "section": "Schlusswort", - "text": "Schlusswort\nViel Erfolg bei euren Prüfungen. Falls euch dieser Artikel geholfen hat oder ihr noch Anregungen/Verbessenguswünsche habt, schreibt mir einfach.", - "crumbs": [ - "Home", - "Serious", - "Uni", - "Wie lerne ich richtig an der Uni?" - ] - }, - { - "objectID": "Opinions/Keyboard-Layout.html", - "href": "Opinions/Keyboard-Layout.html", - "title": "Keyboard-Layout", + "objectID": "Opinions/Editors.html", + "href": "Opinions/Editors.html", + "title": "Editors", "section": "", - "text": "Since around 2006 i basically write only using the NEO2-Layout. There are many advantages that are not obvious to an onlooker right away.\nDon’t get me wrong. I still can type QWERTZ - just because you learn an additional layout does not mean that you forget everything from before.\nThe secret sauce lies in the deeper layers. Especially layer 3 having all the “hard to reach” things like brackets, braces, etc. right on the home row. And the 4th layer is magic for text-navigation. Left hand has the full navigation, right hand has the complete Numpad - even on laptop-keyboards that are lacking those.\nFor me as a person having the usual German Keyboard with AltGr this just means:\n\nPutting the thumb down on AltGr - it is above there anyway.\nUse left hand as normal arrow-keys (that work EVERYWHERE because they are just arrow keys)\nAlso use Home/End/PgUp/PgDown/…\n\nBefore i always had to switch over or hope that a thing had support for vi-style “hjkl”.\nThat’s why i also prefer Neovim as my primary editor - just not having to touch your mouse at any time for anything is such a godsend :)\nBest thing: If you don’t want to switch, there is also a “Neo-QWERTZ”-variant .. where you can just try the deeper layers while not leaving your QWERTZ-layout behind. But i have just seen and never tried it. Your experience may be sub-par.", + "text": "Better said: “neovim is currently the best™ editor” ;)\n\n\nYou can find my current Config along with other things in my gitea snippet-git.", "crumbs": [ "Home", "Fun", "Opinions", - "Keyboard-Layout" + "Editors" ] }, { - "objectID": "Coding/Haskell/Advantages.html", - "href": "Coding/Haskell/Advantages.html", - "title": "Talks und Posts zu Haskell", + "objectID": "Opinions/Editors.html#editors", + "href": "Opinions/Editors.html#editors", + "title": "Editors", "section": "", - "text": "Gründe Haskell zu nutzen und wo Vorteile liegen.\n\n\n\nThe Future is parallel\nLenses (Registrierung nötig - kostenfrei), siehe auch: Lenses\nRunning a Startup on Haskell\nWe’re doing it all wrong - A Long-Term Scala-Compiler-Developer quits his job after years and tells why Scala is a mess.\nMonads explained in Javascript\nVinyl Records with Slides\nThinking with Laziness\n\n\n\n\n\nPapers on STM\nTackling the awkward squad\nParallel and Concurrent Programming in Haskell\nSlides of a Quickcheck-Talk\nUnderstanding F-Algebras schöne Erklärung. Man könnte danach anfangen den Morphismen-zoo zu verstehen…\nMonad Transformers\n\n\n\n\n\nTom LaGatta on Category-Theory\nUnifying Structured Recursion Schemes aka. [[Morphisms|The Morphism-Zoo]]\nHole-Driven-Development Teaser (Enthusiasticon, raichoo)\n\n\n\n\n\nFunctional Reactive Programming\nDiagrams: Declarative Vector Graphics in Haskell\nLenses, Folds & Traversels by Edward Kmett\nDiscrimination is Wrong by Edward Kmett\n\n\n\n\n\nHaskell fast and hard\nCounterexamples for Typeclasses", + "text": "Better said: “neovim is currently the best™ editor” ;)\n\n\nYou can find my current Config along with other things in my gitea snippet-git.", + "crumbs": [ + "Home", + "Fun", + "Opinions", + "Editors" + ] + }, + { + "objectID": "Opinions/Editors.html#references", + "href": "Opinions/Editors.html#references", + "title": "Editors", + "section": "References", + "text": "References\n\nLearning Vim in 2014: Vim as Language - Herding Lions\nvi - What is your most productive shortcut with Vim? - Stack Overflow", + "crumbs": [ + "Home", + "Fun", + "Opinions", + "Editors" + ] + }, + { + "objectID": "Health/Issues.html", + "href": "Health/Issues.html", + "title": "Mental Health", + "section": "", + "text": "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.\nSo even despite my exceptional1 successes that can be seen in my work 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 summary of my experiences or the awesome things i did at university. 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.\nOnly thanks to the not-that-awful system in Germany and massive financial help from my parents i was even able to pursue this way.", "crumbs": [ "Home", "Serious", - "Coding", - "Haskell", - "Talks und Posts zu Haskell" + "Health", + "Mental Health" ] }, { - "objectID": "Coding/Haskell/Advantages.html#talks", - "href": "Coding/Haskell/Advantages.html#talks", - "title": "Talks und Posts zu Haskell", - "section": "", - "text": "The Future is parallel\nLenses (Registrierung nötig - kostenfrei), siehe auch: Lenses\nRunning a Startup on Haskell\nWe’re doing it all wrong - A Long-Term Scala-Compiler-Developer quits his job after years and tells why Scala is a mess.\nMonads explained in Javascript\nVinyl Records with Slides\nThinking with Laziness", + "objectID": "Health/Issues.html#what-are-my-issues", + "href": "Health/Issues.html#what-are-my-issues", + "title": "Mental Health", + "section": "What are my issues?", + "text": "What are my issues?\nWell.. 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.\nCurrently i am successfully diagnosed with\n\nsocial anxiety\nADHD\ntransgenderism\n\nand i got a big suspician of\n\nautism\n\nAll 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.\nThat’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.\nToday i finally found myself. I currently have a great partner (with 3 kids) - and all 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.\nI 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.", "crumbs": [ "Home", "Serious", - "Coding", - "Haskell", - "Talks und Posts zu Haskell" + "Health", + "Mental Health" ] }, { - "objectID": "Coding/Haskell/Advantages.html#bücherpaper", - "href": "Coding/Haskell/Advantages.html#bücherpaper", - "title": "Talks und Posts zu Haskell", - "section": "", - "text": "Papers on STM\nTackling the awkward squad\nParallel and Concurrent Programming in Haskell\nSlides of a Quickcheck-Talk\nUnderstanding F-Algebras schöne Erklärung. Man könnte danach anfangen den Morphismen-zoo zu verstehen…\nMonad Transformers", + "objectID": "Health/Issues.html#the-early-days", + "href": "Health/Issues.html#the-early-days", + "title": "Mental Health", + "section": "The early days", + "text": "The early days\nTo be continued …", "crumbs": [ "Home", "Serious", - "Coding", - "Haskell", - "Talks und Posts zu Haskell" - ] - }, - { - "objectID": "Coding/Haskell/Advantages.html#funny-talks", - "href": "Coding/Haskell/Advantages.html#funny-talks", - "title": "Talks und Posts zu Haskell", - "section": "", - "text": "Tom LaGatta on Category-Theory\nUnifying Structured Recursion Schemes aka. [[Morphisms|The Morphism-Zoo]]\nHole-Driven-Development Teaser (Enthusiasticon, raichoo)", - "crumbs": [ - "Home", - "Serious", - "Coding", - "Haskell", - "Talks und Posts zu Haskell" - ] - }, - { - "objectID": "Coding/Haskell/Advantages.html#unsortedunseen", - "href": "Coding/Haskell/Advantages.html#unsortedunseen", - "title": "Talks und Posts zu Haskell", - "section": "", - "text": "Functional Reactive Programming\nDiagrams: Declarative Vector Graphics in Haskell\nLenses, Folds & Traversels by Edward Kmett\nDiscrimination is Wrong by Edward Kmett", - "crumbs": [ - "Home", - "Serious", - "Coding", - "Haskell", - "Talks und Posts zu Haskell" - ] - }, - { - "objectID": "Coding/Haskell/Advantages.html#tutorials", - "href": "Coding/Haskell/Advantages.html#tutorials", - "title": "Talks und Posts zu Haskell", - "section": "", - "text": "Haskell fast and hard\nCounterexamples for Typeclasses", - "crumbs": [ - "Home", - "Serious", - "Coding", - "Haskell", - "Talks und Posts zu Haskell" - ] - }, - { - "objectID": "Coding/Haskell/FFPiH.html", - "href": "Coding/Haskell/FFPiH.html", - "title": "Fortgeschrittene funktionale Programmierung in Haskell", - "section": "", - "text": "FFPiH ist eine Vorlesung, die ich zusammen mit einem Kommilitonen im Sommer 2015 erstmals erstellt und gehalten haben.\nInsgesamt 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.\nDie gesamten Übungen sind unter anderem in der FFPiH-Organisation in meinem gitea hinterlegt: https://gitea.dresselhaus.cloud/FFPiH\nEinige der aktualisierten Übungen sind privat geschaltet, da diese iterativ aufeinander aufbauen und jeweils die Musterlösung der vorherigen enthalten.", - "crumbs": [ - "Home", - "Serious", - "Coding", - "Haskell", - "Fortgeschrittene funktionale Programmierung in Haskell" - ] - }, - { - "objectID": "Coding/Haskell/FFPiH.html#aufbau-der-vorlesung", - "href": "Coding/Haskell/FFPiH.html#aufbau-der-vorlesung", - "title": "Fortgeschrittene funktionale Programmierung in Haskell", - "section": "Aufbau der Vorlesung", - "text": "Aufbau der Vorlesung\nVorausgesetzt 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 wirklich erklärt bekommen haben).\nStü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.\nSchlussendlich 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.\nOptional gab es weitere Übungen zu dingen wie “verteiltes Rechnen”.\nZiel 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.", - "crumbs": [ - "Home", - "Serious", - "Coding", - "Haskell", - "Fortgeschrittene funktionale Programmierung in Haskell" - ] - }, - { - "objectID": "Coding/Haskell/FFPiH.html#studentisches-feedback", - "href": "Coding/Haskell/FFPiH.html#studentisches-feedback", - "title": "Fortgeschrittene funktionale Programmierung in Haskell", - "section": "Studentisches Feedback", - "text": "Studentisches Feedback\nSehr gutes Feedback von den Studenten bekamen wir insbesondere für Übungen wie:\nÜbung 2, Aufgabe 2, 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.\nÜbung 4, 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:\n\nEinarbeitung in QuickCheck zur Behebung eines Bugs im Test\nUmschreiben von explizitem Argument-Passing hin zu Monad-Transformers mit stateful Lenses\nContinuation-Basierendes Event-System\nHinzufügen eines Parsers für Level, Items & deren Effekte und implementation dieser\nÄndern des GUI-Parts von CLI auf 2D GL mittels gloss\nÄndern von StateT World auf RWST GameConfig Log World und somit nutzen von individuellen Konfigurationen für z.b. Keybindings", - "crumbs": [ - "Home", - "Serious", - "Coding", - "Haskell", - "Fortgeschrittene funktionale Programmierung in Haskell" - ] - }, - { - "objectID": "Coding/Haskell/Webapp-Example/MyService_Types.hs.html", - "href": "Coding/Haskell/Webapp-Example/MyService_Types.hs.html", - "title": "Webapp-Example: MyService/Types.hs", - "section": "", - "text": "Anleitung siehe Webapp-Example.\n{-# OPTIONS_GHC -Wno-orphans #-}\n{-# OPTIONS_GHC -Wno-name-shadowing #-}\n{-# LANGUAGE DeriveAnyClass #-}\n{-# LANGUAGE DeriveFunctor #-}\n{-# LANGUAGE DeriveGeneric #-}\n{-# LANGUAGE DerivingVia #-}\n{-# LANGUAGE DuplicateRecordFields #-}\n{-# LANGUAGE FlexibleContexts #-}\n{-# LANGUAGE FlexibleInstances #-}\n{-# LANGUAGE GADTs #-}\n{-# LANGUAGE LambdaCase #-}\n{-# LANGUAGE MultiParamTypeClasses #-}\n{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE RankNTypes #-}\n{-# LANGUAGE RecordWildCards #-}\nmodule MyService.Types where\n\nimport Data.Aeson (FromJSON, ToJSON)\nimport Data.Text\nimport Data.Time.Clock\nimport GHC.Generics\nimport System.Envy\nimport Text.PrettyPrint (text)\nimport Text.PrettyPrint.GenericPretty\n\n-- Out hat hierfür keine Instanzen, daher kurz eine einfach Definition.\ninstance Out Text where\n doc = text . unpack\n docPrec i a = text $ showsPrec i a \"\"\n\ninstance Out UTCTime where\n doc = text . show\n docPrec i a = text $ showsPrec i a \"\"\n\n-- 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.\ndata ServerConfig = ServerConfig\n { myserviceHost :: String -- ^ Environment: $MYSERVICE_HOST\n , myservicePort :: Int -- ^ Environment: $MYSERVICE_PORT\n , myserviceMaxTimeout :: Int -- ^ Environment: $MYSERVICE_MAX_TIMEOUT\n , myserviceInternalProxyUrl :: String -- ^ Environment: $MYSERVICE_INTERNAL_PROXY_URL\n , myserviceInternalProxyPort :: Int -- ^ Environment: $MYSERVICE_INTERNAL_PROXY_PORT\n , myserviceExternalProxyUrl :: String -- ^ Environment: $MYSERVICE_EXTERNAL_PROXY_URL\n , myserviceExternalProxyPort :: Int -- ^ Environment: $MYSERVICE_EXTERNAL_PROXY_PORT\n , myserviceActivemqHost :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_HOST\n , myserviceActivemqPort :: Int -- ^ Environment: $MYSERVICE_ACTIVEMQ_PORT\n , myserviceActivemqUsername :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_USERNAME\n , myserviceActivemqPassword :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_PASSWORD\n , myserviceMongoUsername :: String -- ^ Environment: $MYSERVICE_MONGO_USERNAME\n , myserviceMongoPassword :: String -- ^ Environment: $MYSERVICE_MONGO_PASSWORD\n , myserviceDebug :: Bool -- ^ Environment: $MYSERVICE_DEBUG\n } deriving (Show, Eq, Generic)\n\n-- Default-Konfigurations-Instanz für diesen Service.\ninstance DefConfig ServerConfig where\n defConfig = ServerConfig \"0.0.0.0\" 8080 20\n \"\"\n \"\"\n \"\"\n 0\n \"\"\n 0\n \"\"\n 0\n \"\"\n \"\"\n \"\"\n \"\"\n False\n\n-- Kann auch aus dem ENV gefüllt werden\ninstance FromEnv ServerConfig\n-- Und hübsch ausgegeben werden.\ninstance Out ServerConfig\n\n\ninstance Out Response\ninstance FromBSON Repsonse -- FromBSON-Instanz geht immer davon aus, dass alle keys da sind (ggf. mit null bei Nothing).", - "crumbs": [ - "Home", - "Serious", - "Coding", - "Haskell", - "Webapp-Development in Haskell", - "Webapp-Example: MyService/Types.hs" - ] - }, - { - "objectID": "Coding/Haskell/Code Snippets/Morphisms.html", - "href": "Coding/Haskell/Code Snippets/Morphisms.html", - "title": "*-Morpisms", - "section": "", - "text": "Note\n\n\n\nBackup eines Blogposts eines Kommilitonen\n\n\nThis weekend I spend some time on Morphisms.\nKnowing that this might sound daunting to many dabbling Haskellers (like I am), I decided to write a real short MergeSort hylomorphism quickstarter.\n\nFor 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.\n\nFirst the usual prelude:\n{-# LANGUAGE DeriveFunctor #-}\n{-# LANGUAGE TypeFamilies #-}\n\nimport Data.Functor.Foldable\nimport Data.List (splitAt, unfoldr)\n\nWe will use a binary tree like this. Note that there is no explicit recursion used, but NodeF has two holes. These will eventually filled later.\ndata TreeF c f = EmptyF | LeafF c | NodeF f f\n deriving (Eq, Show, Functor)\n\nAside: We could use this as a normal binary tree by wrapping it in Fix: type Tree a = Fix (TreeF a) But this would require us to write our tree like Fix (NodeF (Fix (LeafF 'l')) (Fix (LeafF 'r'))) which would get tedious fast. Luckily Edward build a much better way to do this into recursion-schemes. I will touch on this later.\n\nWithout 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”.\nunflatten :: [a] -> TreeF a [a]\nunflatten ( []) = EmptyF\nunflatten (x:[]) = LeafF x\nunflatten ( xs) = NodeF l r where (l,r) = splitAt (length xs `div` 2) xs\nFrom 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.\nThe 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.\n\nAside: At this point we could use this Coalgebra to construct (unsorted) binary trees from lists:\nexample1 = ana unflatten [1,3] == Fix (NodeF (Fix (LeafF 1)) (Fix (LeafF 3)))\n\nOn to our sorting, tree-collapsing Algebra. Which again is just a creepy word for “function that is used to deconstruct datastructures”.\nThe function mergeList is defined below and just merges two sorted lists into one sorted list in O(n), I would probably take this from the ordlist package if I were to implement this for real.\nAgain we see that we can just construct our sorted output list from a TreeF that apparently contains just lists.\nflatten :: Ord a => TreeF a [a] -> [a]\nflatten EmptyF = []\nflatten (LeafF c) = [c]\nflatten (NodeF l r) = mergeLists l r\n\nAside: We could use a Coalgebra to deconstruct trees:\nexample2 = cata flatten (Fix (NodeF (Fix (LeafF 3)) (Fix (LeafF 1)))) == [1,3]\n\nNow we just combine the Coalgebra and the Algebra with one from the functions from Edwards recursion-schemes library:\nmergeSort :: Ord a => [a] -> [a]\nmergeSort = hylo flatten unflatten\n\nexample3 = mergeSort [5,2,7,9,1,4] == [1,2,4,5,7,9]\n\nWhat have we gained?\nWe have implemented a MergeSort variant in 9 lines of code, not counting the mergeLists function below. Not bad, but this implementation is not much longer.\nOn the other hand the morphism based implementation cleanly describes what happens during construction and deconstruction of our intermediate structure.\nMy guess is that, as soon as the algortihms get more complex, this will really make a difference.\n\nAt 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.\nIt 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):\n mergeSort :: [Integer] -> [Integer]\n mergeSort =\n \\ (x :: [Integer]) ->\n case x of wild {\n [] -> [];\n : x1 ds ->\n case ds of _ {\n [] -> : x1 ([]);\n : ipv ipv1 ->\n unfoldr\n lvl9\n (let {\n p :: ([Integer], [Integer])\n p =\n case $wlenAcc wild 0 of ww { __DEFAULT ->\n case divInt# ww 2 of ww4 { __DEFAULT ->\n case tagToEnum# (<# ww4 0) of _ {\n False ->\n case $wsplitAt# ww4 wild of _ { (# ww2, ww3 #) -> (ww2, ww3) };\n True -> ([], wild)\n }\n }\n } } in\n (case p of _ { (x2, ds1) -> mergeSort x2 },\n case p of _ { (ds1, y) -> mergeSort y }))\n }\n }\n end Rec }\nWhile 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.\nThis 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.\n\nAftermath:\nIn the beginning I promised a way to work on non-functor data structures. Actually that was how I began to work with the recursion-schemes library.\nWe are able to create a ‘normal’ version of our tree from above:\ndata Tree c = Empty | Leaf c | Node (Tree c) (Tree c)\n deriving (Eq, Show)\nBut we can not use this directly with our (Co-)Algebras. Luckily Edward build a little bit of type magic into the library:\ntype instance Base (Tree c) = (TreeF c)\n\ninstance Unfoldable (Tree c) where\n embed EmptyF = Empty\n embed (LeafF c) = Leaf c\n embed (NodeF l r) = Node l r\n\ninstance Foldable (Tree c) where\n project Empty = EmptyF\n project (Leaf c) = LeafF c\n project (Node l r) = NodeF l r\nWithout going into detail by doing this we establish a relationship between Tree and TreeF and teach the compiler how to translate between these types.\nNow we can use our Alebra on our non functor type:\nexample4 = cata flatten (Node (Leaf 'l') (Leaf 'r')) == \"lr\"\nThe great thing about this is that, looking at the Core output again, there is no traces of the TreeF structure to be found. As far as I can tell, the algorithm is working directly on our Tree type.\n\nLiterature:\n\nUnderstanding F-Algebras\nRecursion Schemes by Example\nRecursion Schemes: A Field Guide\nThis StackOverflow question\n\n\nAppendix:\nmergeLists :: Ord a => [a] -> [a] -> [a]\nmergeLists = curry $ unfoldr c where\n c ([], []) = Nothing\n c ([], y:ys) = Just (y, ([], ys))\n c (x:xs, []) = Just (x, (xs, []))\n c (x:xs, y:ys) | x <= y = Just (x, (xs, y:ys))\n | x > y = Just (y, (x:xs, ys))", - "crumbs": [ - "Home", - "Serious", - "Coding", - "Haskell", - "Code Snippets", - "*-Morpisms" - ] - }, - { - "objectID": "About/index.html", - "href": "About/index.html", - "title": "About me", - "section": "", - "text": "March 2024 to September 2028:\n\nResearch-Software-Engineer at “Digital History” workgroup at HU Berlin\nPart of NFDI4Memory, Task Area 5 “Data Culture”\nPhD-ing on the side\n\nJanuary 2024 to February 2024:\n\nWorked for yarvis\nFirst fulltime-developer; Responsive Webapp with react\n\nMarch 2023 to September 2023:\n\nWorked for 2lambda\nSilicon Valley start-up trying to beat the stock-market with fancy ML-Models\nThat work kickstarted my employment at Red Queen UG where i continue doing consulting work for 2lambda while also moving into a more senior role of also building up our own team of specialists to work on different future projects.\n\nOct. 2018 to Aug. 2021:\n\nML-Specialist at Jobware (Paderborn; german Job-Advertising-Platform)\n\n2013-2018 several jobs at my University including\n\nWorked 6 Months in the Workgroup “Theoretical Computer Science” on migrating algorithms to CUDA\nTutor “Introduction to Machine Learning”\n\nWas awarded Tutoring-Award of the Faculty of Technology for excellent tutoring\n\nLecture “Intermediate Functional Programming in Haskell”\nDevelopment of Pandoc-Filters for effective generation of lecture-slides for Mario Botsch (Leader Workgroup Computer Graphics) using Pandoc & reveal.js", - "crumbs": [ - "Home", - "Info", - "About me" - ] - }, - { - "objectID": "About/index.html#work", - "href": "About/index.html#work", - "title": "About me", - "section": "", - "text": "March 2024 to September 2028:\n\nResearch-Software-Engineer at “Digital History” workgroup at HU Berlin\nPart of NFDI4Memory, Task Area 5 “Data Culture”\nPhD-ing on the side\n\nJanuary 2024 to February 2024:\n\nWorked for yarvis\nFirst fulltime-developer; Responsive Webapp with react\n\nMarch 2023 to September 2023:\n\nWorked for 2lambda\nSilicon Valley start-up trying to beat the stock-market with fancy ML-Models\nThat work kickstarted my employment at Red Queen UG where i continue doing consulting work for 2lambda while also moving into a more senior role of also building up our own team of specialists to work on different future projects.\n\nOct. 2018 to Aug. 2021:\n\nML-Specialist at Jobware (Paderborn; german Job-Advertising-Platform)\n\n2013-2018 several jobs at my University including\n\nWorked 6 Months in the Workgroup “Theoretical Computer Science” on migrating algorithms to CUDA\nTutor “Introduction to Machine Learning”\n\nWas awarded Tutoring-Award of the Faculty of Technology for excellent tutoring\n\nLecture “Intermediate Functional Programming in Haskell”\nDevelopment of Pandoc-Filters for effective generation of lecture-slides for Mario Botsch (Leader Workgroup Computer Graphics) using Pandoc & reveal.js", - "crumbs": [ - "Home", - "Info", - "About me" - ] - }, - { - "objectID": "About/index.html#education", - "href": "About/index.html#education", - "title": "About me", - "section": "Education", - "text": "Education\n\nBachelor “Kognitive Informatik” (Cognitive Informatics) in Bielefeld 2010-2014\nMaster “Naturwissenschaftliche Informatik” (Informatics in the natural sciences) 2014-2018\n\n\nExtraordinary grades (Excerpt of my Transcript)\nNote: Scale of grades in Germany is 1.0 to 4.0 with 1.0 being best, 4.0 being passing grade, 5.0 being failed grade\n\n1.0 in Modern Data Analysis\n\nMaster course on data-analysis (time-series, core-vector-machines, gaussian processes, …)\n\n1.0 in Computergraphics\n\nRaytracing, Modern OpenGL\n\n1.3 in Computer-Animation\n\nDual-Quarternion-Skinning, Character-Animation, FACS-Poses, etc.\n\n1.3 in GPU-Computing (CUDA)\n\noriginally a 1.7 by timing (task was de-mosaicing on images, grade was measured in ms, whereby 400ms equated to 4.0 and 100ms equated to 1.0), but because my deep knowledge was visible in the code i was given a 1.3 after oral presentation.\n\n1.0 in Parallel Algorithms and Data-Structures\nEthical Hacking\n\nReverse Engineering with IDApro", - "crumbs": [ - "Home", - "Info", - "About me" - ] - }, - { - "objectID": "About/index.html#further-information", - "href": "About/index.html#further-information", - "title": "About me", - "section": "Further information", - "text": "Further information\n\nMore details on my work-experience\nMore details of my coding\nMore details of things i did beside studying at University", - "crumbs": [ - "Home", - "Info", - "About me" - ] - }, - { - "objectID": "About/Work.html", - "href": "About/Work.html", - "title": "Work-Experience", - "section": "", - "text": "Work-Experience\n\nMar. 2023 to Sep. 2023:\n\nDeveloper for 2Lambda.co. Role migrated from just coding stuff to architecting and rewriting the whole software from the ground up using a small modular approach instead of the shaky one-off systems in place.\nWas later a “nanny for everything”.\nDid a lot of work to have self-documenting code (i.e. generate documentation from the actual values used in the program, not some comments that always get out of date)\nSetting up a knowledge-base (Zettelkasten-approach) to track experiments and hyperlink them to the documentation generated above (and due to Zettelkasten you then get “this thing was used in Experiments a, b and c” automatically\nTechnologies used:\n\nClojure\n\nComplete application was written in Clojure\nNever touched that language before March - got up to speed in just 2 days, poked the expert on the team detailed questions about the runtime-system after 1 month (like inlining-behavior, allocation-things, etc.)\n\nEmanote\n\nautogenerated & linked documentation of internal modules\nintegrated with manual written tutorials/notes\ncrosslinking documentation of experiments with documentation of modules\n\nWeb of knowledge\nbidirectional discovery of things tried/done in the past to optimize finding of new strategies (meta-optimizing the decisions on what to optimize/try)\n\n\nInfrastructure\n\nOrganized and co-administrated the 4 Root-Servers we had\nSet up Kubernetes, Nexus, Docker, Nginx, letsencrypt-certs, dns-entries, etc..\n\n\n\nOct. 2018 to Aug. 2021:\n\nML-Specialist at Jobware (Paderborn; german Job-Advertising-Platform)\n\nExtraction/Classification of sentences from JobAds (Requirements, Benefits, Tasks, …)\nExtraction of Information from JobAds (Location of company, Location of workplay, contact-details, application-procedure, etc.) including geocoding of those information (backed by OpenStreetMap)\nEmbedding of JobAds into a meaningful space (i.e. “get me similar ads. btw. i dislike ad a, b, c”).\nAnalyse & predict search-queries of users on the webpage and offer likely but distinct queries (i.e. similar when typo or complete different words (synonyms, hyponyms, etc.))\n\nTechnologies used:\n\nHaskell (currently GHC 8.6, soon GHC 8.8)\n\nstack + stackage-lts\nfixplate (recursion-schemes-implementation)\nmany usual technologies like lens, http-simple, mtl, ..\ngolden-testing via tasty\nseveral inhouse-developments:\n\ntemplating based on text-replacement via generics (fieldname in Template-Type == variable replaced in template)\nactiveMQ/Kibana-bridge for logging via hs-stomp\ngeneric internal logging-framework\n\n\nPython\n\ntensorflow\npytorch\nsklearn\nnltk\n\n\n\n2013-2018:\n\nseveral jobs at my University including\n\nWorked 6 Months in the Workgroup “Theoretical Computer Science” on migrating algorithms to CUDA\nTutor “Introduction to Machine Learning”\n\nWas awarded Tutoring-Award of the Faculty of Technology for excellent tutoring\n\nLecture “[[FFPiH|Intermediate Functional Programming in Haskell]]”\n\nOriginally developed as student-project in cooperation with Jonas Betzendahl\nFirst held in Summer 2015\nDue to high demand held again in Summer 2016 and 2017\nWas awarded Lecturer-Award “silver Chalk” in 2016\n\nFirst time that this award was given to students\nMany lecturers at our faculty never get any teaching-award until retirement\n\n\nDevelopment of Pandoc-Filters for effective generation of lecture-slides for Mario Botsch (Leader “Workgroup Computer Graphics”) using Pandoc & reveal.js\n\nFramework: https://github.com/mbotsch/revealSlides\nExample: https://github.com/mbotsch/eLearning\nPandoc-Filters: https://github.com/mbotsch/pandoc-slide-filter", - "crumbs": [ - "Home", - "Info", - "About me", - "Work-Experience" - ] - }, - { - "objectID": "About/Experience.html", - "href": "About/Experience.html", - "title": "Highlights of my experiences in the programming world", - "section": "", - "text": "(as far as NDA and other things allow it)\n\n\n\nLearning/Writing Haskell since ~2014\nCreated and held advanced Haskell-Lecture at my University\n\n\n\n\nMy Profile\nHaskell-Lecture\nCo-Founder of DataHaskell\n\n\n\n\n\nI also have a gitea-instance where one can finde more current things and backups of old.\n\n\n\nAuthor of Eve-Online-Interface in yesod-auth-oauth2\nAuthor of “New Eden Accounting Tool” (neat), which is basically a ledger for Trading in the game Eve-Online\nDriver behind getting https://github.com/jgm/pandoc/issues/168 implemented and merged, because we needed it for our slide-filters (see [[Work]]# -> Development of Filters)\nAuthor of img2ascii - Small cli-tool for converting images into terminal-codes & ascii using JuicyPixels, because i always forget what is on the images over an ssh-connection -.-\nImplemented Array-Fusion and Recycling for subhask as layed out in Recycle your Arrays by Roman Leshchinskiy\nRaytracer in Haskell for my Computergraphics-Course\nimplementation of Densely Connected Bi-Clusters-Algorithm in Haskell (Paper)\nChemodiversity-Project at University during my masters. Complete with slideshow explaining everything.\nseveral other dead projects :D", - "crumbs": [ - "Home", - "Info", - "About me", - "Highlights of my experiences in the programming world" - ] - }, - { - "objectID": "About/Experience.html#haskell-enthusiast", - "href": "About/Experience.html#haskell-enthusiast", - "title": "Highlights of my experiences in the programming world", - "section": "", - "text": "Learning/Writing Haskell since ~2014\nCreated and held advanced Haskell-Lecture at my University\n\n\n\n\nMy Profile\nHaskell-Lecture\nCo-Founder of DataHaskell", - "crumbs": [ - "Home", - "Info", - "About me", - "Highlights of my experiences in the programming world" - ] - }, - { - "objectID": "About/Experience.html#gitea", - "href": "About/Experience.html#gitea", - "title": "Highlights of my experiences in the programming world", - "section": "", - "text": "I also have a gitea-instance where one can finde more current things and backups of old.\n\n\n\nAuthor of Eve-Online-Interface in yesod-auth-oauth2\nAuthor of “New Eden Accounting Tool” (neat), which is basically a ledger for Trading in the game Eve-Online\nDriver behind getting https://github.com/jgm/pandoc/issues/168 implemented and merged, because we needed it for our slide-filters (see [[Work]]# -> Development of Filters)\nAuthor of img2ascii - Small cli-tool for converting images into terminal-codes & ascii using JuicyPixels, because i always forget what is on the images over an ssh-connection -.-\nImplemented Array-Fusion and Recycling for subhask as layed out in Recycle your Arrays by Roman Leshchinskiy\nRaytracer in Haskell for my Computergraphics-Course\nimplementation of Densely Connected Bi-Clusters-Algorithm in Haskell (Paper)\nChemodiversity-Project at University during my masters. Complete with slideshow explaining everything.\nseveral other dead projects :D", - "crumbs": [ - "Home", - "Info", - "About me", - "Highlights of my experiences in the programming world" - ] - }, - { - "objectID": "About/Extracurricular.html", - "href": "About/Extracurricular.html", - "title": "Studium generale / University-Life", - "section": "", - "text": "(What I did at university besides studying :sunglasses: )\n\n\n\nStudent Member of Studienbeirat Informatik (Study-Profile Commission)\nStudent Member of Tutorenauswahlkommission (Tutor-Selection Committee)\n\nLeader Tutorenevaluation (Evaluation of Tutors)\n\nStudent Member of NWI-Master-Auswahlausschuss (Master-Application Committee for my course of study)\nStudent Member of NWI-Master-Prüfungsausschuss (Committee for Exam-disputes of my Master course)\nMember of the Admin-Team for the student-body pcs\n\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nLecture on YouTube\n[[FFPiH|more details on the lecture]]#\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nLecture on YouTube (differs from link above)\nThis was the “silver chalk”-lecture\n[[FFPiH|more details on the lecture]]#\n\n\n\n\n\n\nRichtig Starten (Start Right!)\nTutor Introduction to Machine Learning (Tutor in this Lecture)\n\nWas awarded Tutoring-Award of the faculty\n\nRemade and updated slides for Computergraphics-Lecture\n\nLecture was awarded “silver chalk” among others things because of the updated slides.\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nSame as Summer 16\nTotally reworked Exercises accompanying the lecture\n[[FFPiH|more details on the lecture]]#", - "crumbs": [ - "Home", - "Info", - "About me", - "Studium generale / University-Life" - ] - }, - { - "objectID": "About/Extracurricular.html#committees-student-body", - "href": "About/Extracurricular.html#committees-student-body", - "title": "Studium generale / University-Life", - "section": "", - "text": "Student Member of Studienbeirat Informatik (Study-Profile Commission)\nStudent Member of Tutorenauswahlkommission (Tutor-Selection Committee)\n\nLeader Tutorenevaluation (Evaluation of Tutors)\n\nStudent Member of NWI-Master-Auswahlausschuss (Master-Application Committee for my course of study)\nStudent Member of NWI-Master-Prüfungsausschuss (Committee for Exam-disputes of my Master course)\nMember of the Admin-Team for the student-body pcs", - "crumbs": [ - "Home", - "Info", - "About me", - "Studium generale / University-Life" - ] - }, - { - "objectID": "About/Extracurricular.html#ekvv-links-entries-in-the-electronic-course-catalog", - "href": "About/Extracurricular.html#ekvv-links-entries-in-the-electronic-course-catalog", - "title": "Studium generale / University-Life", - "section": "", - "text": "Fortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nLecture on YouTube\n[[FFPiH|more details on the lecture]]#\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nLecture on YouTube (differs from link above)\nThis was the “silver chalk”-lecture\n[[FFPiH|more details on the lecture]]#\n\n\n\n\n\n\nRichtig Starten (Start Right!)\nTutor Introduction to Machine Learning (Tutor in this Lecture)\n\nWas awarded Tutoring-Award of the faculty\n\nRemade and updated slides for Computergraphics-Lecture\n\nLecture was awarded “silver chalk” among others things because of the updated slides.\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nSame as Summer 16\nTotally reworked Exercises accompanying the lecture\n[[FFPiH|more details on the lecture]]#", - "crumbs": [ - "Home", - "Info", - "About me", - "Studium generale / University-Life" - ] - }, - { - "objectID": "Coding/Haskell/Code Snippets/Monoid.html", - "href": "Coding/Haskell/Code Snippets/Monoid.html", - "title": "Monoid? Da war doch was…", - "section": "", - "text": "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:\nWas passiert hier an Vodoo? Und was machen die ganzen wilden Zeichen da?\nGehen wir die Main zeilenweise durch: Wir lesen die Datei, die im ersten Kommandozeilen-Argument gegeben wird. getArgs hat folgende Signatur:\nWir 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:\nIrgendwie müssen wird as in das IO bekommen. Hierzu gibt es fmap. Somit ist\nEin 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.\nEine 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:\nfmap “packt” die Funktion quasi 1 Umgebung (Funktor, Monade, ..) weiter rein - Sei es nun in Maybe, Either oder irgendwas anderes.\nAlternatives (ausführliches) Beispiel am Ende.\nWenn wir uns die Signatur ansehen, dann haben wir nun\nreadFile will aber nun ein String haben. Man kann nun\nkann man auch “inline” mit =<< die Sachen “auspacken”.\nDie 2. Zeile lesen wir nun einfach “von hinten”, wie man das meistens tun sollte. Hier ist ein\nwas uns den Inhalt der Datei zeilenweise gibt. Mit jeder Zeile möchten wir nun folgendes machen:\nWenn wir uns die Signatur ansehen:\nDas mag im ersten Moment verwirren, daher noch die Signaturen der Einzelfunktionen:\nDa 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:\neek! 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.\nWenn 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 …. Trommelwirbel “nichts”! Wir müssen die [IO ()]-Liste also “nur noch” mit mappend falten. Hierzu gibt es schon eine vorgefertigte Funktion:\nWas 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.\nViel 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.\nKleinen Tipp gab es noch: mapM_ macht genau das, was oben mit mconcat erreicht werden sollte. Somit kann man auch\nschreiben. Ich hab es aber mal wegen der klarheit oben so gelassen.", - "crumbs": [ - "Home", - "Serious", - "Coding", - "Haskell", - "Code Snippets", - "Monoid? Da war doch was..." - ] - }, - { - "objectID": "Coding/Haskell/Code Snippets/Monoid.html#alternatives-fmap-beispiel", - "href": "Coding/Haskell/Code Snippets/Monoid.html#alternatives-fmap-beispiel", - "title": "Monoid? Da war doch was…", - "section": "Alternatives fmap-Beispiel", - "text": "Alternatives fmap-Beispiel\nNehmen wir als alternatives Beispiel mal an:\na :: IO Maybe State t\nUm Funktionen vom Typ\nf :: IO a -> IO a\nf a -- valide\nzu nehmen, brauchen wir nichts machen. Bei\nf' :: Maybe a -> Maybe a\nbrauchen wir 1 fmap, also ein\nf' a -- error\nf' <$> a\num eine Funktion\nf'' :: State t -> State t\nzu benutzen folglich:\nf'' a -- error\nf'' <$> a -- error\nfmap f'' <$> a", - "crumbs": [ - "Home", - "Serious", - "Coding", - "Haskell", - "Code Snippets", - "Monoid? Da war doch was..." - ] - }, - { - "objectID": "Coding/Haskell/Webapp-Example/Main.hs.html", - "href": "Coding/Haskell/Webapp-Example/Main.hs.html", - "title": "Webapp-Example: Main.hs", - "section": "", - "text": "Wie man das verwendet, siehe Webapp-Example.\n{-# OPTIONS_GHC -Wno-name-shadowing #-}\n{-# LANGUAGE FlexibleContexts #-}\n{-# LANGUAGE LambdaCase #-}\n{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE RankNTypes #-}\n{-# LANGUAGE RecordWildCards #-}\n{-# LANGUAGE ScopedTypeVariables #-}\nmodule MyService where\n\n-- generische imports aus den dependencies/base, nicht in der prelude\nimport Codec.MIME.Type\nimport Configuration.Dotenv as Dotenv\nimport Control.Concurrent (forkIO, threadDelay)\nimport Control.Concurrent.Async\nimport Control.Concurrent.STM\nimport Control.Monad\nimport Control.Monad.Catch\nimport Control.Monad.Except\nimport Conversion\nimport Conversion.Text ()\nimport Data.Binary.Builder\nimport Data.String (IsString (..))\nimport Data.Time\nimport Data.Time.Clock\nimport Data.Time.Format\nimport Data.Default\nimport Network.HostName\nimport Network.HTTP.Client as HTTP hiding\n (withConnection)\nimport Network.HTTP.Types (Status, statusCode)\nimport Network.Mom.Stompl.Client.Queue\nimport Network.Wai (Middleware)\nimport Network.Wai.Logger\nimport Network.Wai.Middleware.Cors\nimport Network.Wai.Middleware.RequestLogger (OutputFormat (..),\n logStdout,\n mkRequestLogger,\n outputFormat)\nimport Servant.Client (mkClientEnv,\n parseBaseUrl)\nimport System.Directory\nimport System.Envy\nimport System.IO\nimport System.Log.FastLogger\nimport Text.PrettyPrint.GenericPretty\n\n-- generische imports, aber qualified, weil es sonst zu name-clashes kommt\n\nimport qualified Data.ByteString as BS\n-- import qualified Data.ByteString.Char8 as BS8\nimport qualified Data.ByteString.Lazy as LBS\nimport qualified Network.HTTP.Client.TLS as UseDefaultHTTPSSettings (tlsManagerSettings)\nimport qualified Network.Mom.Stompl.Client.Queue as AMQ\nimport qualified Network.Wai as WAI\n\n-- Handler für den MyServiceBackend-Typen und Imports aus den Libraries\nimport MyService.Handler as H -- handler der H.myApiEndpointV1Post implementiert\nimport MyService.Types -- weitere Type (s. nächste box)\nimport MyServiceGen.API as MS -- aus der generierten library\n\n\nmyServicemain :: IO ()\nmyServicemain = do\n -- .env-Datei ins Prozess-Environment laden, falls noch nicht von außen gesetzt\n void $ loadFile $ Dotenv.Config [\".env\"] [] False\n -- Config holen (defaults + overrides aus dem Environment)\n sc@ServerConfig{..} <- decodeWithDefaults defConfig\n -- Backend-Setup\n -- legt sowas wie Proxy-Server fest und wo man wie dran kommt. Benötigt für das Sprechen mit anderen Microservices\n let defaultHTTPSSettings = UseDefaultHTTPSSettings.tlsManagerSettings { managerResponseTimeout = responseTimeoutMicro $ 1000 * 1000 * myserviceMaxTimeout }\n createBackend url proxy = do\n manager <- newManager . managerSetProxy proxy\n $ defaultHTTPSSettings\n url' <- parseBaseUrl url\n return (mkClientEnv manager url')\n internalProxy = case myserviceInternalProxyUrl of\n \"\" -> noProxy\n url -> useProxy $ HTTP.Proxy (fromString url) myserviceInternalProxyPort\n -- externalProxy = case myserviceExternalProxyUrl of\n -- \"\" -> noProxy\n -- url -> useProxy $ HTTP.Proxy (fromString url) myserviceExternalProxyPort\n\n -- Definieren & Erzeugen der Funktionen um die anderen Services anzusprechen.\n calls <- (,)\n <$> createBackend myserviceAUri internalProxy\n <*> createBackend myserviceBUri internalProxy\n\n -- Logging-Setup\n hSetBuffering stdout LineBuffering\n hSetBuffering stderr LineBuffering\n\n\n -- Infos holen, brauchen wir später\n myName <- getHostName\n today <- formatTime defaultTimeLocale \"%F\" . utctDay <$> getCurrentTime\n\n\n -- activeMQ-Transaktional-Queue zum schreiben nachher vorbereiten\n amqPost <- newTQueueIO\n\n\n -- 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.\n bracket\n -- logfiles öffnen\n (LogFiles <$> openFile (\"/logs/myservice-\"<>myName<>\"-\"<>today<>\".info\") AppendMode\n <*> openFile (if myserviceDebug then \"/logs/myservice-\"<>myName<>\"-\"<>today<>\".debug\" else \"/dev/null\") AppendMode\n <*> openFile (\"/logs/myservice-\"<>myName<>\"-\"<>today<>\".error\") AppendMode\n <*> openFile (\"/logs/myservice-\"<>myName<>\"-\"<>today<>\".timings\") AppendMode\n )\n -- und bei exception/beendigung schlißen.h\n (\\(LogFiles a b c d) -> mapM_ hClose [a,b,c,d])\n $ \\logfiles -> do\n\n\n -- logschreibe-funktionen aliasen; log ist hier abstrakt, iolog spezialisiert auf io.\n let log = printLogFiles logfiles :: MonadIO m => [LogItem] -> m ()\n iolog = printLogFilesIO logfiles :: [LogItem] -> IO ()\n\n\n -- H.myApiEndpointV1Post ist ein Handler (alle Handler werden mit alias H importiert) und in einer eigenen Datei\n -- 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\n -- Man kann aber noch viel mehr machen - z.b. gecachte Daten übergeben, eine Talk-Instanz, etc. pp.\n server = MyServiceBackend{ myApiEndpointV1Post = H.myApiEndpointV1Post sc calls amqPost log\n }\n config = MS.Config $ \"http://\" ++ myserviceHost ++ \":\" ++ show myservicePort ++ \"/\"\n iolog . pure . Info $ \"Using Server configuration:\"\n iolog . pure . Info $ pretty sc { myserviceActivemqPassword = \"******\" -- Do NOT log the password ;)\n , myserviceMongoPassword = \"******\"\n }\n -- alle Services starten (Hintergrund-Aktionen wie z.b. einen MongoDB-Dumper, einen Talk-Server oder wie hier die ActiveMQ\n void $ forkIO $ keepActiveMQConnected sc iolog amqPost\n -- logging-Framework erzeugen\n loggingMW <- loggingMiddleware\n -- server starten\n if myserviceDebug\n then runMyServiceMiddlewareServer config (cors (\\_ -> Just (simpleCorsResourcePolicy {corsRequestHeaders = [\"Content-Type\"]})) . loggingMW . logStdout) server\n else runMyServiceMiddlewareServer config (cors (\\_ -> Just (simpleCorsResourcePolicy {corsRequestHeaders = [\"Content-Type\"]}))) server\n\n\n-- Sollte bald in die Library hs-stomp ausgelagert werden\n-- ist ein Beispiel für einen ActiveMQ-Dumper\nkeepActiveMQConnected :: ServerConfig -> ([LogItem] -> IO ()) -> TQueue BS.ByteString -> IO ()\nkeepActiveMQConnected sc@ServerConfig{..} printLog var = do\n res <- handle (\\(e :: SomeException) -> do\n printLog . pure . Error $ \"Exception in AMQ-Thread: \"<>show e\n return $ Right ()\n ) $ AMQ.try $ do -- catches all AMQ-Exception that we can handle. All others bubble up.\n printLog . pure . Info $ \"AMQ: connecting...\"\n withConnection myserviceActivemqHost myserviceActivemqPort [ OAuth myserviceActivemqUsername myserviceActivemqPassword\n , OTmo (30*1000) {- 30 sec timeout -}\n ]\n [] $ \\c -> do\n let oconv = return\n printLog . pure . Info $ \"AMQ: connected\"\n withWriter c \"Chaos-Logger for Kibana\" \"chaos.logs\" [] [] oconv $ \\writer -> do\n printLog . pure . Info $ \"AMQ: queue created\"\n let postfun = writeQ writer (Type (Application \"json\") []) []\n void $ race\n (forever $ atomically (readTQueue var) >>= postfun)\n (threadDelay (600*1000*1000)) -- wait 10 Minutes\n -- close writer\n -- close connection\n -- get outside of all try/handle/...-constructions befor recursing.\n case res of\n Left ex -> do\n printLog . pure . Error $ \"AMQ: \"<>show ex\n keepActiveMQConnected sc printLog var\n Right _ -> keepActiveMQConnected sc printLog var\n\n\n-- Beispiel für eine Custom-Logging-Middleware.\n-- Hier werden z.B. alle 4xx-Status-Codes inkl. Payload ins stdout-Log geschrieben.\n-- Nützlich, wenn die Kollegen ihre Requests nicht ordentlich schreiben können und der Server das Format zurecht mit einem BadRequest ablehnt ;)\nloggingMiddleware :: IO Middleware\nloggingMiddleware = liftIO $ mkRequestLogger $ def { outputFormat = CustomOutputFormatWithDetails out }\n where\n out :: ZonedDate -> WAI.Request -> Status -> Maybe Integer -> NominalDiffTime -> [BS.ByteString] -> Builder -> LogStr\n out _ r status _ _ payload _\n | statusCode status < 300 = \"\"\n | statusCode status > 399 && statusCode status < 500 = \"Error code \"<>toLogStr (statusCode status) <>\" sent. Request-Payload was: \"<> mconcat (toLogStr <$> payload) <> \"\\n\"\n | otherwise = toLogStr (show r) <> \"\\n\"", - "crumbs": [ - "Home", - "Serious", - "Coding", - "Haskell", - "Webapp-Development in Haskell", - "Webapp-Example: Main.hs" - ] - }, - { - "objectID": "Coding/Haskell/Webapp-Example/index.html", - "href": "Coding/Haskell/Webapp-Example/index.html", - "title": "Webapp-Development in Haskell", - "section": "", - "text": "Erster Schritt ist immer ein wünsch-dir-was bei der Api-Defenition.\nDie meisten Services haben offensichtliche Anforderungen (Schnittstellen nach draußen, Schnittstellen intern, …). Diese kann man immer sehr gut in einem Request -> Response-Model erfassen.\nUm 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 Openapi-generator zu nutzen.\nDiese Definition läuft über openapi-v3 und kann z.b. mit Echtzeit-Vorschau im http://editor.swagger.io/ erspielen. Per Default ist der noch auf openapi-v2 (aka swagger), kann aber auch v3.\nNach 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).\nIm folgenden wird (aus offensichtlichen Gründen) nur auf das Haskell-Projekt eingegangen.", - "crumbs": [ - "Home", - "Serious", - "Coding", - "Haskell", - "Webapp-Development in Haskell" - ] - }, - { - "objectID": "Coding/Haskell/Webapp-Example/index.html#definition-der-api", - "href": "Coding/Haskell/Webapp-Example/index.html#definition-der-api", - "title": "Webapp-Development in Haskell", - "section": "", - "text": "Erster Schritt ist immer ein wünsch-dir-was bei der Api-Defenition.\nDie meisten Services haben offensichtliche Anforderungen (Schnittstellen nach draußen, Schnittstellen intern, …). Diese kann man immer sehr gut in einem Request -> Response-Model erfassen.\nUm 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 Openapi-generator zu nutzen.\nDiese Definition läuft über openapi-v3 und kann z.b. mit Echtzeit-Vorschau im http://editor.swagger.io/ erspielen. Per Default ist der noch auf openapi-v2 (aka swagger), kann aber auch v3.\nNach 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).\nIm folgenden wird (aus offensichtlichen Gründen) nur auf das Haskell-Projekt eingegangen.", - "crumbs": [ - "Home", - "Serious", - "Coding", - "Haskell", - "Webapp-Development in Haskell" - ] - }, - { - "objectID": "Coding/Haskell/Webapp-Example/index.html#startprojekt-in-haskell", - "href": "Coding/Haskell/Webapp-Example/index.html#startprojekt-in-haskell", - "title": "Webapp-Development in Haskell", - "section": "Startprojekt in Haskell", - "text": "Startprojekt in Haskell\n\nErstellen eines neuen Projektes\nZunächst erstellen wir in normales Haskell-Projekt ohne Funktionalität & Firlefanz:\nstack new myservice\nDies erstellt ein neues Verzeichnis und das generelle scaffolding. Nach einer kurzen Anpassung der stack.yaml (resolver auf unserer setzen; aktuell: lts-17.4) fügen wir am Ende der Datei\nallow-newer: true\nghc-options:\n \"$locals\": -fwrite-ide-info\nein. Anschließend organisieren™ wir uns noch eine gute .gitignore und initialisieren das git mittels git init; git add .; git commit -m \"initial scaffold\"\n\n\nGenerierung der API\nDa die API immer wieder neu generiert werden kann (und sollte!) liegt sich in einem unterverzeichnis des Hauptprojektes.\nInitial ist es das einfachste ein leeres temporäres Verzeichnis woanders zu erstellen, die api-doc.yml hinein kopieren und folgendes ausführen:\nopenapi-generator generate -g haskell -o . -i api-doc.yml\nDieses erstellt einem dann eine komplette library inkl. Datentypen. Wichtig: Der Name in der api-doc sollte vom Namen des Services (oben myservice) 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, foo & fooAPI. Da der generator das API abschneidet endet man mit foo & foo und der compiler meckert, dass er nicht weiß, welche lib gemeint ist).\ndanach: wie gewohnt git init; git add .; git commit -m \"initial\". Auf dem Server der Wahl (github, gitea, gitlab, …) nun ein Repository erstellen (am Besten: myserviceAPI - nach Konvention ist alles auf API endend autogeneriert!) und den Anweisungen nach ein remote hinzufügen & pushen.\n\nWieder zurück im Haskell-Service\nIn unserem eigentlichen Service müssen wir nun die API einbinden. Dazu erstellen wir ein Verzeichnis libs (Konvention) und machen ein git submodule add <repository-url> libs/myserviceAPI\nGit hat nun die API in das submodul gepackt und wir können das oben erstellte temporäre Verzeichnis wieder löschen.\nAnschließend müssen wir stack noch erklären, dass wir die API da nun liegen haben und passen wieder die stack.yaml an, indem wir das Verzeichnis unter packages hinzufügen.\npackages:\n - .\n - libs/myserviceAPI # <<\nNun können wir in der package.yaml (oder myservice.cabal, falls kein hpack verwendet wird) unter den dependencies unsere API hinzufügen (name wie die cabal-Datei in libs/myserviceAPI).\n\n\n\nEinbinden anderer Microservices\nFunktioniert komplett analog zu dem vorgehen oben (ohne das generieren natürlich :grin:). stack.yaml editieren und zu den packages hinzufügen:\npackages:\n - .\n - libs/myserviceAPI\n - libs/myCoolMLServiceAPI\nin der package.yaml (oder der cabal) die dependencies hinzufügen und schon haben wir die Features zur Verfügung und können gegen diese Services reden.\n\n\nEntfernen von anderen Technologien/Microservices\nIn git ist das entfernen von Submodules etwas frickelig, daher hier ein copy&paste der GitHub-Antwort:\n## Remove the submodule entry from .git/config\ngit submodule deinit -f path/to/submodule\n\n## Remove the submodule directory from the superproject's .git/modules directory\nrm-rf .git/modules/path/to/submodule\n\n## Remove the entry in .gitmodules and remove the submodule directory located at path/to/submodule\ngit rm-f path/to/submodule\nFalls das nicht klappt, gibt es alternative Vorschläge unter dem Link oben.\n\n\nWoher weiss ich, was wo liegt? Dokumentation? Halloo??\nKeine Panik. Ein stack haddock --open hilft da. Das generiert die Dokumentation für alle in der package.yaml (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:\nEs gibt 2 wichtige Pfade im Browser:\n\n...../all/index.html - hier sind alle Pakete aufgeführt\n...../index.html - hier sind nur die direkten dependencies aufgeführt.\n\nWenn man einen lokalen Webserver startet kann man mittels “s” auch die interaktive Suche öffnen (Suche nach Typen, Funktionen, Signaturen, etc.). In Bash mit python3 geht das z.b. einfach über:\ncd $(stack path --local-doc-root)\npython3 -m SimpleHTTPServer 8000\nfirefox \"http://localhost:8000\"\n\n\nImplementation des Services und Start\n\nLoader/Bootstrapper\nGenerelles Vorgehen:\n\nin app/Main.hs: Hier ist quasi immer nur eine Zeile drin: main = myServiceMain\nGrund: Applications tauchen nicht im Haddock auf. Also haben wir ein “src”-Modul, welches hier nur geladen & ausgeführt wird.\nin src/MyService.hs: myServiceMain :: IO () definieren\n\nFü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.\nMain.hs anzeigen\n{-# OPTIONS_GHC -Wno-name-shadowing #-}\n{-# LANGUAGE FlexibleContexts #-}\n{-# LANGUAGE LambdaCase #-}\n{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE RankNTypes #-}\n{-# LANGUAGE RecordWildCards #-}\n{-# LANGUAGE ScopedTypeVariables #-}\nmodule MyService where\n\n-- generische imports aus den dependencies/base, nicht in der prelude\nimport Codec.MIME.Type\nimport Configuration.Dotenv as Dotenv\nimport Control.Concurrent (forkIO, threadDelay)\nimport Control.Concurrent.Async\nimport Control.Concurrent.STM\nimport Control.Monad\nimport Control.Monad.Catch\nimport Control.Monad.Except\nimport Conversion\nimport Conversion.Text ()\nimport Data.Binary.Builder\nimport Data.String (IsString (..))\nimport Data.Time\nimport Data.Time.Clock\nimport Data.Time.Format\nimport Data.Default\nimport Network.HostName\nimport Network.HTTP.Client as HTTP hiding\n (withConnection)\nimport Network.HTTP.Types (Status, statusCode)\nimport Network.Mom.Stompl.Client.Queue\nimport Network.Wai (Middleware)\nimport Network.Wai.Logger\nimport Network.Wai.Middleware.Cors\nimport Network.Wai.Middleware.RequestLogger (OutputFormat (..),\n logStdout,\n mkRequestLogger,\n outputFormat)\nimport Servant.Client (mkClientEnv,\n parseBaseUrl)\nimport System.Directory\nimport System.Envy\nimport System.IO\nimport System.Log.FastLogger\nimport Text.PrettyPrint.GenericPretty\n\n-- generische imports, aber qualified, weil es sonst zu name-clashes kommt\n\nimport qualified Data.ByteString as BS\n-- import qualified Data.ByteString.Char8 as BS8\nimport qualified Data.ByteString.Lazy as LBS\nimport qualified Network.HTTP.Client.TLS as UseDefaultHTTPSSettings (tlsManagerSettings)\nimport qualified Network.Mom.Stompl.Client.Queue as AMQ\nimport qualified Network.Wai as WAI\n\n-- Handler für den MyServiceBackend-Typen und Imports aus den Libraries\nimport MyService.Handler as H -- handler der H.myApiEndpointV1Post implementiert\nimport MyService.Types -- weitere Type (s. nächste box)\nimport MyServiceGen.API as MS -- aus der generierten library\n\n\nmyServicemain :: IO ()\nmyServicemain = do\n -- .env-Datei ins Prozess-Environment laden, falls noch nicht von außen gesetzt\n void $ loadFile $ Dotenv.Config [\".env\"] [] False\n -- Config holen (defaults + overrides aus dem Environment)\n sc@ServerConfig{..} <- decodeWithDefaults defConfig\n -- Backend-Setup\n -- legt sowas wie Proxy-Server fest und wo man wie dran kommt. Benötigt für das Sprechen mit anderen Microservices\n let defaultHTTPSSettings = UseDefaultHTTPSSettings.tlsManagerSettings { managerResponseTimeout = responseTimeoutMicro $ 1000 * 1000 * myserviceMaxTimeout }\n createBackend url proxy = do\n manager <- newManager . managerSetProxy proxy\n $ defaultHTTPSSettings\n url' <- parseBaseUrl url\n return (mkClientEnv manager url')\n internalProxy = case myserviceInternalProxyUrl of\n \"\" -> noProxy\n url -> useProxy $ HTTP.Proxy (fromString url) myserviceInternalProxyPort\n -- externalProxy = case myserviceExternalProxyUrl of\n -- \"\" -> noProxy\n -- url -> useProxy $ HTTP.Proxy (fromString url) myserviceExternalProxyPort\n\n -- Definieren & Erzeugen der Funktionen um die anderen Services anzusprechen.\n calls <- (,)\n <$> createBackend myserviceAUri internalProxy\n <*> createBackend myserviceBUri internalProxy\n\n -- Logging-Setup\n hSetBuffering stdout LineBuffering\n hSetBuffering stderr LineBuffering\n\n\n -- Infos holen, brauchen wir später\n myName <- getHostName\n today <- formatTime defaultTimeLocale \"%F\" . utctDay <$> getCurrentTime\n\n\n -- activeMQ-Transaktional-Queue zum schreiben nachher vorbereiten\n amqPost <- newTQueueIO\n\n\n -- 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.\n bracket\n -- logfiles öffnen\n (LogFiles <$> openFile (\"/logs/myservice-\"<>myName<>\"-\"<>today<>\".info\") AppendMode\n <*> openFile (if myserviceDebug then \"/logs/myservice-\"<>myName<>\"-\"<>today<>\".debug\" else \"/dev/null\") AppendMode\n <*> openFile (\"/logs/myservice-\"<>myName<>\"-\"<>today<>\".error\") AppendMode\n <*> openFile (\"/logs/myservice-\"<>myName<>\"-\"<>today<>\".timings\") AppendMode\n )\n -- und bei exception/beendigung schlißen.h\n (\\(LogFiles a b c d) -> mapM_ hClose [a,b,c,d])\n $ \\logfiles -> do\n\n\n -- logschreibe-funktionen aliasen; log ist hier abstrakt, iolog spezialisiert auf io.\n let log = printLogFiles logfiles :: MonadIO m => [LogItem] -> m ()\n iolog = printLogFilesIO logfiles :: [LogItem] -> IO ()\n\n\n -- H.myApiEndpointV1Post ist ein Handler (alle Handler werden mit alias H importiert) und in einer eigenen Datei\n -- 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\n -- Man kann aber noch viel mehr machen - z.b. gecachte Daten übergeben, eine Talk-Instanz, etc. pp.\n server = MyServiceBackend{ myApiEndpointV1Post = H.myApiEndpointV1Post sc calls amqPost log\n }\n config = MS.Config $ \"http://\" ++ myserviceHost ++ \":\" ++ show myservicePort ++ \"/\"\n iolog . pure . Info $ \"Using Server configuration:\"\n iolog . pure . Info $ pretty sc { myserviceActivemqPassword = \"******\" -- Do NOT log the password ;)\n , myserviceMongoPassword = \"******\"\n }\n -- alle Services starten (Hintergrund-Aktionen wie z.b. einen MongoDB-Dumper, einen Talk-Server oder wie hier die ActiveMQ\n void $ forkIO $ keepActiveMQConnected sc iolog amqPost\n -- logging-Framework erzeugen\n loggingMW <- loggingMiddleware\n -- server starten\n if myserviceDebug\n then runMyServiceMiddlewareServer config (cors (\\_ -> Just (simpleCorsResourcePolicy {corsRequestHeaders = [\"Content-Type\"]})) . loggingMW . logStdout) server\n else runMyServiceMiddlewareServer config (cors (\\_ -> Just (simpleCorsResourcePolicy {corsRequestHeaders = [\"Content-Type\"]}))) server\n\n\n-- Sollte bald in die Library hs-stomp ausgelagert werden\n-- ist ein Beispiel für einen ActiveMQ-Dumper\nkeepActiveMQConnected :: ServerConfig -> ([LogItem] -> IO ()) -> TQueue BS.ByteString -> IO ()\nkeepActiveMQConnected sc@ServerConfig{..} printLog var = do\n res <- handle (\\(e :: SomeException) -> do\n printLog . pure . Error $ \"Exception in AMQ-Thread: \"<>show e\n return $ Right ()\n ) $ AMQ.try $ do -- catches all AMQ-Exception that we can handle. All others bubble up.\n printLog . pure . Info $ \"AMQ: connecting...\"\n withConnection myserviceActivemqHost myserviceActivemqPort [ OAuth myserviceActivemqUsername myserviceActivemqPassword\n , OTmo (30*1000) {- 30 sec timeout -}\n ]\n [] $ \\c -> do\n let oconv = return\n printLog . pure . Info $ \"AMQ: connected\"\n withWriter c \"Chaos-Logger for Kibana\" \"chaos.logs\" [] [] oconv $ \\writer -> do\n printLog . pure . Info $ \"AMQ: queue created\"\n let postfun = writeQ writer (Type (Application \"json\") []) []\n void $ race\n (forever $ atomically (readTQueue var) >>= postfun)\n (threadDelay (600*1000*1000)) -- wait 10 Minutes\n -- close writer\n -- close connection\n -- get outside of all try/handle/...-constructions befor recursing.\n case res of\n Left ex -> do\n printLog . pure . Error $ \"AMQ: \"<>show ex\n keepActiveMQConnected sc printLog var\n Right _ -> keepActiveMQConnected sc printLog var\n\n\n-- Beispiel für eine Custom-Logging-Middleware.\n-- Hier werden z.B. alle 4xx-Status-Codes inkl. Payload ins stdout-Log geschrieben.\n-- Nützlich, wenn die Kollegen ihre Requests nicht ordentlich schreiben können und der Server das Format zurecht mit einem BadRequest ablehnt ;)\nloggingMiddleware :: IO Middleware\nloggingMiddleware = liftIO $ mkRequestLogger $ def { outputFormat = CustomOutputFormatWithDetails out }\n where\n out :: ZonedDate -> WAI.Request -> Status -> Maybe Integer -> NominalDiffTime -> [BS.ByteString] -> Builder -> LogStr\n out _ r status _ _ payload _\n | statusCode status < 300 = \"\"\n | statusCode status > 399 && statusCode status < 500 = \"Error code \"<>toLogStr (statusCode status) <>\" sent. Request-Payload was: \"<> mconcat (toLogStr <$> payload) <> \"\\n\"\n | otherwise = toLogStr (show r) <> \"\\n\"\n{{< dend >}}\n\n\nWeitere Instanzen und Definitionen, die der Generator (noch) nicht macht\nIn der Myservice.Types werden ein paar hilfreiche Typen und Typ-Instanzen definiert. Im Folgenden geht es dabei um Dinge für:\n\nEnvy\n\nLaden von $ENV_VAR in Datentypen\nDefinitionen für Default-Settings\n\nServerConfig\n\nDefinition der Server-Konfiguration & Benennung der Environment-Variablen\n\nExtraTypes\n\nggf. Paketweite extra-Typen, die der Generator nicht macht, weil sie nicht aus der API kommen (z.B. cache)\n\nOut/BSON-Instanzen\n\nDer API-Generator generiert nur wenige Instanzen automatisch (z.B. aeson), daher werden hier die fehlenden definiert.\nBSON: Kommunikation mit MongoDB\nOut: pretty-printing im Log\n\nNur nötig, wenn man pretty-printing via Out statt über Generics wie z.b. pretty-generic oder die automatische Show-Instanz via prerryShow macht.\n\n\n\nTypes.hs anzeigen\n{-# OPTIONS_GHC -Wno-orphans #-}\n{-# OPTIONS_GHC -Wno-name-shadowing #-}\n{-# LANGUAGE DeriveAnyClass #-}\n{-# LANGUAGE DeriveFunctor #-}\n{-# LANGUAGE DeriveGeneric #-}\n{-# LANGUAGE DerivingVia #-}\n{-# LANGUAGE DuplicateRecordFields #-}\n{-# LANGUAGE FlexibleContexts #-}\n{-# LANGUAGE FlexibleInstances #-}\n{-# LANGUAGE GADTs #-}\n{-# LANGUAGE LambdaCase #-}\n{-# LANGUAGE MultiParamTypeClasses #-}\n{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE RankNTypes #-}\n{-# LANGUAGE RecordWildCards #-}\nmodule MyService.Types where\n \nimport Data.Aeson (FromJSON, ToJSON)\nimport Data.Text\nimport Data.Time.Clock\nimport GHC.Generics\nimport System.Envy\nimport Text.PrettyPrint (text)\nimport Text.PrettyPrint.GenericPretty\n \n-- Out hat hierfür keine Instanzen, daher kurz eine einfach Definition.\ninstance Out Text where\n doc = text . unpack\n docPrec i a = text $ showsPrec i a \"\"\n \ninstance Out UTCTime where\n doc = text . show\n docPrec i a = text $ showsPrec i a \"\"\n \n-- 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.\ndata ServerConfig = ServerConfig\n { myserviceHost :: String -- ^ Environment: $MYSERVICE_HOST\n , myservicePort :: Int -- ^ Environment: $MYSERVICE_PORT\n , myserviceMaxTimeout :: Int -- ^ Environment: $MYSERVICE_MAX_TIMEOUT\n , myserviceInternalProxyUrl :: String -- ^ Environment: $MYSERVICE_INTERNAL_PROXY_URL\n , myserviceInternalProxyPort :: Int -- ^ Environment: $MYSERVICE_INTERNAL_PROXY_PORT\n , myserviceExternalProxyUrl :: String -- ^ Environment: $MYSERVICE_EXTERNAL_PROXY_URL\n , myserviceExternalProxyPort :: Int -- ^ Environment: $MYSERVICE_EXTERNAL_PROXY_PORT\n , myserviceActivemqHost :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_HOST\n , myserviceActivemqPort :: Int -- ^ Environment: $MYSERVICE_ACTIVEMQ_PORT\n , myserviceActivemqUsername :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_USERNAME\n , myserviceActivemqPassword :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_PASSWORD\n , myserviceMongoUsername :: String -- ^ Environment: $MYSERVICE_MONGO_USERNAME\n , myserviceMongoPassword :: String -- ^ Environment: $MYSERVICE_MONGO_PASSWORD\n , myserviceDebug :: Bool -- ^ Environment: $MYSERVICE_DEBUG\n } deriving (Show, Eq, Generic)\n \n-- Default-Konfigurations-Instanz für diesen Service.\ninstance DefConfig ServerConfig where\n defConfig = ServerConfig \"0.0.0.0\" 8080 20\n \"\"\n \"\"\n \"\"\n 0\n \"\"\n 0\n \"\"\n 0\n \"\"\n \"\"\n \"\"\n \"\"\n False\n \n-- Kann auch aus dem ENV gefüllt werden\ninstance FromEnv ServerConfig\n-- Und hübsch ausgegeben werden.\ninstance Out ServerConfig\n \n \ninstance Out Response\ninstance FromBSON Repsonse -- FromBSON-Instanz geht immer davon aus, dass alle keys da sind (ggf. mit null bei Nothing).\n{{< dend >}}\n\n\nWas noch zu tun ist\nDen Service implementieren. Einfach ein neues Modul aufmachen (z.B. MyService.Handler oder MyService.DieserEndpunktbereich/MyService.JenerEndpunktbereich) und dort die Funktion implementieren, die man in der Main.hs 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:\nmyApiEndpointV1Post :: MonadIO m => ServerConfig -> (ClientEnv,ClientEnv) -> TQueue BS.ByteString -> ([LogItem] -> IO ()) -> Request -> m Response\nmyApiEndpointV1Post sc calls amqPost log req = do\n liftIO . log $ [Info $ \"recieved \"<>pretty req] -- input-logging\n liftIO . atomically . writeTQueue . LBS.toStrict $ \"{\\\"hey Kibana, i recieved:\\\"\" <> A.encode (pretty req) <> \"}\" -- log in activeMQ/Kibana\n\n\n --- .... gaaaanz viel komplizierter code um die Response zu erhalten ;)\n let ret = Response 1337 Nothing -- dummy-response ;)\n -- gegeben wir haben eine gültige mongodb-pipe;\n -- mehr logik will ich in die Beispiele nicht packen.\n -- Man kann die z.b. als weiteren Wert in einer TMVar (damit man sie ändern & updaten kann) an die Funktion übergeben.\n liftIO . access pipe master \"DatabaseName\" $ do\n ifM (auth (myServiceMongoUsername sc) (myServiceMongoPassword sc)) (return ()) (liftIO . printLog . pure . Error $ \"MongoDB: Login failed.\")\n save \"DatabaseCollection\" [\"_id\" =: 1337, \"entry\" =: ret] -- selbe id wie oben ;)\n return ret\nDiese dummy-Antwort führt auf, wie gut man die ganzen Sachen mischen kann.\n\nLogging in die Dateien/stdout - je nach Konfiguration\nLogging von Statistiken in Kibana\nSpeichern der Antwort in der MongoDB\nGenerieren einer Serverantwort und ausliefern dieser über die Schnittstelle\n\n\n\nTipps & Tricks\n\nDateien, die statisch ausgeliefert werden sollen\nHierzu erstellt man ein Verzeichnis static/ (Konvention; ist im generator so generiert, dass das ausgeliefert wird). Packt man hier z.b. eine index.html rein, erscheint die, wenn man den Service ansurft.\n\n\nWie bekomme ich diese fancy Preview hin?\nDer Editor, der ganz am Anfang zum Einsatz gekommen ist, braucht nur die api-doc.yml 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 http://localhost:PORT/ui/ und kann direkt dort getestet werden.\n\n\nWie sorge ich für bessere Warnungen, damit der Compiler meine Bugs fängt?\nstack build --file-watch --ghc-options '-freverse-errors -W -Wall -Wcompat' --interleaved-output\nWas tut das?\n\n--file-watch: automatisches (minimales) kompilieren bei dateiänderungen\n--ghc-options\n\n-freverse-errors: Fehlermeldungen in umgekehrter Reihenfolge (Erster Fehler ganz unten; wenig scrollen )\n-W: Warnungen an\n-Wall: Alle sinnvollen Warnungen an (im gegensatz zu -Weverything, was WIRKLICH alles ist )\n-Wcompat: Warnungen für Sachen, die in der nächsten Compilerversion kaputt brechen werden & vermieden werden sollten\n\n--interleaved-output: stack-log direkt ausgeben & nicht in Dateien schreiben und die dann am ende zusammen cat'en.\n\nUm pro Datei Warnungen auszuschalten (z.B. weil man ganz sicher weiss, was man tut -.-): {-# OPTIONS_GHC -Wno-whatsoever #-} als pragma in die Datei.\nIdealerweise sollte das Projekt keine Warnungen erzeugen.\n\n\n\n\nDeployment\nAls 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.\n\nDocker\nDie 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 Dockerfile.release und der Jenkinsfile müssen noch Anpassungen gemacht werden. Konkret:\n\nin der Dockerfile.release: alle <<<HIER>>>-Stellen sinnvoll befüllen\nin der Jenkinsfile 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.\n\n\n\nJenkins\nÄnderungen die dann noch gemacht werden müssen:\n\ngit-repository URL anpassen\nEnvironment-Vars anpassen ($BRANCH = test & live haben keine zusatzdinger im docker-image-repository; ansonsten hat das image $BRANCH im Namen)\n\nWenn das fertig gebaut ist, liegt im test/live-repository ein docker-image namens servicename:version.\n\n\n\nOMG! Ich muss meine API ändern. Was mache ich nun?\n\napi-doc.yml bearbeiten, wie gewünscht\nmittels generator die Api & submodule neu generieren\nggf. custom Änderungen übernehmen (:Gitdiffsplit hilft)\nAlle Compilerfehler + Warnungen in der eigentlichen Applikation fixen\nIf it comipilez, ship it! (Besser nicht :grin:)", - "crumbs": [ - "Home", - "Serious", - "Coding", - "Haskell", - "Webapp-Development in Haskell" + "Health", + "Mental Health" ] }, { @@ -998,148 +656,490 @@ ] }, { - "objectID": "Health/Issues.html", - "href": "Health/Issues.html", - "title": "Mental Health", + "objectID": "Coding/Haskell/Webapp-Example/index.html", + "href": "Coding/Haskell/Webapp-Example/index.html", + "title": "Webapp-Development in Haskell", "section": "", - "text": "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.\nSo even despite my exceptional1 successes that can be seen in my work 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 summary of my experiences or the awesome things i did at university. 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.\nOnly thanks to the not-that-awful system in Germany and massive financial help from my parents i was even able to pursue this way.", + "text": "Erster Schritt ist immer ein wünsch-dir-was bei der Api-Defenition.\nDie meisten Services haben offensichtliche Anforderungen (Schnittstellen nach draußen, Schnittstellen intern, …). Diese kann man immer sehr gut in einem Request -> Response-Model erfassen.\nUm 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 Openapi-generator zu nutzen.\nDiese Definition läuft über openapi-v3 und kann z.b. mit Echtzeit-Vorschau im http://editor.swagger.io/ erspielen. Per Default ist der noch auf openapi-v2 (aka swagger), kann aber auch v3.\nNach 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).\nIm folgenden wird (aus offensichtlichen Gründen) nur auf das Haskell-Projekt eingegangen.", "crumbs": [ "Home", "Serious", - "Health", - "Mental Health" + "Coding", + "Haskell", + "Webapp-Development in Haskell" ] }, { - "objectID": "Health/Issues.html#what-are-my-issues", - "href": "Health/Issues.html#what-are-my-issues", - "title": "Mental Health", - "section": "What are my issues?", - "text": "What are my issues?\nWell.. 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.\nCurrently i am successfully diagnosed with\n\nsocial anxiety\nADHD\ntransgenderism\n\nand i got a big suspician of\n\nautism\n\nAll 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.\nThat’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.\nToday i finally found myself. I currently have a great partner (with 3 kids) - and all 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.\nI 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.", - "crumbs": [ - "Home", - "Serious", - "Health", - "Mental Health" - ] - }, - { - "objectID": "Health/Issues.html#the-early-days", - "href": "Health/Issues.html#the-early-days", - "title": "Mental Health", - "section": "The early days", - "text": "The early days\nTo be continued …", - "crumbs": [ - "Home", - "Serious", - "Health", - "Mental Health" - ] - }, - { - "objectID": "Opinions/Editors.html", - "href": "Opinions/Editors.html", - "title": "Editors", + "objectID": "Coding/Haskell/Webapp-Example/index.html#definition-der-api", + "href": "Coding/Haskell/Webapp-Example/index.html#definition-der-api", + "title": "Webapp-Development in Haskell", "section": "", - "text": "Better said: “neovim is currently the best™ editor” ;)\n\n\nYou can find my current Config along with other things in my gitea snippet-git.", + "text": "Erster Schritt ist immer ein wünsch-dir-was bei der Api-Defenition.\nDie meisten Services haben offensichtliche Anforderungen (Schnittstellen nach draußen, Schnittstellen intern, …). Diese kann man immer sehr gut in einem Request -> Response-Model erfassen.\nUm 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 Openapi-generator zu nutzen.\nDiese Definition läuft über openapi-v3 und kann z.b. mit Echtzeit-Vorschau im http://editor.swagger.io/ erspielen. Per Default ist der noch auf openapi-v2 (aka swagger), kann aber auch v3.\nNach 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).\nIm folgenden wird (aus offensichtlichen Gründen) nur auf das Haskell-Projekt eingegangen.", + "crumbs": [ + "Home", + "Serious", + "Coding", + "Haskell", + "Webapp-Development in Haskell" + ] + }, + { + "objectID": "Coding/Haskell/Webapp-Example/index.html#startprojekt-in-haskell", + "href": "Coding/Haskell/Webapp-Example/index.html#startprojekt-in-haskell", + "title": "Webapp-Development in Haskell", + "section": "Startprojekt in Haskell", + "text": "Startprojekt in Haskell\n\nErstellen eines neuen Projektes\nZunächst erstellen wir in normales Haskell-Projekt ohne Funktionalität & Firlefanz:\nstack new myservice\nDies erstellt ein neues Verzeichnis und das generelle scaffolding. Nach einer kurzen Anpassung der stack.yaml (resolver auf unserer setzen; aktuell: lts-17.4) fügen wir am Ende der Datei\nallow-newer: true\nghc-options:\n \"$locals\": -fwrite-ide-info\nein. Anschließend organisieren™ wir uns noch eine gute .gitignore und initialisieren das git mittels git init; git add .; git commit -m \"initial scaffold\"\n\n\nGenerierung der API\nDa die API immer wieder neu generiert werden kann (und sollte!) liegt sich in einem unterverzeichnis des Hauptprojektes.\nInitial ist es das einfachste ein leeres temporäres Verzeichnis woanders zu erstellen, die api-doc.yml hinein kopieren und folgendes ausführen:\nopenapi-generator generate -g haskell -o . -i api-doc.yml\nDieses erstellt einem dann eine komplette library inkl. Datentypen. Wichtig: Der Name in der api-doc sollte vom Namen des Services (oben myservice) 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, foo & fooAPI. Da der generator das API abschneidet endet man mit foo & foo und der compiler meckert, dass er nicht weiß, welche lib gemeint ist).\ndanach: wie gewohnt git init; git add .; git commit -m \"initial\". Auf dem Server der Wahl (github, gitea, gitlab, …) nun ein Repository erstellen (am Besten: myserviceAPI - nach Konvention ist alles auf API endend autogeneriert!) und den Anweisungen nach ein remote hinzufügen & pushen.\n\nWieder zurück im Haskell-Service\nIn unserem eigentlichen Service müssen wir nun die API einbinden. Dazu erstellen wir ein Verzeichnis libs (Konvention) und machen ein git submodule add <repository-url> libs/myserviceAPI\nGit hat nun die API in das submodul gepackt und wir können das oben erstellte temporäre Verzeichnis wieder löschen.\nAnschließend müssen wir stack noch erklären, dass wir die API da nun liegen haben und passen wieder die stack.yaml an, indem wir das Verzeichnis unter packages hinzufügen.\npackages:\n - .\n - libs/myserviceAPI # <<\nNun können wir in der package.yaml (oder myservice.cabal, falls kein hpack verwendet wird) unter den dependencies unsere API hinzufügen (name wie die cabal-Datei in libs/myserviceAPI).\n\n\n\nEinbinden anderer Microservices\nFunktioniert komplett analog zu dem vorgehen oben (ohne das generieren natürlich :grin:). stack.yaml editieren und zu den packages hinzufügen:\npackages:\n - .\n - libs/myserviceAPI\n - libs/myCoolMLServiceAPI\nin der package.yaml (oder der cabal) die dependencies hinzufügen und schon haben wir die Features zur Verfügung und können gegen diese Services reden.\n\n\nEntfernen von anderen Technologien/Microservices\nIn git ist das entfernen von Submodules etwas frickelig, daher hier ein copy&paste der GitHub-Antwort:\n## Remove the submodule entry from .git/config\ngit submodule deinit -f path/to/submodule\n\n## Remove the submodule directory from the superproject's .git/modules directory\nrm-rf .git/modules/path/to/submodule\n\n## Remove the entry in .gitmodules and remove the submodule directory located at path/to/submodule\ngit rm-f path/to/submodule\nFalls das nicht klappt, gibt es alternative Vorschläge unter dem Link oben.\n\n\nWoher weiss ich, was wo liegt? Dokumentation? Halloo??\nKeine Panik. Ein stack haddock --open hilft da. Das generiert die Dokumentation für alle in der package.yaml (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:\nEs gibt 2 wichtige Pfade im Browser:\n\n...../all/index.html - hier sind alle Pakete aufgeführt\n...../index.html - hier sind nur die direkten dependencies aufgeführt.\n\nWenn man einen lokalen Webserver startet kann man mittels “s” auch die interaktive Suche öffnen (Suche nach Typen, Funktionen, Signaturen, etc.). In Bash mit python3 geht das z.b. einfach über:\ncd $(stack path --local-doc-root)\npython3 -m SimpleHTTPServer 8000\nfirefox \"http://localhost:8000\"\n\n\nImplementation des Services und Start\n\nLoader/Bootstrapper\nGenerelles Vorgehen:\n\nin app/Main.hs: Hier ist quasi immer nur eine Zeile drin: main = myServiceMain\nGrund: Applications tauchen nicht im Haddock auf. Also haben wir ein “src”-Modul, welches hier nur geladen & ausgeführt wird.\nin src/MyService.hs: myServiceMain :: IO () definieren\n\nFü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.\nMain.hs anzeigen\n{-# OPTIONS_GHC -Wno-name-shadowing #-}\n{-# LANGUAGE FlexibleContexts #-}\n{-# LANGUAGE LambdaCase #-}\n{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE RankNTypes #-}\n{-# LANGUAGE RecordWildCards #-}\n{-# LANGUAGE ScopedTypeVariables #-}\nmodule MyService where\n\n-- generische imports aus den dependencies/base, nicht in der prelude\nimport Codec.MIME.Type\nimport Configuration.Dotenv as Dotenv\nimport Control.Concurrent (forkIO, threadDelay)\nimport Control.Concurrent.Async\nimport Control.Concurrent.STM\nimport Control.Monad\nimport Control.Monad.Catch\nimport Control.Monad.Except\nimport Conversion\nimport Conversion.Text ()\nimport Data.Binary.Builder\nimport Data.String (IsString (..))\nimport Data.Time\nimport Data.Time.Clock\nimport Data.Time.Format\nimport Data.Default\nimport Network.HostName\nimport Network.HTTP.Client as HTTP hiding\n (withConnection)\nimport Network.HTTP.Types (Status, statusCode)\nimport Network.Mom.Stompl.Client.Queue\nimport Network.Wai (Middleware)\nimport Network.Wai.Logger\nimport Network.Wai.Middleware.Cors\nimport Network.Wai.Middleware.RequestLogger (OutputFormat (..),\n logStdout,\n mkRequestLogger,\n outputFormat)\nimport Servant.Client (mkClientEnv,\n parseBaseUrl)\nimport System.Directory\nimport System.Envy\nimport System.IO\nimport System.Log.FastLogger\nimport Text.PrettyPrint.GenericPretty\n\n-- generische imports, aber qualified, weil es sonst zu name-clashes kommt\n\nimport qualified Data.ByteString as BS\n-- import qualified Data.ByteString.Char8 as BS8\nimport qualified Data.ByteString.Lazy as LBS\nimport qualified Network.HTTP.Client.TLS as UseDefaultHTTPSSettings (tlsManagerSettings)\nimport qualified Network.Mom.Stompl.Client.Queue as AMQ\nimport qualified Network.Wai as WAI\n\n-- Handler für den MyServiceBackend-Typen und Imports aus den Libraries\nimport MyService.Handler as H -- handler der H.myApiEndpointV1Post implementiert\nimport MyService.Types -- weitere Type (s. nächste box)\nimport MyServiceGen.API as MS -- aus der generierten library\n\n\nmyServicemain :: IO ()\nmyServicemain = do\n -- .env-Datei ins Prozess-Environment laden, falls noch nicht von außen gesetzt\n void $ loadFile $ Dotenv.Config [\".env\"] [] False\n -- Config holen (defaults + overrides aus dem Environment)\n sc@ServerConfig{..} <- decodeWithDefaults defConfig\n -- Backend-Setup\n -- legt sowas wie Proxy-Server fest und wo man wie dran kommt. Benötigt für das Sprechen mit anderen Microservices\n let defaultHTTPSSettings = UseDefaultHTTPSSettings.tlsManagerSettings { managerResponseTimeout = responseTimeoutMicro $ 1000 * 1000 * myserviceMaxTimeout }\n createBackend url proxy = do\n manager <- newManager . managerSetProxy proxy\n $ defaultHTTPSSettings\n url' <- parseBaseUrl url\n return (mkClientEnv manager url')\n internalProxy = case myserviceInternalProxyUrl of\n \"\" -> noProxy\n url -> useProxy $ HTTP.Proxy (fromString url) myserviceInternalProxyPort\n -- externalProxy = case myserviceExternalProxyUrl of\n -- \"\" -> noProxy\n -- url -> useProxy $ HTTP.Proxy (fromString url) myserviceExternalProxyPort\n\n -- Definieren & Erzeugen der Funktionen um die anderen Services anzusprechen.\n calls <- (,)\n <$> createBackend myserviceAUri internalProxy\n <*> createBackend myserviceBUri internalProxy\n\n -- Logging-Setup\n hSetBuffering stdout LineBuffering\n hSetBuffering stderr LineBuffering\n\n\n -- Infos holen, brauchen wir später\n myName <- getHostName\n today <- formatTime defaultTimeLocale \"%F\" . utctDay <$> getCurrentTime\n\n\n -- activeMQ-Transaktional-Queue zum schreiben nachher vorbereiten\n amqPost <- newTQueueIO\n\n\n -- 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.\n bracket\n -- logfiles öffnen\n (LogFiles <$> openFile (\"/logs/myservice-\"<>myName<>\"-\"<>today<>\".info\") AppendMode\n <*> openFile (if myserviceDebug then \"/logs/myservice-\"<>myName<>\"-\"<>today<>\".debug\" else \"/dev/null\") AppendMode\n <*> openFile (\"/logs/myservice-\"<>myName<>\"-\"<>today<>\".error\") AppendMode\n <*> openFile (\"/logs/myservice-\"<>myName<>\"-\"<>today<>\".timings\") AppendMode\n )\n -- und bei exception/beendigung schlißen.h\n (\\(LogFiles a b c d) -> mapM_ hClose [a,b,c,d])\n $ \\logfiles -> do\n\n\n -- logschreibe-funktionen aliasen; log ist hier abstrakt, iolog spezialisiert auf io.\n let log = printLogFiles logfiles :: MonadIO m => [LogItem] -> m ()\n iolog = printLogFilesIO logfiles :: [LogItem] -> IO ()\n\n\n -- H.myApiEndpointV1Post ist ein Handler (alle Handler werden mit alias H importiert) und in einer eigenen Datei\n -- 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\n -- Man kann aber noch viel mehr machen - z.b. gecachte Daten übergeben, eine Talk-Instanz, etc. pp.\n server = MyServiceBackend{ myApiEndpointV1Post = H.myApiEndpointV1Post sc calls amqPost log\n }\n config = MS.Config $ \"http://\" ++ myserviceHost ++ \":\" ++ show myservicePort ++ \"/\"\n iolog . pure . Info $ \"Using Server configuration:\"\n iolog . pure . Info $ pretty sc { myserviceActivemqPassword = \"******\" -- Do NOT log the password ;)\n , myserviceMongoPassword = \"******\"\n }\n -- alle Services starten (Hintergrund-Aktionen wie z.b. einen MongoDB-Dumper, einen Talk-Server oder wie hier die ActiveMQ\n void $ forkIO $ keepActiveMQConnected sc iolog amqPost\n -- logging-Framework erzeugen\n loggingMW <- loggingMiddleware\n -- server starten\n if myserviceDebug\n then runMyServiceMiddlewareServer config (cors (\\_ -> Just (simpleCorsResourcePolicy {corsRequestHeaders = [\"Content-Type\"]})) . loggingMW . logStdout) server\n else runMyServiceMiddlewareServer config (cors (\\_ -> Just (simpleCorsResourcePolicy {corsRequestHeaders = [\"Content-Type\"]}))) server\n\n\n-- Sollte bald in die Library hs-stomp ausgelagert werden\n-- ist ein Beispiel für einen ActiveMQ-Dumper\nkeepActiveMQConnected :: ServerConfig -> ([LogItem] -> IO ()) -> TQueue BS.ByteString -> IO ()\nkeepActiveMQConnected sc@ServerConfig{..} printLog var = do\n res <- handle (\\(e :: SomeException) -> do\n printLog . pure . Error $ \"Exception in AMQ-Thread: \"<>show e\n return $ Right ()\n ) $ AMQ.try $ do -- catches all AMQ-Exception that we can handle. All others bubble up.\n printLog . pure . Info $ \"AMQ: connecting...\"\n withConnection myserviceActivemqHost myserviceActivemqPort [ OAuth myserviceActivemqUsername myserviceActivemqPassword\n , OTmo (30*1000) {- 30 sec timeout -}\n ]\n [] $ \\c -> do\n let oconv = return\n printLog . pure . Info $ \"AMQ: connected\"\n withWriter c \"Chaos-Logger for Kibana\" \"chaos.logs\" [] [] oconv $ \\writer -> do\n printLog . pure . Info $ \"AMQ: queue created\"\n let postfun = writeQ writer (Type (Application \"json\") []) []\n void $ race\n (forever $ atomically (readTQueue var) >>= postfun)\n (threadDelay (600*1000*1000)) -- wait 10 Minutes\n -- close writer\n -- close connection\n -- get outside of all try/handle/...-constructions befor recursing.\n case res of\n Left ex -> do\n printLog . pure . Error $ \"AMQ: \"<>show ex\n keepActiveMQConnected sc printLog var\n Right _ -> keepActiveMQConnected sc printLog var\n\n\n-- Beispiel für eine Custom-Logging-Middleware.\n-- Hier werden z.B. alle 4xx-Status-Codes inkl. Payload ins stdout-Log geschrieben.\n-- Nützlich, wenn die Kollegen ihre Requests nicht ordentlich schreiben können und der Server das Format zurecht mit einem BadRequest ablehnt ;)\nloggingMiddleware :: IO Middleware\nloggingMiddleware = liftIO $ mkRequestLogger $ def { outputFormat = CustomOutputFormatWithDetails out }\n where\n out :: ZonedDate -> WAI.Request -> Status -> Maybe Integer -> NominalDiffTime -> [BS.ByteString] -> Builder -> LogStr\n out _ r status _ _ payload _\n | statusCode status < 300 = \"\"\n | statusCode status > 399 && statusCode status < 500 = \"Error code \"<>toLogStr (statusCode status) <>\" sent. Request-Payload was: \"<> mconcat (toLogStr <$> payload) <> \"\\n\"\n | otherwise = toLogStr (show r) <> \"\\n\"\n{{< dend >}}\n\n\nWeitere Instanzen und Definitionen, die der Generator (noch) nicht macht\nIn der Myservice.Types werden ein paar hilfreiche Typen und Typ-Instanzen definiert. Im Folgenden geht es dabei um Dinge für:\n\nEnvy\n\nLaden von $ENV_VAR in Datentypen\nDefinitionen für Default-Settings\n\nServerConfig\n\nDefinition der Server-Konfiguration & Benennung der Environment-Variablen\n\nExtraTypes\n\nggf. Paketweite extra-Typen, die der Generator nicht macht, weil sie nicht aus der API kommen (z.B. cache)\n\nOut/BSON-Instanzen\n\nDer API-Generator generiert nur wenige Instanzen automatisch (z.B. aeson), daher werden hier die fehlenden definiert.\nBSON: Kommunikation mit MongoDB\nOut: pretty-printing im Log\n\nNur nötig, wenn man pretty-printing via Out statt über Generics wie z.b. pretty-generic oder die automatische Show-Instanz via prerryShow macht.\n\n\n\nTypes.hs anzeigen\n{-# OPTIONS_GHC -Wno-orphans #-}\n{-# OPTIONS_GHC -Wno-name-shadowing #-}\n{-# LANGUAGE DeriveAnyClass #-}\n{-# LANGUAGE DeriveFunctor #-}\n{-# LANGUAGE DeriveGeneric #-}\n{-# LANGUAGE DerivingVia #-}\n{-# LANGUAGE DuplicateRecordFields #-}\n{-# LANGUAGE FlexibleContexts #-}\n{-# LANGUAGE FlexibleInstances #-}\n{-# LANGUAGE GADTs #-}\n{-# LANGUAGE LambdaCase #-}\n{-# LANGUAGE MultiParamTypeClasses #-}\n{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE RankNTypes #-}\n{-# LANGUAGE RecordWildCards #-}\nmodule MyService.Types where\n \nimport Data.Aeson (FromJSON, ToJSON)\nimport Data.Text\nimport Data.Time.Clock\nimport GHC.Generics\nimport System.Envy\nimport Text.PrettyPrint (text)\nimport Text.PrettyPrint.GenericPretty\n \n-- Out hat hierfür keine Instanzen, daher kurz eine einfach Definition.\ninstance Out Text where\n doc = text . unpack\n docPrec i a = text $ showsPrec i a \"\"\n \ninstance Out UTCTime where\n doc = text . show\n docPrec i a = text $ showsPrec i a \"\"\n \n-- 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.\ndata ServerConfig = ServerConfig\n { myserviceHost :: String -- ^ Environment: $MYSERVICE_HOST\n , myservicePort :: Int -- ^ Environment: $MYSERVICE_PORT\n , myserviceMaxTimeout :: Int -- ^ Environment: $MYSERVICE_MAX_TIMEOUT\n , myserviceInternalProxyUrl :: String -- ^ Environment: $MYSERVICE_INTERNAL_PROXY_URL\n , myserviceInternalProxyPort :: Int -- ^ Environment: $MYSERVICE_INTERNAL_PROXY_PORT\n , myserviceExternalProxyUrl :: String -- ^ Environment: $MYSERVICE_EXTERNAL_PROXY_URL\n , myserviceExternalProxyPort :: Int -- ^ Environment: $MYSERVICE_EXTERNAL_PROXY_PORT\n , myserviceActivemqHost :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_HOST\n , myserviceActivemqPort :: Int -- ^ Environment: $MYSERVICE_ACTIVEMQ_PORT\n , myserviceActivemqUsername :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_USERNAME\n , myserviceActivemqPassword :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_PASSWORD\n , myserviceMongoUsername :: String -- ^ Environment: $MYSERVICE_MONGO_USERNAME\n , myserviceMongoPassword :: String -- ^ Environment: $MYSERVICE_MONGO_PASSWORD\n , myserviceDebug :: Bool -- ^ Environment: $MYSERVICE_DEBUG\n } deriving (Show, Eq, Generic)\n \n-- Default-Konfigurations-Instanz für diesen Service.\ninstance DefConfig ServerConfig where\n defConfig = ServerConfig \"0.0.0.0\" 8080 20\n \"\"\n \"\"\n \"\"\n 0\n \"\"\n 0\n \"\"\n 0\n \"\"\n \"\"\n \"\"\n \"\"\n False\n \n-- Kann auch aus dem ENV gefüllt werden\ninstance FromEnv ServerConfig\n-- Und hübsch ausgegeben werden.\ninstance Out ServerConfig\n \n \ninstance Out Response\ninstance FromBSON Repsonse -- FromBSON-Instanz geht immer davon aus, dass alle keys da sind (ggf. mit null bei Nothing).\n{{< dend >}}\n\n\nWas noch zu tun ist\nDen Service implementieren. Einfach ein neues Modul aufmachen (z.B. MyService.Handler oder MyService.DieserEndpunktbereich/MyService.JenerEndpunktbereich) und dort die Funktion implementieren, die man in der Main.hs 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:\nmyApiEndpointV1Post :: MonadIO m => ServerConfig -> (ClientEnv,ClientEnv) -> TQueue BS.ByteString -> ([LogItem] -> IO ()) -> Request -> m Response\nmyApiEndpointV1Post sc calls amqPost log req = do\n liftIO . log $ [Info $ \"recieved \"<>pretty req] -- input-logging\n liftIO . atomically . writeTQueue . LBS.toStrict $ \"{\\\"hey Kibana, i recieved:\\\"\" <> A.encode (pretty req) <> \"}\" -- log in activeMQ/Kibana\n\n\n --- .... gaaaanz viel komplizierter code um die Response zu erhalten ;)\n let ret = Response 1337 Nothing -- dummy-response ;)\n -- gegeben wir haben eine gültige mongodb-pipe;\n -- mehr logik will ich in die Beispiele nicht packen.\n -- Man kann die z.b. als weiteren Wert in einer TMVar (damit man sie ändern & updaten kann) an die Funktion übergeben.\n liftIO . access pipe master \"DatabaseName\" $ do\n ifM (auth (myServiceMongoUsername sc) (myServiceMongoPassword sc)) (return ()) (liftIO . printLog . pure . Error $ \"MongoDB: Login failed.\")\n save \"DatabaseCollection\" [\"_id\" =: 1337, \"entry\" =: ret] -- selbe id wie oben ;)\n return ret\nDiese dummy-Antwort führt auf, wie gut man die ganzen Sachen mischen kann.\n\nLogging in die Dateien/stdout - je nach Konfiguration\nLogging von Statistiken in Kibana\nSpeichern der Antwort in der MongoDB\nGenerieren einer Serverantwort und ausliefern dieser über die Schnittstelle\n\n\n\nTipps & Tricks\n\nDateien, die statisch ausgeliefert werden sollen\nHierzu erstellt man ein Verzeichnis static/ (Konvention; ist im generator so generiert, dass das ausgeliefert wird). Packt man hier z.b. eine index.html rein, erscheint die, wenn man den Service ansurft.\n\n\nWie bekomme ich diese fancy Preview hin?\nDer Editor, der ganz am Anfang zum Einsatz gekommen ist, braucht nur die api-doc.yml 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 http://localhost:PORT/ui/ und kann direkt dort getestet werden.\n\n\nWie sorge ich für bessere Warnungen, damit der Compiler meine Bugs fängt?\nstack build --file-watch --ghc-options '-freverse-errors -W -Wall -Wcompat' --interleaved-output\nWas tut das?\n\n--file-watch: automatisches (minimales) kompilieren bei dateiänderungen\n--ghc-options\n\n-freverse-errors: Fehlermeldungen in umgekehrter Reihenfolge (Erster Fehler ganz unten; wenig scrollen )\n-W: Warnungen an\n-Wall: Alle sinnvollen Warnungen an (im gegensatz zu -Weverything, was WIRKLICH alles ist )\n-Wcompat: Warnungen für Sachen, die in der nächsten Compilerversion kaputt brechen werden & vermieden werden sollten\n\n--interleaved-output: stack-log direkt ausgeben & nicht in Dateien schreiben und die dann am ende zusammen cat'en.\n\nUm pro Datei Warnungen auszuschalten (z.B. weil man ganz sicher weiss, was man tut -.-): {-# OPTIONS_GHC -Wno-whatsoever #-} als pragma in die Datei.\nIdealerweise sollte das Projekt keine Warnungen erzeugen.\n\n\n\n\nDeployment\nAls 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.\n\nDocker\nDie 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 Dockerfile.release und der Jenkinsfile müssen noch Anpassungen gemacht werden. Konkret:\n\nin der Dockerfile.release: alle <<<HIER>>>-Stellen sinnvoll befüllen\nin der Jenkinsfile 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.\n\n\n\nJenkins\nÄnderungen die dann noch gemacht werden müssen:\n\ngit-repository URL anpassen\nEnvironment-Vars anpassen ($BRANCH = test & live haben keine zusatzdinger im docker-image-repository; ansonsten hat das image $BRANCH im Namen)\n\nWenn das fertig gebaut ist, liegt im test/live-repository ein docker-image namens servicename:version.\n\n\n\nOMG! Ich muss meine API ändern. Was mache ich nun?\n\napi-doc.yml bearbeiten, wie gewünscht\nmittels generator die Api & submodule neu generieren\nggf. custom Änderungen übernehmen (:Gitdiffsplit hilft)\nAlle Compilerfehler + Warnungen in der eigentlichen Applikation fixen\nIf it comipilez, ship it! (Besser nicht :grin:)", + "crumbs": [ + "Home", + "Serious", + "Coding", + "Haskell", + "Webapp-Development in Haskell" + ] + }, + { + "objectID": "Coding/Haskell/Webapp-Example/Main.hs.html", + "href": "Coding/Haskell/Webapp-Example/Main.hs.html", + "title": "Webapp-Example: Main.hs", + "section": "", + "text": "Wie man das verwendet, siehe Webapp-Example.\n{-# OPTIONS_GHC -Wno-name-shadowing #-}\n{-# LANGUAGE FlexibleContexts #-}\n{-# LANGUAGE LambdaCase #-}\n{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE RankNTypes #-}\n{-# LANGUAGE RecordWildCards #-}\n{-# LANGUAGE ScopedTypeVariables #-}\nmodule MyService where\n\n-- generische imports aus den dependencies/base, nicht in der prelude\nimport Codec.MIME.Type\nimport Configuration.Dotenv as Dotenv\nimport Control.Concurrent (forkIO, threadDelay)\nimport Control.Concurrent.Async\nimport Control.Concurrent.STM\nimport Control.Monad\nimport Control.Monad.Catch\nimport Control.Monad.Except\nimport Conversion\nimport Conversion.Text ()\nimport Data.Binary.Builder\nimport Data.String (IsString (..))\nimport Data.Time\nimport Data.Time.Clock\nimport Data.Time.Format\nimport Data.Default\nimport Network.HostName\nimport Network.HTTP.Client as HTTP hiding\n (withConnection)\nimport Network.HTTP.Types (Status, statusCode)\nimport Network.Mom.Stompl.Client.Queue\nimport Network.Wai (Middleware)\nimport Network.Wai.Logger\nimport Network.Wai.Middleware.Cors\nimport Network.Wai.Middleware.RequestLogger (OutputFormat (..),\n logStdout,\n mkRequestLogger,\n outputFormat)\nimport Servant.Client (mkClientEnv,\n parseBaseUrl)\nimport System.Directory\nimport System.Envy\nimport System.IO\nimport System.Log.FastLogger\nimport Text.PrettyPrint.GenericPretty\n\n-- generische imports, aber qualified, weil es sonst zu name-clashes kommt\n\nimport qualified Data.ByteString as BS\n-- import qualified Data.ByteString.Char8 as BS8\nimport qualified Data.ByteString.Lazy as LBS\nimport qualified Network.HTTP.Client.TLS as UseDefaultHTTPSSettings (tlsManagerSettings)\nimport qualified Network.Mom.Stompl.Client.Queue as AMQ\nimport qualified Network.Wai as WAI\n\n-- Handler für den MyServiceBackend-Typen und Imports aus den Libraries\nimport MyService.Handler as H -- handler der H.myApiEndpointV1Post implementiert\nimport MyService.Types -- weitere Type (s. nächste box)\nimport MyServiceGen.API as MS -- aus der generierten library\n\n\nmyServicemain :: IO ()\nmyServicemain = do\n -- .env-Datei ins Prozess-Environment laden, falls noch nicht von außen gesetzt\n void $ loadFile $ Dotenv.Config [\".env\"] [] False\n -- Config holen (defaults + overrides aus dem Environment)\n sc@ServerConfig{..} <- decodeWithDefaults defConfig\n -- Backend-Setup\n -- legt sowas wie Proxy-Server fest und wo man wie dran kommt. Benötigt für das Sprechen mit anderen Microservices\n let defaultHTTPSSettings = UseDefaultHTTPSSettings.tlsManagerSettings { managerResponseTimeout = responseTimeoutMicro $ 1000 * 1000 * myserviceMaxTimeout }\n createBackend url proxy = do\n manager <- newManager . managerSetProxy proxy\n $ defaultHTTPSSettings\n url' <- parseBaseUrl url\n return (mkClientEnv manager url')\n internalProxy = case myserviceInternalProxyUrl of\n \"\" -> noProxy\n url -> useProxy $ HTTP.Proxy (fromString url) myserviceInternalProxyPort\n -- externalProxy = case myserviceExternalProxyUrl of\n -- \"\" -> noProxy\n -- url -> useProxy $ HTTP.Proxy (fromString url) myserviceExternalProxyPort\n\n -- Definieren & Erzeugen der Funktionen um die anderen Services anzusprechen.\n calls <- (,)\n <$> createBackend myserviceAUri internalProxy\n <*> createBackend myserviceBUri internalProxy\n\n -- Logging-Setup\n hSetBuffering stdout LineBuffering\n hSetBuffering stderr LineBuffering\n\n\n -- Infos holen, brauchen wir später\n myName <- getHostName\n today <- formatTime defaultTimeLocale \"%F\" . utctDay <$> getCurrentTime\n\n\n -- activeMQ-Transaktional-Queue zum schreiben nachher vorbereiten\n amqPost <- newTQueueIO\n\n\n -- 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.\n bracket\n -- logfiles öffnen\n (LogFiles <$> openFile (\"/logs/myservice-\"<>myName<>\"-\"<>today<>\".info\") AppendMode\n <*> openFile (if myserviceDebug then \"/logs/myservice-\"<>myName<>\"-\"<>today<>\".debug\" else \"/dev/null\") AppendMode\n <*> openFile (\"/logs/myservice-\"<>myName<>\"-\"<>today<>\".error\") AppendMode\n <*> openFile (\"/logs/myservice-\"<>myName<>\"-\"<>today<>\".timings\") AppendMode\n )\n -- und bei exception/beendigung schlißen.h\n (\\(LogFiles a b c d) -> mapM_ hClose [a,b,c,d])\n $ \\logfiles -> do\n\n\n -- logschreibe-funktionen aliasen; log ist hier abstrakt, iolog spezialisiert auf io.\n let log = printLogFiles logfiles :: MonadIO m => [LogItem] -> m ()\n iolog = printLogFilesIO logfiles :: [LogItem] -> IO ()\n\n\n -- H.myApiEndpointV1Post ist ein Handler (alle Handler werden mit alias H importiert) und in einer eigenen Datei\n -- 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\n -- Man kann aber noch viel mehr machen - z.b. gecachte Daten übergeben, eine Talk-Instanz, etc. pp.\n server = MyServiceBackend{ myApiEndpointV1Post = H.myApiEndpointV1Post sc calls amqPost log\n }\n config = MS.Config $ \"http://\" ++ myserviceHost ++ \":\" ++ show myservicePort ++ \"/\"\n iolog . pure . Info $ \"Using Server configuration:\"\n iolog . pure . Info $ pretty sc { myserviceActivemqPassword = \"******\" -- Do NOT log the password ;)\n , myserviceMongoPassword = \"******\"\n }\n -- alle Services starten (Hintergrund-Aktionen wie z.b. einen MongoDB-Dumper, einen Talk-Server oder wie hier die ActiveMQ\n void $ forkIO $ keepActiveMQConnected sc iolog amqPost\n -- logging-Framework erzeugen\n loggingMW <- loggingMiddleware\n -- server starten\n if myserviceDebug\n then runMyServiceMiddlewareServer config (cors (\\_ -> Just (simpleCorsResourcePolicy {corsRequestHeaders = [\"Content-Type\"]})) . loggingMW . logStdout) server\n else runMyServiceMiddlewareServer config (cors (\\_ -> Just (simpleCorsResourcePolicy {corsRequestHeaders = [\"Content-Type\"]}))) server\n\n\n-- Sollte bald in die Library hs-stomp ausgelagert werden\n-- ist ein Beispiel für einen ActiveMQ-Dumper\nkeepActiveMQConnected :: ServerConfig -> ([LogItem] -> IO ()) -> TQueue BS.ByteString -> IO ()\nkeepActiveMQConnected sc@ServerConfig{..} printLog var = do\n res <- handle (\\(e :: SomeException) -> do\n printLog . pure . Error $ \"Exception in AMQ-Thread: \"<>show e\n return $ Right ()\n ) $ AMQ.try $ do -- catches all AMQ-Exception that we can handle. All others bubble up.\n printLog . pure . Info $ \"AMQ: connecting...\"\n withConnection myserviceActivemqHost myserviceActivemqPort [ OAuth myserviceActivemqUsername myserviceActivemqPassword\n , OTmo (30*1000) {- 30 sec timeout -}\n ]\n [] $ \\c -> do\n let oconv = return\n printLog . pure . Info $ \"AMQ: connected\"\n withWriter c \"Chaos-Logger for Kibana\" \"chaos.logs\" [] [] oconv $ \\writer -> do\n printLog . pure . Info $ \"AMQ: queue created\"\n let postfun = writeQ writer (Type (Application \"json\") []) []\n void $ race\n (forever $ atomically (readTQueue var) >>= postfun)\n (threadDelay (600*1000*1000)) -- wait 10 Minutes\n -- close writer\n -- close connection\n -- get outside of all try/handle/...-constructions befor recursing.\n case res of\n Left ex -> do\n printLog . pure . Error $ \"AMQ: \"<>show ex\n keepActiveMQConnected sc printLog var\n Right _ -> keepActiveMQConnected sc printLog var\n\n\n-- Beispiel für eine Custom-Logging-Middleware.\n-- Hier werden z.B. alle 4xx-Status-Codes inkl. Payload ins stdout-Log geschrieben.\n-- Nützlich, wenn die Kollegen ihre Requests nicht ordentlich schreiben können und der Server das Format zurecht mit einem BadRequest ablehnt ;)\nloggingMiddleware :: IO Middleware\nloggingMiddleware = liftIO $ mkRequestLogger $ def { outputFormat = CustomOutputFormatWithDetails out }\n where\n out :: ZonedDate -> WAI.Request -> Status -> Maybe Integer -> NominalDiffTime -> [BS.ByteString] -> Builder -> LogStr\n out _ r status _ _ payload _\n | statusCode status < 300 = \"\"\n | statusCode status > 399 && statusCode status < 500 = \"Error code \"<>toLogStr (statusCode status) <>\" sent. Request-Payload was: \"<> mconcat (toLogStr <$> payload) <> \"\\n\"\n | otherwise = toLogStr (show r) <> \"\\n\"", + "crumbs": [ + "Home", + "Serious", + "Coding", + "Haskell", + "Webapp-Development in Haskell", + "Webapp-Example: Main.hs" + ] + }, + { + "objectID": "Coding/Haskell/Code Snippets/Monoid.html", + "href": "Coding/Haskell/Code Snippets/Monoid.html", + "title": "Monoid? Da war doch was…", + "section": "", + "text": "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:\nWas passiert hier an Vodoo? Und was machen die ganzen wilden Zeichen da?\nGehen wir die Main zeilenweise durch: Wir lesen die Datei, die im ersten Kommandozeilen-Argument gegeben wird. getArgs hat folgende Signatur:\nWir 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:\nIrgendwie müssen wird as in das IO bekommen. Hierzu gibt es fmap. Somit ist\nEin 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.\nEine 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:\nfmap “packt” die Funktion quasi 1 Umgebung (Funktor, Monade, ..) weiter rein - Sei es nun in Maybe, Either oder irgendwas anderes.\nAlternatives (ausführliches) Beispiel am Ende.\nWenn wir uns die Signatur ansehen, dann haben wir nun\nreadFile will aber nun ein String haben. Man kann nun\nkann man auch “inline” mit =<< die Sachen “auspacken”.\nDie 2. Zeile lesen wir nun einfach “von hinten”, wie man das meistens tun sollte. Hier ist ein\nwas uns den Inhalt der Datei zeilenweise gibt. Mit jeder Zeile möchten wir nun folgendes machen:\nWenn wir uns die Signatur ansehen:\nDas mag im ersten Moment verwirren, daher noch die Signaturen der Einzelfunktionen:\nDa 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:\neek! 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.\nWenn 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 …. Trommelwirbel “nichts”! Wir müssen die [IO ()]-Liste also “nur noch” mit mappend falten. Hierzu gibt es schon eine vorgefertigte Funktion:\nWas 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.\nViel 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.\nKleinen Tipp gab es noch: mapM_ macht genau das, was oben mit mconcat erreicht werden sollte. Somit kann man auch\nschreiben. Ich hab es aber mal wegen der klarheit oben so gelassen.", + "crumbs": [ + "Home", + "Serious", + "Coding", + "Haskell", + "Code Snippets", + "Monoid? Da war doch was..." + ] + }, + { + "objectID": "Coding/Haskell/Code Snippets/Monoid.html#alternatives-fmap-beispiel", + "href": "Coding/Haskell/Code Snippets/Monoid.html#alternatives-fmap-beispiel", + "title": "Monoid? Da war doch was…", + "section": "Alternatives fmap-Beispiel", + "text": "Alternatives fmap-Beispiel\nNehmen wir als alternatives Beispiel mal an:\na :: IO Maybe State t\nUm Funktionen vom Typ\nf :: IO a -> IO a\nf a -- valide\nzu nehmen, brauchen wir nichts machen. Bei\nf' :: Maybe a -> Maybe a\nbrauchen wir 1 fmap, also ein\nf' a -- error\nf' <$> a\num eine Funktion\nf'' :: State t -> State t\nzu benutzen folglich:\nf'' a -- error\nf'' <$> a -- error\nfmap f'' <$> a", + "crumbs": [ + "Home", + "Serious", + "Coding", + "Haskell", + "Code Snippets", + "Monoid? Da war doch was..." + ] + }, + { + "objectID": "About/Extracurricular.html", + "href": "About/Extracurricular.html", + "title": "Studium generale / University-Life", + "section": "", + "text": "(What I did at university besides studying :sunglasses: )\n\n\n\nStudent Member of Studienbeirat Informatik (Study-Profile Commission)\nStudent Member of Tutorenauswahlkommission (Tutor-Selection Committee)\n\nLeader Tutorenevaluation (Evaluation of Tutors)\n\nStudent Member of NWI-Master-Auswahlausschuss (Master-Application Committee for my course of study)\nStudent Member of NWI-Master-Prüfungsausschuss (Committee for Exam-disputes of my Master course)\nMember of the Admin-Team for the student-body pcs\n\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nLecture on YouTube\n[[FFPiH|more details on the lecture]]#\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nLecture on YouTube (differs from link above)\nThis was the “silver chalk”-lecture\n[[FFPiH|more details on the lecture]]#\n\n\n\n\n\n\nRichtig Starten (Start Right!)\nTutor Introduction to Machine Learning (Tutor in this Lecture)\n\nWas awarded Tutoring-Award of the faculty\n\nRemade and updated slides for Computergraphics-Lecture\n\nLecture was awarded “silver chalk” among others things because of the updated slides.\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nSame as Summer 16\nTotally reworked Exercises accompanying the lecture\n[[FFPiH|more details on the lecture]]#", + "crumbs": [ + "Home", + "Info", + "About me", + "Studium generale / University-Life" + ] + }, + { + "objectID": "About/Extracurricular.html#committees-student-body", + "href": "About/Extracurricular.html#committees-student-body", + "title": "Studium generale / University-Life", + "section": "", + "text": "Student Member of Studienbeirat Informatik (Study-Profile Commission)\nStudent Member of Tutorenauswahlkommission (Tutor-Selection Committee)\n\nLeader Tutorenevaluation (Evaluation of Tutors)\n\nStudent Member of NWI-Master-Auswahlausschuss (Master-Application Committee for my course of study)\nStudent Member of NWI-Master-Prüfungsausschuss (Committee for Exam-disputes of my Master course)\nMember of the Admin-Team for the student-body pcs", + "crumbs": [ + "Home", + "Info", + "About me", + "Studium generale / University-Life" + ] + }, + { + "objectID": "About/Extracurricular.html#ekvv-links-entries-in-the-electronic-course-catalog", + "href": "About/Extracurricular.html#ekvv-links-entries-in-the-electronic-course-catalog", + "title": "Studium generale / University-Life", + "section": "", + "text": "Fortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nLecture on YouTube\n[[FFPiH|more details on the lecture]]#\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nLecture on YouTube (differs from link above)\nThis was the “silver chalk”-lecture\n[[FFPiH|more details on the lecture]]#\n\n\n\n\n\n\nRichtig Starten (Start Right!)\nTutor Introduction to Machine Learning (Tutor in this Lecture)\n\nWas awarded Tutoring-Award of the faculty\n\nRemade and updated slides for Computergraphics-Lecture\n\nLecture was awarded “silver chalk” among others things because of the updated slides.\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nSame as Summer 16\nTotally reworked Exercises accompanying the lecture\n[[FFPiH|more details on the lecture]]#", + "crumbs": [ + "Home", + "Info", + "About me", + "Studium generale / University-Life" + ] + }, + { + "objectID": "About/Experience.html", + "href": "About/Experience.html", + "title": "Highlights of my experiences in the programming world", + "section": "", + "text": "(as far as NDA and other things allow it)\n\n\n\nLearning/Writing Haskell since ~2014\nCreated and held advanced Haskell-Lecture at my University\n\n\n\n\nMy Profile\nHaskell-Lecture\nCo-Founder of DataHaskell\n\n\n\n\n\nI also have a gitea-instance where one can finde more current things and backups of old.\n\n\n\nAuthor of Eve-Online-Interface in yesod-auth-oauth2\nAuthor of “New Eden Accounting Tool” (neat), which is basically a ledger for Trading in the game Eve-Online\nDriver behind getting https://github.com/jgm/pandoc/issues/168 implemented and merged, because we needed it for our slide-filters (see [[Work]]# -> Development of Filters)\nAuthor of img2ascii - Small cli-tool for converting images into terminal-codes & ascii using JuicyPixels, because i always forget what is on the images over an ssh-connection -.-\nImplemented Array-Fusion and Recycling for subhask as layed out in Recycle your Arrays by Roman Leshchinskiy\nRaytracer in Haskell for my Computergraphics-Course\nimplementation of Densely Connected Bi-Clusters-Algorithm in Haskell (Paper)\nChemodiversity-Project at University during my masters. Complete with slideshow explaining everything.\nseveral other dead projects :D", + "crumbs": [ + "Home", + "Info", + "About me", + "Highlights of my experiences in the programming world" + ] + }, + { + "objectID": "About/Experience.html#haskell-enthusiast", + "href": "About/Experience.html#haskell-enthusiast", + "title": "Highlights of my experiences in the programming world", + "section": "", + "text": "Learning/Writing Haskell since ~2014\nCreated and held advanced Haskell-Lecture at my University\n\n\n\n\nMy Profile\nHaskell-Lecture\nCo-Founder of DataHaskell", + "crumbs": [ + "Home", + "Info", + "About me", + "Highlights of my experiences in the programming world" + ] + }, + { + "objectID": "About/Experience.html#gitea", + "href": "About/Experience.html#gitea", + "title": "Highlights of my experiences in the programming world", + "section": "", + "text": "I also have a gitea-instance where one can finde more current things and backups of old.\n\n\n\nAuthor of Eve-Online-Interface in yesod-auth-oauth2\nAuthor of “New Eden Accounting Tool” (neat), which is basically a ledger for Trading in the game Eve-Online\nDriver behind getting https://github.com/jgm/pandoc/issues/168 implemented and merged, because we needed it for our slide-filters (see [[Work]]# -> Development of Filters)\nAuthor of img2ascii - Small cli-tool for converting images into terminal-codes & ascii using JuicyPixels, because i always forget what is on the images over an ssh-connection -.-\nImplemented Array-Fusion and Recycling for subhask as layed out in Recycle your Arrays by Roman Leshchinskiy\nRaytracer in Haskell for my Computergraphics-Course\nimplementation of Densely Connected Bi-Clusters-Algorithm in Haskell (Paper)\nChemodiversity-Project at University during my masters. Complete with slideshow explaining everything.\nseveral other dead projects :D", + "crumbs": [ + "Home", + "Info", + "About me", + "Highlights of my experiences in the programming world" + ] + }, + { + "objectID": "About/Work.html", + "href": "About/Work.html", + "title": "Work-Experience", + "section": "", + "text": "Work-Experience\n\nMar. 2023 to Sep. 2023:\n\nDeveloper for 2Lambda.co. Role migrated from just coding stuff to architecting and rewriting the whole software from the ground up using a small modular approach instead of the shaky one-off systems in place.\nWas later a “nanny for everything”.\nDid a lot of work to have self-documenting code (i.e. generate documentation from the actual values used in the program, not some comments that always get out of date)\nSetting up a knowledge-base (Zettelkasten-approach) to track experiments and hyperlink them to the documentation generated above (and due to Zettelkasten you then get “this thing was used in Experiments a, b and c” automatically\nTechnologies used:\n\nClojure\n\nComplete application was written in Clojure\nNever touched that language before March - got up to speed in just 2 days, poked the expert on the team detailed questions about the runtime-system after 1 month (like inlining-behavior, allocation-things, etc.)\n\nEmanote\n\nautogenerated & linked documentation of internal modules\nintegrated with manual written tutorials/notes\ncrosslinking documentation of experiments with documentation of modules\n\nWeb of knowledge\nbidirectional discovery of things tried/done in the past to optimize finding of new strategies (meta-optimizing the decisions on what to optimize/try)\n\n\nInfrastructure\n\nOrganized and co-administrated the 4 Root-Servers we had\nSet up Kubernetes, Nexus, Docker, Nginx, letsencrypt-certs, dns-entries, etc..\n\n\n\nOct. 2018 to Aug. 2021:\n\nML-Specialist at Jobware (Paderborn; german Job-Advertising-Platform)\n\nExtraction/Classification of sentences from JobAds (Requirements, Benefits, Tasks, …)\nExtraction of Information from JobAds (Location of company, Location of workplay, contact-details, application-procedure, etc.) including geocoding of those information (backed by OpenStreetMap)\nEmbedding of JobAds into a meaningful space (i.e. “get me similar ads. btw. i dislike ad a, b, c”).\nAnalyse & predict search-queries of users on the webpage and offer likely but distinct queries (i.e. similar when typo or complete different words (synonyms, hyponyms, etc.))\n\nTechnologies used:\n\nHaskell (currently GHC 8.6, soon GHC 8.8)\n\nstack + stackage-lts\nfixplate (recursion-schemes-implementation)\nmany usual technologies like lens, http-simple, mtl, ..\ngolden-testing via tasty\nseveral inhouse-developments:\n\ntemplating based on text-replacement via generics (fieldname in Template-Type == variable replaced in template)\nactiveMQ/Kibana-bridge for logging via hs-stomp\ngeneric internal logging-framework\n\n\nPython\n\ntensorflow\npytorch\nsklearn\nnltk\n\n\n\n2013-2018:\n\nseveral jobs at my University including\n\nWorked 6 Months in the Workgroup “Theoretical Computer Science” on migrating algorithms to CUDA\nTutor “Introduction to Machine Learning”\n\nWas awarded Tutoring-Award of the Faculty of Technology for excellent tutoring\n\nLecture “[[FFPiH|Intermediate Functional Programming in Haskell]]”\n\nOriginally developed as student-project in cooperation with Jonas Betzendahl\nFirst held in Summer 2015\nDue to high demand held again in Summer 2016 and 2017\nWas awarded Lecturer-Award “silver Chalk” in 2016\n\nFirst time that this award was given to students\nMany lecturers at our faculty never get any teaching-award until retirement\n\n\nDevelopment of Pandoc-Filters for effective generation of lecture-slides for Mario Botsch (Leader “Workgroup Computer Graphics”) using Pandoc & reveal.js\n\nFramework: https://github.com/mbotsch/revealSlides\nExample: https://github.com/mbotsch/eLearning\nPandoc-Filters: https://github.com/mbotsch/pandoc-slide-filter", + "crumbs": [ + "Home", + "Info", + "About me", + "Work-Experience" + ] + }, + { + "objectID": "About/index.html", + "href": "About/index.html", + "title": "About me", + "section": "", + "text": "March 2024 to September 2028:\n\nResearch-Software-Engineer at “Digital History” workgroup at HU Berlin\nPart of NFDI4Memory, Task Area 5 “Data Culture”\nPhD-ing on the side\n\nJanuary 2024 to February 2024:\n\nWorked for yarvis\nFirst fulltime-developer; Responsive Webapp with react\n\nMarch 2023 to September 2023:\n\nWorked for 2lambda\nSilicon Valley start-up trying to beat the stock-market with fancy ML-Models\nThat work kickstarted my employment at Red Queen UG where i continue doing consulting work for 2lambda while also moving into a more senior role of also building up our own team of specialists to work on different future projects.\n\nOct. 2018 to Aug. 2021:\n\nML-Specialist at Jobware (Paderborn; german Job-Advertising-Platform)\n\n2013-2018 several jobs at my University including\n\nWorked 6 Months in the Workgroup “Theoretical Computer Science” on migrating algorithms to CUDA\nTutor “Introduction to Machine Learning”\n\nWas awarded Tutoring-Award of the Faculty of Technology for excellent tutoring\n\nLecture “Intermediate Functional Programming in Haskell”\nDevelopment of Pandoc-Filters for effective generation of lecture-slides for Mario Botsch (Leader Workgroup Computer Graphics) using Pandoc & reveal.js", + "crumbs": [ + "Home", + "Info", + "About me" + ] + }, + { + "objectID": "About/index.html#work", + "href": "About/index.html#work", + "title": "About me", + "section": "", + "text": "March 2024 to September 2028:\n\nResearch-Software-Engineer at “Digital History” workgroup at HU Berlin\nPart of NFDI4Memory, Task Area 5 “Data Culture”\nPhD-ing on the side\n\nJanuary 2024 to February 2024:\n\nWorked for yarvis\nFirst fulltime-developer; Responsive Webapp with react\n\nMarch 2023 to September 2023:\n\nWorked for 2lambda\nSilicon Valley start-up trying to beat the stock-market with fancy ML-Models\nThat work kickstarted my employment at Red Queen UG where i continue doing consulting work for 2lambda while also moving into a more senior role of also building up our own team of specialists to work on different future projects.\n\nOct. 2018 to Aug. 2021:\n\nML-Specialist at Jobware (Paderborn; german Job-Advertising-Platform)\n\n2013-2018 several jobs at my University including\n\nWorked 6 Months in the Workgroup “Theoretical Computer Science” on migrating algorithms to CUDA\nTutor “Introduction to Machine Learning”\n\nWas awarded Tutoring-Award of the Faculty of Technology for excellent tutoring\n\nLecture “Intermediate Functional Programming in Haskell”\nDevelopment of Pandoc-Filters for effective generation of lecture-slides for Mario Botsch (Leader Workgroup Computer Graphics) using Pandoc & reveal.js", + "crumbs": [ + "Home", + "Info", + "About me" + ] + }, + { + "objectID": "About/index.html#education", + "href": "About/index.html#education", + "title": "About me", + "section": "Education", + "text": "Education\n\nBachelor “Kognitive Informatik” (Cognitive Informatics) in Bielefeld 2010-2014\nMaster “Naturwissenschaftliche Informatik” (Informatics in the natural sciences) 2014-2018\n\n\nExtraordinary grades (Excerpt of my Transcript)\nNote: Scale of grades in Germany is 1.0 to 4.0 with 1.0 being best, 4.0 being passing grade, 5.0 being failed grade\n\n1.0 in Modern Data Analysis\n\nMaster course on data-analysis (time-series, core-vector-machines, gaussian processes, …)\n\n1.0 in Computergraphics\n\nRaytracing, Modern OpenGL\n\n1.3 in Computer-Animation\n\nDual-Quarternion-Skinning, Character-Animation, FACS-Poses, etc.\n\n1.3 in GPU-Computing (CUDA)\n\noriginally a 1.7 by timing (task was de-mosaicing on images, grade was measured in ms, whereby 400ms equated to 4.0 and 100ms equated to 1.0), but because my deep knowledge was visible in the code i was given a 1.3 after oral presentation.\n\n1.0 in Parallel Algorithms and Data-Structures\nEthical Hacking\n\nReverse Engineering with IDApro", + "crumbs": [ + "Home", + "Info", + "About me" + ] + }, + { + "objectID": "About/index.html#further-information", + "href": "About/index.html#further-information", + "title": "About me", + "section": "Further information", + "text": "Further information\n\nMore details on my work-experience\nMore details of my coding\nMore details of things i did beside studying at University", + "crumbs": [ + "Home", + "Info", + "About me" + ] + }, + { + "objectID": "Coding/Haskell/Code Snippets/Morphisms.html", + "href": "Coding/Haskell/Code Snippets/Morphisms.html", + "title": "*-Morpisms", + "section": "", + "text": "Note\n\n\n\nBackup eines Blogposts eines Kommilitonen\n\n\nThis weekend I spend some time on Morphisms.\nKnowing that this might sound daunting to many dabbling Haskellers (like I am), I decided to write a real short MergeSort hylomorphism quickstarter.\n\nFor 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.\n\nFirst the usual prelude:\n{-# LANGUAGE DeriveFunctor #-}\n{-# LANGUAGE TypeFamilies #-}\n\nimport Data.Functor.Foldable\nimport Data.List (splitAt, unfoldr)\n\nWe will use a binary tree like this. Note that there is no explicit recursion used, but NodeF has two holes. These will eventually filled later.\ndata TreeF c f = EmptyF | LeafF c | NodeF f f\n deriving (Eq, Show, Functor)\n\nAside: We could use this as a normal binary tree by wrapping it in Fix: type Tree a = Fix (TreeF a) But this would require us to write our tree like Fix (NodeF (Fix (LeafF 'l')) (Fix (LeafF 'r'))) which would get tedious fast. Luckily Edward build a much better way to do this into recursion-schemes. I will touch on this later.\n\nWithout 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”.\nunflatten :: [a] -> TreeF a [a]\nunflatten ( []) = EmptyF\nunflatten (x:[]) = LeafF x\nunflatten ( xs) = NodeF l r where (l,r) = splitAt (length xs `div` 2) xs\nFrom 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.\nThe 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.\n\nAside: At this point we could use this Coalgebra to construct (unsorted) binary trees from lists:\nexample1 = ana unflatten [1,3] == Fix (NodeF (Fix (LeafF 1)) (Fix (LeafF 3)))\n\nOn to our sorting, tree-collapsing Algebra. Which again is just a creepy word for “function that is used to deconstruct datastructures”.\nThe function mergeList is defined below and just merges two sorted lists into one sorted list in O(n), I would probably take this from the ordlist package if I were to implement this for real.\nAgain we see that we can just construct our sorted output list from a TreeF that apparently contains just lists.\nflatten :: Ord a => TreeF a [a] -> [a]\nflatten EmptyF = []\nflatten (LeafF c) = [c]\nflatten (NodeF l r) = mergeLists l r\n\nAside: We could use a Coalgebra to deconstruct trees:\nexample2 = cata flatten (Fix (NodeF (Fix (LeafF 3)) (Fix (LeafF 1)))) == [1,3]\n\nNow we just combine the Coalgebra and the Algebra with one from the functions from Edwards recursion-schemes library:\nmergeSort :: Ord a => [a] -> [a]\nmergeSort = hylo flatten unflatten\n\nexample3 = mergeSort [5,2,7,9,1,4] == [1,2,4,5,7,9]\n\nWhat have we gained?\nWe have implemented a MergeSort variant in 9 lines of code, not counting the mergeLists function below. Not bad, but this implementation is not much longer.\nOn the other hand the morphism based implementation cleanly describes what happens during construction and deconstruction of our intermediate structure.\nMy guess is that, as soon as the algortihms get more complex, this will really make a difference.\n\nAt 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.\nIt 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):\n mergeSort :: [Integer] -> [Integer]\n mergeSort =\n \\ (x :: [Integer]) ->\n case x of wild {\n [] -> [];\n : x1 ds ->\n case ds of _ {\n [] -> : x1 ([]);\n : ipv ipv1 ->\n unfoldr\n lvl9\n (let {\n p :: ([Integer], [Integer])\n p =\n case $wlenAcc wild 0 of ww { __DEFAULT ->\n case divInt# ww 2 of ww4 { __DEFAULT ->\n case tagToEnum# (<# ww4 0) of _ {\n False ->\n case $wsplitAt# ww4 wild of _ { (# ww2, ww3 #) -> (ww2, ww3) };\n True -> ([], wild)\n }\n }\n } } in\n (case p of _ { (x2, ds1) -> mergeSort x2 },\n case p of _ { (ds1, y) -> mergeSort y }))\n }\n }\n end Rec }\nWhile 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.\nThis 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.\n\nAftermath:\nIn the beginning I promised a way to work on non-functor data structures. Actually that was how I began to work with the recursion-schemes library.\nWe are able to create a ‘normal’ version of our tree from above:\ndata Tree c = Empty | Leaf c | Node (Tree c) (Tree c)\n deriving (Eq, Show)\nBut we can not use this directly with our (Co-)Algebras. Luckily Edward build a little bit of type magic into the library:\ntype instance Base (Tree c) = (TreeF c)\n\ninstance Unfoldable (Tree c) where\n embed EmptyF = Empty\n embed (LeafF c) = Leaf c\n embed (NodeF l r) = Node l r\n\ninstance Foldable (Tree c) where\n project Empty = EmptyF\n project (Leaf c) = LeafF c\n project (Node l r) = NodeF l r\nWithout going into detail by doing this we establish a relationship between Tree and TreeF and teach the compiler how to translate between these types.\nNow we can use our Alebra on our non functor type:\nexample4 = cata flatten (Node (Leaf 'l') (Leaf 'r')) == \"lr\"\nThe great thing about this is that, looking at the Core output again, there is no traces of the TreeF structure to be found. As far as I can tell, the algorithm is working directly on our Tree type.\n\nLiterature:\n\nUnderstanding F-Algebras\nRecursion Schemes by Example\nRecursion Schemes: A Field Guide\nThis StackOverflow question\n\n\nAppendix:\nmergeLists :: Ord a => [a] -> [a] -> [a]\nmergeLists = curry $ unfoldr c where\n c ([], []) = Nothing\n c ([], y:ys) = Just (y, ([], ys))\n c (x:xs, []) = Just (x, (xs, []))\n c (x:xs, y:ys) | x <= y = Just (x, (xs, y:ys))\n | x > y = Just (y, (x:xs, ys))", + "crumbs": [ + "Home", + "Serious", + "Coding", + "Haskell", + "Code Snippets", + "*-Morpisms" + ] + }, + { + "objectID": "Coding/Haskell/Webapp-Example/MyService_Types.hs.html", + "href": "Coding/Haskell/Webapp-Example/MyService_Types.hs.html", + "title": "Webapp-Example: MyService/Types.hs", + "section": "", + "text": "Anleitung siehe Webapp-Example.\n{-# OPTIONS_GHC -Wno-orphans #-}\n{-# OPTIONS_GHC -Wno-name-shadowing #-}\n{-# LANGUAGE DeriveAnyClass #-}\n{-# LANGUAGE DeriveFunctor #-}\n{-# LANGUAGE DeriveGeneric #-}\n{-# LANGUAGE DerivingVia #-}\n{-# LANGUAGE DuplicateRecordFields #-}\n{-# LANGUAGE FlexibleContexts #-}\n{-# LANGUAGE FlexibleInstances #-}\n{-# LANGUAGE GADTs #-}\n{-# LANGUAGE LambdaCase #-}\n{-# LANGUAGE MultiParamTypeClasses #-}\n{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE RankNTypes #-}\n{-# LANGUAGE RecordWildCards #-}\nmodule MyService.Types where\n\nimport Data.Aeson (FromJSON, ToJSON)\nimport Data.Text\nimport Data.Time.Clock\nimport GHC.Generics\nimport System.Envy\nimport Text.PrettyPrint (text)\nimport Text.PrettyPrint.GenericPretty\n\n-- Out hat hierfür keine Instanzen, daher kurz eine einfach Definition.\ninstance Out Text where\n doc = text . unpack\n docPrec i a = text $ showsPrec i a \"\"\n\ninstance Out UTCTime where\n doc = text . show\n docPrec i a = text $ showsPrec i a \"\"\n\n-- 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.\ndata ServerConfig = ServerConfig\n { myserviceHost :: String -- ^ Environment: $MYSERVICE_HOST\n , myservicePort :: Int -- ^ Environment: $MYSERVICE_PORT\n , myserviceMaxTimeout :: Int -- ^ Environment: $MYSERVICE_MAX_TIMEOUT\n , myserviceInternalProxyUrl :: String -- ^ Environment: $MYSERVICE_INTERNAL_PROXY_URL\n , myserviceInternalProxyPort :: Int -- ^ Environment: $MYSERVICE_INTERNAL_PROXY_PORT\n , myserviceExternalProxyUrl :: String -- ^ Environment: $MYSERVICE_EXTERNAL_PROXY_URL\n , myserviceExternalProxyPort :: Int -- ^ Environment: $MYSERVICE_EXTERNAL_PROXY_PORT\n , myserviceActivemqHost :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_HOST\n , myserviceActivemqPort :: Int -- ^ Environment: $MYSERVICE_ACTIVEMQ_PORT\n , myserviceActivemqUsername :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_USERNAME\n , myserviceActivemqPassword :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_PASSWORD\n , myserviceMongoUsername :: String -- ^ Environment: $MYSERVICE_MONGO_USERNAME\n , myserviceMongoPassword :: String -- ^ Environment: $MYSERVICE_MONGO_PASSWORD\n , myserviceDebug :: Bool -- ^ Environment: $MYSERVICE_DEBUG\n } deriving (Show, Eq, Generic)\n\n-- Default-Konfigurations-Instanz für diesen Service.\ninstance DefConfig ServerConfig where\n defConfig = ServerConfig \"0.0.0.0\" 8080 20\n \"\"\n \"\"\n \"\"\n 0\n \"\"\n 0\n \"\"\n 0\n \"\"\n \"\"\n \"\"\n \"\"\n False\n\n-- Kann auch aus dem ENV gefüllt werden\ninstance FromEnv ServerConfig\n-- Und hübsch ausgegeben werden.\ninstance Out ServerConfig\n\n\ninstance Out Response\ninstance FromBSON Repsonse -- FromBSON-Instanz geht immer davon aus, dass alle keys da sind (ggf. mit null bei Nothing).", + "crumbs": [ + "Home", + "Serious", + "Coding", + "Haskell", + "Webapp-Development in Haskell", + "Webapp-Example: MyService/Types.hs" + ] + }, + { + "objectID": "Coding/Haskell/FFPiH.html", + "href": "Coding/Haskell/FFPiH.html", + "title": "Fortgeschrittene funktionale Programmierung in Haskell", + "section": "", + "text": "FFPiH ist eine Vorlesung, die ich zusammen mit einem Kommilitonen im Sommer 2015 erstmals erstellt und gehalten haben.\nInsgesamt 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.\nDie gesamten Übungen sind unter anderem in der FFPiH-Organisation in meinem gitea hinterlegt: https://gitea.dresselhaus.cloud/FFPiH\nEinige der aktualisierten Übungen sind privat geschaltet, da diese iterativ aufeinander aufbauen und jeweils die Musterlösung der vorherigen enthalten.", + "crumbs": [ + "Home", + "Serious", + "Coding", + "Haskell", + "Fortgeschrittene funktionale Programmierung in Haskell" + ] + }, + { + "objectID": "Coding/Haskell/FFPiH.html#aufbau-der-vorlesung", + "href": "Coding/Haskell/FFPiH.html#aufbau-der-vorlesung", + "title": "Fortgeschrittene funktionale Programmierung in Haskell", + "section": "Aufbau der Vorlesung", + "text": "Aufbau der Vorlesung\nVorausgesetzt 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 wirklich erklärt bekommen haben).\nStü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.\nSchlussendlich 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.\nOptional gab es weitere Übungen zu dingen wie “verteiltes Rechnen”.\nZiel 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.", + "crumbs": [ + "Home", + "Serious", + "Coding", + "Haskell", + "Fortgeschrittene funktionale Programmierung in Haskell" + ] + }, + { + "objectID": "Coding/Haskell/FFPiH.html#studentisches-feedback", + "href": "Coding/Haskell/FFPiH.html#studentisches-feedback", + "title": "Fortgeschrittene funktionale Programmierung in Haskell", + "section": "Studentisches Feedback", + "text": "Studentisches Feedback\nSehr gutes Feedback von den Studenten bekamen wir insbesondere für Übungen wie:\nÜbung 2, Aufgabe 2, 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.\nÜbung 4, 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:\n\nEinarbeitung in QuickCheck zur Behebung eines Bugs im Test\nUmschreiben von explizitem Argument-Passing hin zu Monad-Transformers mit stateful Lenses\nContinuation-Basierendes Event-System\nHinzufügen eines Parsers für Level, Items & deren Effekte und implementation dieser\nÄndern des GUI-Parts von CLI auf 2D GL mittels gloss\nÄndern von StateT World auf RWST GameConfig Log World und somit nutzen von individuellen Konfigurationen für z.b. Keybindings", + "crumbs": [ + "Home", + "Serious", + "Coding", + "Haskell", + "Fortgeschrittene funktionale Programmierung in Haskell" + ] + }, + { + "objectID": "Coding/Haskell/Advantages.html", + "href": "Coding/Haskell/Advantages.html", + "title": "Talks und Posts zu Haskell", + "section": "", + "text": "Gründe Haskell zu nutzen und wo Vorteile liegen.\n\n\n\nThe Future is parallel\nLenses (Registrierung nötig - kostenfrei), siehe auch: Lenses\nRunning a Startup on Haskell\nWe’re doing it all wrong - A Long-Term Scala-Compiler-Developer quits his job after years and tells why Scala is a mess.\nMonads explained in Javascript\nVinyl Records with Slides\nThinking with Laziness\n\n\n\n\n\nPapers on STM\nTackling the awkward squad\nParallel and Concurrent Programming in Haskell\nSlides of a Quickcheck-Talk\nUnderstanding F-Algebras schöne Erklärung. Man könnte danach anfangen den Morphismen-zoo zu verstehen…\nMonad Transformers\n\n\n\n\n\nTom LaGatta on Category-Theory\nUnifying Structured Recursion Schemes aka. [[Morphisms|The Morphism-Zoo]]\nHole-Driven-Development Teaser (Enthusiasticon, raichoo)\n\n\n\n\n\nFunctional Reactive Programming\nDiagrams: Declarative Vector Graphics in Haskell\nLenses, Folds & Traversels by Edward Kmett\nDiscrimination is Wrong by Edward Kmett\n\n\n\n\n\nHaskell fast and hard\nCounterexamples for Typeclasses", + "crumbs": [ + "Home", + "Serious", + "Coding", + "Haskell", + "Talks und Posts zu Haskell" + ] + }, + { + "objectID": "Coding/Haskell/Advantages.html#talks", + "href": "Coding/Haskell/Advantages.html#talks", + "title": "Talks und Posts zu Haskell", + "section": "", + "text": "The Future is parallel\nLenses (Registrierung nötig - kostenfrei), siehe auch: Lenses\nRunning a Startup on Haskell\nWe’re doing it all wrong - A Long-Term Scala-Compiler-Developer quits his job after years and tells why Scala is a mess.\nMonads explained in Javascript\nVinyl Records with Slides\nThinking with Laziness", + "crumbs": [ + "Home", + "Serious", + "Coding", + "Haskell", + "Talks und Posts zu Haskell" + ] + }, + { + "objectID": "Coding/Haskell/Advantages.html#bücherpaper", + "href": "Coding/Haskell/Advantages.html#bücherpaper", + "title": "Talks und Posts zu Haskell", + "section": "", + "text": "Papers on STM\nTackling the awkward squad\nParallel and Concurrent Programming in Haskell\nSlides of a Quickcheck-Talk\nUnderstanding F-Algebras schöne Erklärung. Man könnte danach anfangen den Morphismen-zoo zu verstehen…\nMonad Transformers", + "crumbs": [ + "Home", + "Serious", + "Coding", + "Haskell", + "Talks und Posts zu Haskell" + ] + }, + { + "objectID": "Coding/Haskell/Advantages.html#funny-talks", + "href": "Coding/Haskell/Advantages.html#funny-talks", + "title": "Talks und Posts zu Haskell", + "section": "", + "text": "Tom LaGatta on Category-Theory\nUnifying Structured Recursion Schemes aka. [[Morphisms|The Morphism-Zoo]]\nHole-Driven-Development Teaser (Enthusiasticon, raichoo)", + "crumbs": [ + "Home", + "Serious", + "Coding", + "Haskell", + "Talks und Posts zu Haskell" + ] + }, + { + "objectID": "Coding/Haskell/Advantages.html#unsortedunseen", + "href": "Coding/Haskell/Advantages.html#unsortedunseen", + "title": "Talks und Posts zu Haskell", + "section": "", + "text": "Functional Reactive Programming\nDiagrams: Declarative Vector Graphics in Haskell\nLenses, Folds & Traversels by Edward Kmett\nDiscrimination is Wrong by Edward Kmett", + "crumbs": [ + "Home", + "Serious", + "Coding", + "Haskell", + "Talks und Posts zu Haskell" + ] + }, + { + "objectID": "Coding/Haskell/Advantages.html#tutorials", + "href": "Coding/Haskell/Advantages.html#tutorials", + "title": "Talks und Posts zu Haskell", + "section": "", + "text": "Haskell fast and hard\nCounterexamples for Typeclasses", + "crumbs": [ + "Home", + "Serious", + "Coding", + "Haskell", + "Talks und Posts zu Haskell" + ] + }, + { + "objectID": "Opinions/Keyboard-Layout.html", + "href": "Opinions/Keyboard-Layout.html", + "title": "Keyboard-Layout", + "section": "", + "text": "Since around 2006 i basically write only using the NEO2-Layout. There are many advantages that are not obvious to an onlooker right away.\nDon’t get me wrong. I still can type QWERTZ - just because you learn an additional layout does not mean that you forget everything from before.\nThe secret sauce lies in the deeper layers. Especially layer 3 having all the “hard to reach” things like brackets, braces, etc. right on the home row. And the 4th layer is magic for text-navigation. Left hand has the full navigation, right hand has the complete Numpad - even on laptop-keyboards that are lacking those.\nFor me as a person having the usual German Keyboard with AltGr this just means:\n\nPutting the thumb down on AltGr - it is above there anyway.\nUse left hand as normal arrow-keys (that work EVERYWHERE because they are just arrow keys)\nAlso use Home/End/PgUp/PgDown/…\n\nBefore i always had to switch over or hope that a thing had support for vi-style “hjkl”.\nThat’s why i also prefer Neovim as my primary editor - just not having to touch your mouse at any time for anything is such a godsend :)\nBest thing: If you don’t want to switch, there is also a “Neo-QWERTZ”-variant .. where you can just try the deeper layers while not leaving your QWERTZ-layout behind. But i have just seen and never tried it. Your experience may be sub-par.", "crumbs": [ "Home", "Fun", "Opinions", - "Editors" + "Keyboard-Layout" ] }, { - "objectID": "Opinions/Editors.html#editors", - "href": "Opinions/Editors.html#editors", - "title": "Editors", + "objectID": "Uni/Lernerfolg_an_der_Uni.html", + "href": "Uni/Lernerfolg_an_der_Uni.html", + "title": "Wie lerne ich richtig an der Uni?", "section": "", - "text": "Better said: “neovim is currently the best™ editor” ;)\n\n\nYou can find my current Config along with other things in my gitea snippet-git.", + "text": "Dies ist eine gute Frage. Da ich im laufe der Zeit einige Antworten gesammelt habe, wollte ich diese mal hier niederschreiben. Vorweg eine Warnung: 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. Da wir das nun aus dem Weg haben, geht es auch gleich los.", "crumbs": [ "Home", - "Fun", - "Opinions", - "Editors" + "Serious", + "Uni", + "Wie lerne ich richtig an der Uni?" ] }, { - "objectID": "Opinions/Editors.html#references", - "href": "Opinions/Editors.html#references", - "title": "Editors", - "section": "References", - "text": "References\n\nLearning Vim in 2014: Vim as Language - Herding Lions\nvi - What is your most productive shortcut with Vim? - Stack Overflow", + "objectID": "Uni/Lernerfolg_an_der_Uni.html#uni-ist-nicht-schule", + "href": "Uni/Lernerfolg_an_der_Uni.html#uni-ist-nicht-schule", + "title": "Wie lerne ich richtig an der Uni?", + "section": "Uni ist nicht Schule", + "text": "Uni ist nicht Schule\nEinige 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).", "crumbs": [ "Home", - "Fun", - "Opinions", - "Editors" + "Serious", + "Uni", + "Wie lerne ich richtig an der Uni?" ] }, { - "objectID": "Stuff/Bielefeldverschwoerung.html", - "href": "Stuff/Bielefeldverschwoerung.html", - "title": "Die Bielefeld-Verschwörung", - "section": "", - "text": "Kopie des vermutlichen Originals von (vermutlich) Achim Held aus 1994.\nWarnung: 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!\nVielen Dank für die Beachtung aller Sicherheitsvorschriften.", + "objectID": "Uni/Lernerfolg_an_der_Uni.html#etwas-lerntheorie", + "href": "Uni/Lernerfolg_an_der_Uni.html#etwas-lerntheorie", + "title": "Wie lerne ich richtig an der Uni?", + "section": "Etwas Lerntheorie", + "text": "Etwas Lerntheorie\nEs gibt einen sehr schönen Talk 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.\n\nAuswendig lernen\nGrade 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: Anki 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.\nIch 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.\nMan 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).\n\n\nMethodik lernen\nIm 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.\nUm 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”.\nKleines Beispiel aus der Welt der Mathematik:\nVektorraum -> Ist zu einer Basis definiert\n -> Basis ist die größtmögliche Zahl lin. unabh. Vektoren. Lin. Hülle der Basis ist der VR\n -> Lin. Hülle ist jede Lin.-Komb. von Vektoren\n -> Hat eine Vektoraddition und skalare Multiplikation\n -> Wird über einem Körper aufgespannt\n -> Körper sind 2 abelsche Gruppen mit Distributivgesetz\n -> abelsche Gruppe ist Menge mit K.A.I.N.\n -> ....\nSo 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.", "crumbs": [ "Home", - "Fun", - "Stuff", - "Die Bielefeld-Verschwörung" + "Serious", + "Uni", + "Wie lerne ich richtig an der Uni?" ] }, { - "objectID": "Stuff/Bielefeldverschwoerung.html#die-geschichte-der-entdeckung", - "href": "Stuff/Bielefeldverschwoerung.html#die-geschichte-der-entdeckung", - "title": "Die Bielefeld-Verschwörung", - "section": "Die Geschichte der Entdeckung", - "text": "Die Geschichte der Entdeckung\nVor 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.\nDann, 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\nDer 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.\nAber 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 “Stadtinfo über Bielefeld” 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.\nEin 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.\nDie 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.\nInzwischen 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:\n“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.”", + "objectID": "Uni/Lernerfolg_an_der_Uni.html#vorbereiten-auf-eine-klausur", + "href": "Uni/Lernerfolg_an_der_Uni.html#vorbereiten-auf-eine-klausur", + "title": "Wie lerne ich richtig an der Uni?", + "section": "Vorbereiten auf eine Klausur", + "text": "Vorbereiten auf eine Klausur\n\nHerausfinden, um was für eine Art von Klausur es sich handelt\n\nAnkreuzklausur?\nAuswendiglern-Klausur?\nPraktische Klausur (z.b. fast 1:1 Übungsaufgaben, feste Schema, ..)?\nOpen-Book?\nAnnotation von Grafiken?\n\nKlausuren von der Fachschaft organisieren\n\nFalls keine Vorhanden: Altfachschaftler fragen, wie die Klausur bei ihnen war\nNeue Klausur mit in die FS bringen, falls möglich (z.b. schreiend rausrennen und Klausur dabei mitnehmen, bevor man offiziell registriert wurde)\n\n\nJe nach Klausurtyp dann mit Anki stumpf Karten machen und auswendig lernen (z.b. Ankreuzklausur, Grafik-annotations-Klausur, ..) oder Übungsaufgaben/Altklausuren durchrechnen", "crumbs": [ "Home", - "Fun", - "Stuff", - "Die Bielefeld-Verschwörung" + "Serious", + "Uni", + "Wie lerne ich richtig an der Uni?" ] }, { - "objectID": "Stuff/Bielefeldverschwoerung.html#was-steckt-dahinter", - "href": "Stuff/Bielefeldverschwoerung.html#was-steckt-dahinter", - "title": "Die Bielefeld-Verschwörung", - "section": "Was steckt dahinter?", - "text": "Was steckt dahinter?\nDies 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:\n\nEs 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.\nAn 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.\nDer 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.\nAn 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.", + "objectID": "Uni/Lernerfolg_an_der_Uni.html#vorbereiten-auf-eine-mündliche-prüfung", + "href": "Uni/Lernerfolg_an_der_Uni.html#vorbereiten-auf-eine-mündliche-prüfung", + "title": "Wie lerne ich richtig an der Uni?", + "section": "Vorbereiten auf eine mündliche Prüfung", + "text": "Vorbereiten auf eine mündliche Prüfung\n\nProtokolle aus der Fachschaft organisieren\n\nHäufig gegen Pfand, dass man bei Abgabe eines Protokolls wieder bekommt\nWenn keins vorhanden für die nachfolgede Generation eins ausfüllen\n\n\nWenn 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).\n\nUnterschiede mündliche Bachelor/Master-Prüfungen\nEinige 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.\nBeispiel: 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.\nDies 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.", "crumbs": [ "Home", - "Fun", - "Stuff", - "Die Bielefeld-Verschwörung" + "Serious", + "Uni", + "Wie lerne ich richtig an der Uni?" ] }, { - "objectID": "Stuff/Bielefeldverschwoerung.html#was-können-wir-tun", - "href": "Stuff/Bielefeldverschwoerung.html#was-können-wir-tun", - "title": "Die Bielefeld-Verschwörung", - "section": "Was können wir tun?", - "text": "Was können wir tun?\nZum 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.\nEine 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.\nAb 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.\nDas, 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: Bielefeld gibt es nicht!!!", + "objectID": "Uni/Lernerfolg_an_der_Uni.html#prüfungsangt", + "href": "Uni/Lernerfolg_an_der_Uni.html#prüfungsangt", + "title": "Wie lerne ich richtig an der Uni?", + "section": "Prüfungsangt", + "text": "Prüfungsangt\nZu 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 ;)\nEs 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).\nDie 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.\nEs 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.", "crumbs": [ "Home", - "Fun", - "Stuff", - "Die Bielefeld-Verschwörung" + "Serious", + "Uni", + "Wie lerne ich richtig an der Uni?" ] }, { - "objectID": "index.html", - "href": "index.html", - "title": "Nicole Dresselhaus", - "section": "", - "text": "Unsortierte Einsichten und Erfahrungen. Archiviert zum verlinken, späteren Überdenken oder Diskutieren.\nKeine Garantie auf Richtigkeit oder Trollfreiheit :>" - }, - { - "objectID": "index.html#letzte-posts", - "href": "index.html#letzte-posts", - "title": "Nicole Dresselhaus", - "section": "Letzte Posts", - "text": "Letzte Posts" + "objectID": "Uni/Lernerfolg_an_der_Uni.html#schlusswort", + "href": "Uni/Lernerfolg_an_der_Uni.html#schlusswort", + "title": "Wie lerne ich richtig an der Uni?", + "section": "Schlusswort", + "text": "Schlusswort\nViel Erfolg bei euren Prüfungen. Falls euch dieser Artikel geholfen hat oder ihr noch Anregungen/Verbessenguswünsche habt, schreibt mir einfach.", + "crumbs": [ + "Home", + "Serious", + "Uni", + "Wie lerne ich richtig an der Uni?" + ] }, { "objectID": "Writing/documentation.html", @@ -1283,5 +1283,44 @@ "Writing", "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)" ] + }, + { + "objectID": "Obsidian-Articles/Artikelbewertung durch LLM.html", + "href": "Obsidian-Articles/Artikelbewertung durch LLM.html", + "title": "Artikelbewertung durch ein Large Language Model (LLM) - Ein Beispiel aus der Praxis", + "section": "", + "text": "Untenstehend ein Praxisbeispiel, wie ich momentan ein LLM aufrufe um sinnvolle(!) Antworten zu bekommen. Das ganze läuft lokal via ollama.\nIn den Kontext setzt das avante-plugin1 ein großes system-prompt und fügt dort die gesamte Datei (in diesem Fall ein sehr langer markdown-text) ein.\n1 Neovim-Plugin, was den Editor quasi zu einem cursor.ai oder einem VS-Code mit allen Späßen aufbohrtInsbesondere sieht man hier: Damit das LLM keinen “mist” baut oder zu sehr halluziniert, wird eine Antwortstruktur vorgegeben, der einfach zu folgen ist. Trotzdem ist das immer noch nur “fancy next word prediction” - aber immerhin besser, als alles, was man sonst in 1 Minute aus dem Ärmel schüttelt.", + "crumbs": [ + "Home", + "Serious", + "Writing", + "Artikelbewertung durch ein Large Language Model (LLM) - Ein Beispiel aus der Praxis" + ] + }, + { + "objectID": "Obsidian-Articles/Artikelbewertung durch LLM.html#hintergrund", + "href": "Obsidian-Articles/Artikelbewertung durch LLM.html#hintergrund", + "title": "Artikelbewertung durch ein Large Language Model (LLM) - Ein Beispiel aus der Praxis", + "section": "", + "text": "Untenstehend ein Praxisbeispiel, wie ich momentan ein LLM aufrufe um sinnvolle(!) Antworten zu bekommen. Das ganze läuft lokal via ollama.\nIn den Kontext setzt das avante-plugin1 ein großes system-prompt und fügt dort die gesamte Datei (in diesem Fall ein sehr langer markdown-text) ein.\n1 Neovim-Plugin, was den Editor quasi zu einem cursor.ai oder einem VS-Code mit allen Späßen aufbohrtInsbesondere sieht man hier: Damit das LLM keinen “mist” baut oder zu sehr halluziniert, wird eine Antwortstruktur vorgegeben, der einfach zu folgen ist. Trotzdem ist das immer noch nur “fancy next word prediction” - aber immerhin besser, als alles, was man sonst in 1 Minute aus dem Ärmel schüttelt.", + "crumbs": [ + "Home", + "Serious", + "Writing", + "Artikelbewertung durch ein Large Language Model (LLM) - Ein Beispiel aus der Praxis" + ] + }, + { + "objectID": "Obsidian-Articles/Artikelbewertung durch LLM.html#llm-interaktion", + "href": "Obsidian-Articles/Artikelbewertung durch LLM.html#llm-interaktion", + "title": "Artikelbewertung durch ein Large Language Model (LLM) - Ein Beispiel aus der Praxis", + "section": "LLM-Interaktion", + "text": "LLM-Interaktion\n\n\n\n\n\n\nCall Metadata\n\n\n\n\nDatetime: 2025-06-05 13:47:11\nModel: ollama/cogito:14b\nSelected files:\n\nbackground/BACKGROUND.md\n\n\n\n\n\n\n\n\n\n\nWarnung\n\n\n\nEnable deep thinking subroutine.\nWelche Absätze gleichen sich inhaltlich zu stark? Welche Absätze sollten in andere Kapitel wandern?\nGehe den Text im Geiste Absatz für Absatz durch und zeig mir, dass du ihn gelesen hast.\nEin einfaches: \"<Absatztitel>: ok\" oder \"<Absatztitel>: xx gefunden\" reicht um pro Absatz eine Aussage zu treffen.\n\n\n\n\n\n\n\n\n🤔 Thought content\n\n\n\n\nEinleitung: Einführung in die Bedeutung der Dokumentation - ok\nInhaltliche Anforderungen an die Dokumentation:\n\nHier werden spezifische Inhalte definiert, die dokumentiert werden müssen\nKeine Überschneidung mit anderen Abschnitten\n\nFormat und Struktur der Dokumentation:\n\nFokussiert auf die Formate und Strukturen der Dokumentation\nKeine Überschneidung mit Inhaltlichen Anforderungen\n\nWas macht eine gute Dokumentation aus:\n\nHier werden allgemeine Prinzipien für gute Dokumentation erläutert\nTeile könnten in “Inhaltliche Anforderungen” oder “Format und Struktur” übernommen werden\n\n(Teil-)automatisierte Dokumentationswerkzeuge:\n\nBeschreibt spezifische Tools zur Automatisierung der Dokumentation\nKeine Überschneidung mit anderen Abschnitten\n\nBest Practices, Vorlagen und Checklisten:\n\nEnthält praktische Ratschläge und Werkzeuge\nTeile könnten in “Format und Struktur” oder “Was macht eine gute Dokumentation aus” integriert werden\n\nImplementierung aller Vorschläge als ready-to-use Repository:\n\nBeschreibt die Umsetzung der Empfehlungen\nKeine Überschneidung mit anderen Abschnitten\n\nFazit:\n\nZusammenfassung und Schlussfolgerung\nKeine Überschneidung mit anderen Abschnitten\n\n\n\n\n\n\n\n\n\n\nRepsonse\n\n\n\nEinleitung: ok\nInhaltliche Anforderungen an die Dokumentation: ok\nFormat und Struktur der Dokumentation: ok\nWas macht eine gute Dokumentation aus: “Nutzungshilfen außerhalb der Dokumentation” könnte in “Format und Struktur” übernommen werden\n(Teil-)automatisierte Dokumentationswerkzeuge: ok\nBest Practices, Vorlagen und Checklisten: “Checkliste für die Mindest-Dokumentation” könnte in “Inhaltliche Anforderungen” integriert werden\nImplementierung aller Vorschläge als ready-to-use Repository: ok\nFazit: ok", + "crumbs": [ + "Home", + "Serious", + "Writing", + "Artikelbewertung durch ein Large Language Model (LLM) - Ein Beispiel aus der Praxis" + ] } ] \ No newline at end of file diff --git a/dist/sitemap.xml b/dist/sitemap.xml index 1aae6c7..8451d0b 100644 --- a/dist/sitemap.xml +++ b/dist/sitemap.xml @@ -1,5 +1,9 @@ + + https://drezil.de/index.html + 2025-06-13T08:17:37.525Z + https://drezil.de/Writing/Obsidian-RAG.html 2025-05-09T19:08:56.279Z @@ -9,79 +13,79 @@ 2025-06-05T13:06:32.542Z - https://drezil.de/Uni/Lernerfolg_an_der_Uni.html - 2025-05-09T18:24:18.456Z - - - https://drezil.de/Opinions/Keyboard-Layout.html - 2025-05-09T18:31:10.026Z - - - https://drezil.de/Coding/Haskell/Advantages.html - 2025-05-09T07:11:06.603Z - - - https://drezil.de/Coding/Haskell/FFPiH.html - 2025-05-09T07:07:51.739Z - - - https://drezil.de/Coding/Haskell/Webapp-Example/MyService_Types.hs.html - 2025-05-09T18:05:11.127Z - - - https://drezil.de/Coding/Haskell/Code Snippets/Morphisms.html - 2025-05-09T18:06:01.303Z - - - https://drezil.de/About/index.html - 2025-05-09T07:01:07.584Z - - - https://drezil.de/About/Work.html - 2025-05-08T16:36:47.996Z - - - https://drezil.de/About/Experience.html - 2025-05-08T16:36:47.996Z - - - https://drezil.de/About/Extracurricular.html - 2025-05-08T16:36:48.003Z - - - https://drezil.de/Coding/Haskell/Code Snippets/Monoid.html - 2025-05-09T07:18:58.729Z - - - https://drezil.de/Coding/Haskell/Webapp-Example/Main.hs.html - 2025-05-09T18:04:40.395Z - - - https://drezil.de/Coding/Haskell/Webapp-Example/index.html - 2025-05-09T19:20:12.478Z - - - https://drezil.de/Coding/Haskell/Lenses.html - 2025-05-09T07:10:26.159Z - - - https://drezil.de/Health/Issues.html - 2025-05-09T19:29:56.847Z + https://drezil.de/Stuff/Bielefeldverschwoerung.html + 2025-05-09T18:31:48.525Z https://drezil.de/Opinions/Editors.html 2025-05-09T18:29:47.541Z - https://drezil.de/Stuff/Bielefeldverschwoerung.html - 2025-05-09T18:31:48.525Z + https://drezil.de/Health/Issues.html + 2025-05-09T19:29:56.847Z - https://drezil.de/index.html - 2025-05-09T19:33:02.487Z + https://drezil.de/Coding/Haskell/Lenses.html + 2025-05-09T07:10:26.159Z + + + https://drezil.de/Coding/Haskell/Webapp-Example/index.html + 2025-05-09T19:20:12.478Z + + + https://drezil.de/Coding/Haskell/Webapp-Example/Main.hs.html + 2025-05-09T18:04:40.395Z + + + https://drezil.de/Coding/Haskell/Code Snippets/Monoid.html + 2025-05-09T07:18:58.729Z + + + https://drezil.de/About/Extracurricular.html + 2025-05-08T16:36:48.003Z + + + https://drezil.de/About/Experience.html + 2025-05-08T16:36:47.996Z + + + https://drezil.de/About/Work.html + 2025-05-08T16:36:47.996Z + + + https://drezil.de/About/index.html + 2025-05-09T07:01:07.584Z + + + https://drezil.de/Coding/Haskell/Code Snippets/Morphisms.html + 2025-05-09T18:06:01.303Z + + + https://drezil.de/Coding/Haskell/Webapp-Example/MyService_Types.hs.html + 2025-05-09T18:05:11.127Z + + + https://drezil.de/Coding/Haskell/FFPiH.html + 2025-05-09T07:07:51.739Z + + + https://drezil.de/Coding/Haskell/Advantages.html + 2025-05-09T07:11:06.603Z + + + https://drezil.de/Opinions/Keyboard-Layout.html + 2025-05-09T18:31:10.026Z + + + https://drezil.de/Uni/Lernerfolg_an_der_Uni.html + 2025-05-09T18:24:18.456Z https://drezil.de/Writing/documentation.html 2025-06-05T17:18:21.768Z + + https://drezil.de/Obsidian-Articles/Artikelbewertung durch LLM.html + 2025-06-13T08:21:41.581Z + diff --git a/index.qmd b/index.qmd index e240575..79a56b5 100644 --- a/index.qmd +++ b/index.qmd @@ -9,6 +9,7 @@ listing: max-description-length: 250 contents: - "Writing" + - "Obsidian-Articles" - "Coding" - "Health" - "Uni" diff --git a/pandoc-filter/obsidian-callouts.lua b/pandoc-filter/obsidian-callouts.lua new file mode 100644 index 0000000..427aa90 --- /dev/null +++ b/pandoc-filter/obsidian-callouts.lua @@ -0,0 +1,46 @@ +local stringify = (require("pandoc.utils")).stringify +-- idea aken from https://forum.obsidian.md/t/rendering-callouts-similarly-in-pandoc/40020/6 and modified to work with Quarto 1.3 + +local function has_value(tab, val) + for index, value in ipairs(tab) do + if value == val then + return true + end + end + + return false +end + +function BlockQuote(el) + local start = el.content[1] + + if start.t == "Para" and start.content[1].t == "Str" and start.content[1].text:match("^%[!%w+%][-+]?$") then + local firstline = stringify(start.content[1]) + local _, _, ctype = firstline:find("%[!(%w+)%]") + local titlevar = stringify(start.content):match("^%[!%w+%](.-)$") + if ctype:lower() == "info" then + ctype = "note" + end + if ctype:lower() == "question" then + ctype = "warning" + end + if ctype:lower() == "success" then + ctype = "tip" + end + + if not has_value({ "note", "warning", "important", "tip", "caution" }, ctype:lower()) then + print(ctype + " is no valid quarto-callout. Change or add special case to lua-script.\nForced to 'note'") + ctype = "note" + end + + el.content:remove(1) + -- Create Quarto callout + return quarto.Callout({ + type = ctype:lower(), + title = titlevar, + content = el.content, + }) + else + return el + end +end