emanote/static_gen/Coding/Haskell/Lenses.html

2252 lines
69 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8' />
<meta name='viewport' content='width=device-width, initial-scale=1' />
<title>
Lenses Home
</title>
<meta property='og:description' content='Die Idee dahinter ist, dass man Zugriffsabstraktionen über Daten verknüpfen kann. Als einfachen Datenstruktur kann man einen Record mit der entsprechenden Syntax nehmen.' />
<meta property='og:site_name' content='Home' />
<meta property='og:image' content />
<meta property='og:type' content='website' />
<meta property='og:title' content='Lenses' />
<base href='/' />
<link href='favicon.svg' rel='icon' />
<script>
window.MathJax = {
startup: {
ready: () => {
MathJax.startup.defaultReady();
}
}
};
</script>
<script async id='MathJax-script' src='https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js'></script>
<!-- mermaid.js --><script src='https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js'></script>
<script>
mermaid.initialize({startOnLoad:false});
mermaid.init(undefined,document.querySelectorAll(".mermaid"));
</script>
<!-- highlight.js -->
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/hybrid.min.css' />
<script src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js'></script>
<!-- Include languages that Emanote itself uses -->
<script src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/languages/haskell.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/languages/nix.min.js'></script>
<script>hljs.highlightAll();</script>
<link href='tailwind.css?instanceId=582ab847-21b9-4a5c-8fed-5c509514ffc2' rel='stylesheet' type='text/css' />
<style>
/* Heist error element */
strong.error {
color: lightcoral;
font-size: 90%;
font-family: monospace;
}
/* External link icon */
a[data-linkicon=""]::after {
content: ""
}
a[data-linkicon=none]::after {
content: ""
}
a[data-linkicon="external"]::after {
content: url('data:image/svg+xml,\
<svg xmlns="http://www.w3.org/2000/svg" height="0.7em" viewBox="0 0 20 20"> \
<g style="stroke:gray;stroke-width:1"> \
<line x1="5" y1="5" x2="5" y2="14" /> \
<line x1="14" y1="9" x2="14" y2="14" /> \
<line x1="5" y1="14" x2="14" y2="14" /> \
<line x1="5" y1="5" x2="9" y2="5" /> \
<line x1="10" y1="2" x2="17" y2="2" /> \
<line x1="17" y1="2" x2="17" y2="9" /> \
<line x1="10" y1="9" x2="17" y2="2" style="stroke-width:1.0" /> \
</g> \
</svg>');
}
a[data-linkicon="external"][href^="mailto:"]::after {
content: url('data:image/svg+xml,\
<svg \
xmlns="http://www.w3.org/2000/svg" \
height="0.7em" \
fill="none" \
viewBox="0 0 24 24" \
stroke="gray" \
stroke-width="2"> \
<path \
stroke-linecap="round" \
stroke-linejoin="round" \
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" /> \
</svg>');
}
</style>
<!-- What goes in this file will appear on near the end of <head>--><link rel='preload' href='_emanote-static/fonts/Work_Sans/WorkSans-VariableFont_wght.ttf' as='font' type='font/ttf' crossorigin />
<style>
@font-face {
font-family: 'WorkSans';
/* FIXME: This ought to be: ${ema:emanoteStaticLayerUrl}/fonts/Work_Sans/WorkSans-VariableFont_wght.ttf */
src: url(_emanote-static/fonts/Work_Sans/WorkSans-VariableFont_wght.ttf) format("truetype");
font-display: swap;
}
body {
font-family: 'WorkSans', sans-serif;
font-variation-settings: 'wght' 350;
}
a.mavenLinkBold {
font-variation-settings: 'wght' 400;
}
strong {
font-variation-settings: 'wght' 500;
}
h1,
h2,
h3,
h4,
h5,
h6,
header,
.header-font {
font-family: 'WorkSans', sans-serif;
}
h1 {
font-variation-settings: 'wght' 500;
}
h2 {
font-variation-settings: 'wght' 400;
}
h3 {
font-variation-settings: 'wght' 300;
}
</style>
<link rel='stylesheet' href='_emanote-static/inverted-tree.css' />
<link rel='stylesheet' href='_emanote-static/stork/flat.css' />
<!-- Custom Stork-search styling for Emanote -->
<style>
#stork-search-container {
z-index: 1000;
background-color: rgb(15 23 42/.8);
}
.stork-overflow-hidden-important {
overflow: hidden !important;
}
</style>
<script src='_emanote-static/stork/stork.js'></script>
<script id='emanote-stork' data-emanote-base-url='/'>
window.emanote = {};
window.emanote.stork = {
searchShown: false,
indexIsStale: false,
toggleSearch: function () {
window.emanote.stork.refreshIndex();
document.getElementById('stork-search-container').classList.toggle('hidden');
window.emanote.stork.searchShown = document.body.classList.toggle('stork-overflow-hidden-important');
if (window.emanote.stork.searchShown) {
document.getElementById('stork-search-input').focus();
}
},
clearSearch: function () {
document.getElementById('stork-search-container').classList.add('hidden');
document.body.classList.remove('stork-overflow-hidden-important');
window.emanote.stork.searchShown = false;
},
getBaseUrl: function () {
const baseUrl = document.getElementById("emanote-stork").getAttribute('data-emanote-base-url') || '/';
return baseUrl;
},
registerIndex: function (options) {
const indexName = 'emanote-search'; // used to match input[data-stork] attribute value
const indexUrl = window.emanote.stork.getBaseUrl() + '-/stork.st';
stork.register(
indexName,
indexUrl,
options);
},
init: function () {
if (document.readyState !== 'complete') {
window.addEventListener('load', function () {
stork.initialize(window.emanote.stork.getBaseUrl() + '_emanote-static/stork/stork.wasm');
window.emanote.stork.registerIndex();
});
document.addEventListener('keydown', event => {
if (window.emanote.stork.searchShown && event.key === 'Escape') {
window.emanote.stork.clearSearch();
event.preventDefault();
} else if ((event.key == 'k' || event.key == 'K') && (event.ctrlKey || event.metaKey)) {
window.emanote.stork.toggleSearch();
event.preventDefault();
}
});
} else {
// This section is called during Ema's hot reload.
//
// Mark the current index as stale, and refresh it *only when* the
// user actually invokes search.
//
// We do not refresh the index *right away*, as that will cause
// memory leaks in the browser. See
// https://github.com/srid/emanote/issues/411#issuecomment-1402056235
console.log("stork: Marking index as stale");
window.emanote.stork.markIndexAsStale();
}
},
markIndexAsStale: function () {
window.emanote.stork.indexIsStale = true;
},
refreshIndex: function () {
if (window.emanote.stork.indexIsStale) {
console.log("stork: Reloading index");
window.emanote.stork.indexIsStale = false;
// NOTE: This will leak memory. See the comment above.
window.emanote.stork.registerIndex({ forceOverwrite: true });
}
}
};
window.emanote.stork.init();
</script>
</head>
<!-- DoNotFormat -->
<!-- DoNotFormat -->
<body class='bg-gray-400 overflow-y-scroll'>
<div class='container mx-auto'>
<nav id='breadcrumbs' class='w-full text-gray-700 md:hidden'>
<div class='flex justify-left'>
<div class='w-full px-2 py-2 bg-gray-50'>
<ul class='flex flex-wrap text-lg'>
<li class='inline-flex items-center'>
<img style='width: 1rem;' src='favicon.svg' />
</li>
<li class='inline-flex items-center'>
<a class='px-1 font-bold' href=''>
Home
</a>
<svg fill='currentColor' viewBox='0 0 20 20' class='w-auto h-5 text-gray-400'>
<path fill-rule='evenodd' d='M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z' clip-rule='evenodd'></path>
</svg>
</li>
<li class='inline-flex items-center'>
<a class='px-1 font-bold' href='Coding'>
Coding
</a>
<svg fill='currentColor' viewBox='0 0 20 20' class='w-auto h-5 text-gray-400'>
<path fill-rule='evenodd' d='M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z' clip-rule='evenodd'></path>
</svg>
</li>
<li class='inline-flex items-center'>
<a class='px-1 font-bold' href='Coding/Haskell'>
Haskell
</a>
<svg fill='currentColor' viewBox='0 0 20 20' class='w-auto h-5 text-gray-400'>
<path fill-rule='evenodd' d='M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z' clip-rule='evenodd'></path>
</svg>
</li>
</ul>
</div>
<button class='inline px-2 py-1 bg-gray-50 outline-none cursor-pointer focus:outline-none' title='Search (Ctrl+K)' type='button' onclick='window.emanote.stork.toggleSearch()'>
<svg xmlns='http://www.w3.org/2000/svg' style='width: 1rem;' class='hover:text-purple-700' f
fill='none' viewBox='0 0 24 24' stroke='currentColor' stroke-width='2'>
<path stroke-linecap='round' stroke-linejoin='round' d='M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z'></path>
</svg>
</button>
<button class='inline px-2 py-1 text-white bg-purple-600 outline-none cursor-pointer focus:outline-none' title='Toggle sidebar' type='button' onclick="toggleHidden('sidebar')">
<svg xmlns='http://www.w3.org/2000/svg' class='w-4' fill='none' viewBox='0 0 24 24' stroke='currentColor'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 6h16M4 12h16M4 18h16'></path>
</svg>
</button>
<script>
function toggleHidden(elemId) {
document.getElementById(elemId).classList.toggle("hidden");
}
</script>
</div>
</nav>
<div id='container' class='flex flex-nowrap flex-col md:flex-row bg-gray-50 md:mt-8 md:shadow-2xl md:mb-8'>
<!-- Sidebar column -->
<nav id='sidebar' class='flex-shrink hidden leading-relaxed md:block md:sticky md:top-0 md:h-full md:w-48 xl:w-64'>
<div class='px-2 py-2 text-gray-800'>
<div id='indexing-links' class='flex flex-row float-right p-2 space-x-2 text-gray-500'>
<a href='-/tags' title='View tags'>
<svg style='width: 1rem;' class='hover:text-purple-700' fill='none' stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z'>
</path>
</svg>
</a>
<a href='-/all' title='Expand full tree'>
<svg style='width: 1rem;' class='hover:text-purple-700' fill='none' stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4'>
</path>
</svg>
</a>
<a title='Search (Ctrl+K)' class='cursor-pointer' onclick='window.emanote.stork.toggleSearch()'>
<svg xmlns='http://www.w3.org/2000/svg' style='width: 1rem;' class='hover:text-purple-700' f
fill='none' viewBox='0 0 24 24' stroke='currentColor' stroke-width='2'>
<path stroke-linecap='round' stroke-linejoin='round' d='M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z'></path>
</svg>
</a>
</div>
<div id='site-logo' class='pl-2'>
<div class='flex items-center my-2 space-x-2 justify-left'>
<a href='' title='Go to Home'>
<!-- The style width attribute here is to prevent huge
icon from displaying at those rare occasions when Tailwind
hasn't kicked in immediately on page load
-->
<img style='width: 1rem;' class='transition transform hover:scale-110 hover:opacity-80' src='favicon.svg' />
</a>
<a class='font-bold truncate' title='Go to Home' href=''>
Home
</a>
</div>
</div>
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg xmlns='http://www.w3.org/2000/svg' class='w-4 h-4 flex-shrink-0 inline text-gray-700' viewBox='0 0 20 20' fill='currentColor'>
<path fill-rule='evenodd' d='M2 6a2 2 0 012-2h4l2 2h4a2 2 0 012 2v1H8a3 3 0 00-3 3v1.5a1.5 1.5 0 01-3 0V6z' clip-rule='evenodd'></path>
<path d='M6 12a2 2 0 012-2h8a2 2 0 012 2v2a2 2 0 01-2 2H2h2a2 2 0 002-2v-2z'></path>
</svg>
<a class='font-bold hover:underline truncate' title='About me - Drezil' href='About'>
About me - Drezil
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg class='w-4 h-4 flex-shrink-0 inline' fill='none' stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z'>
</path>
</svg>
<a class='hover:underline truncate' title='Curriculum Vitae' href='About/CV'>
Curriculum Vitae
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
</div>
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg class='w-4 h-4 flex-shrink-0 inline' fill='none' stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z'>
</path>
</svg>
<a class='hover:underline truncate' title='Highlights of my experiences in the programming world' href='About/Experience'>
Highlights of my experiences in the programming world
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
</div>
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg class='w-4 h-4 flex-shrink-0 inline' fill='none' stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z'>
</path>
</svg>
<a class='hover:underline truncate' title='Work-Experience' href='About/Work'>
Work-Experience
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
</div>
</div>
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg xmlns='http://www.w3.org/2000/svg' class='w-4 h-4 flex-shrink-0 inline text-gray-700' viewBox='0 0 20 20' fill='currentColor'>
<path fill-rule='evenodd' d='M2 6a2 2 0 012-2h4l2 2h4a2 2 0 012 2v1H8a3 3 0 00-3 3v1.5a1.5 1.5 0 01-3 0V6z' clip-rule='evenodd'></path>
<path d='M6 12a2 2 0 012-2h8a2 2 0 012 2v2a2 2 0 01-2 2H2h2a2 2 0 002-2v-2z'></path>
</svg>
<a class='font-bold hover:underline truncate' title='Android' href='Android'>
Android
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg class='w-4 h-4 flex-shrink-0 inline' fill='none' stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z'>
</path>
</svg>
<a class='hover:underline truncate' title='Einrichtung Android-Smartphones' href='Android/Einrichtung'>
Einrichtung Android-Smartphones
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
</div>
</div>
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg xmlns='http://www.w3.org/2000/svg' class='w-4 h-4 flex-shrink-0 inline text-gray-700' viewBox='0 0 20 20' fill='currentColor'>
<path fill-rule='evenodd' d='M2 6a2 2 0 012-2h4l2 2h4a2 2 0 012 2v1H8a3 3 0 00-3 3v1.5a1.5 1.5 0 01-3 0V6z' clip-rule='evenodd'></path>
<path d='M6 12a2 2 0 012-2h8a2 2 0 012 2v2a2 2 0 01-2 2H2h2a2 2 0 002-2v-2z'></path>
</svg>
<a class='font-bold hover:underline truncate' title='Coding' href='Coding'>
Coding
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg xmlns='http://www.w3.org/2000/svg' class='w-4 h-4 flex-shrink-0 inline text-gray-700' viewBox='0 0 20 20' fill='currentColor'>
<path fill-rule='evenodd' d='M2 6a2 2 0 012-2h4l2 2h4a2 2 0 012 2v1H8a3 3 0 00-3 3v1.5a1.5 1.5 0 01-3 0V6z' clip-rule='evenodd'></path>
<path d='M6 12a2 2 0 012-2h8a2 2 0 012 2v2a2 2 0 01-2 2H2h2a2 2 0 002-2v-2z'></path>
</svg>
<a class='font-bold hover:underline truncate' title='Haskell' href='Coding/Haskell'>
Haskell
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg class='w-4 h-4 flex-shrink-0 inline' fill='none' stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z'>
</path>
</svg>
<a class='hover:underline truncate' title='Talks und Posts zu Haskell' href='Coding/Haskell/Advantages'>
Talks und Posts zu Haskell
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
</div>
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg xmlns='http://www.w3.org/2000/svg' class='w-4 h-4 flex-shrink-0 inline text-gray-500' viewBox='0 0 20 20' fill='currentColor'>
<path d='M2 6a2 2 0 012-2h5l2 2h5a2 2 0 012 2v6a2 2 0 01-2 2H4a2 2 0 01-2-2V6z'></path>
</svg>
<a class='hover:underline truncate' title='Code-Snippets' href='Coding/Haskell/Code%20Snippets'>
Code-Snippets
</a>
<span class='text-gray-300' title='2 children inside'>
2
</span>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
</div>
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg class='w-4 h-4 flex-shrink-0 inline' fill='none' stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z'>
</path>
</svg>
<a class='hover:underline truncate' title='Fortgeschrittene funktionale Programmierung in Haskell' href='Coding/Haskell/FFPiH'>
Fortgeschrittene funktionale Programmierung in Haskell
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
</div>
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg class='w-4 h-4 flex-shrink-0 inline' fill='none' stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z'>
</path>
</svg>
<a class='font-bold text-purple-600 hover:underline truncate' title='Lenses' href='Coding/Haskell/Lenses'>
Lenses
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
</div>
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg xmlns='http://www.w3.org/2000/svg' class='w-4 h-4 flex-shrink-0 inline text-gray-500' viewBox='0 0 20 20' fill='currentColor'>
<path d='M2 6a2 2 0 012-2h5l2 2h5a2 2 0 012 2v6a2 2 0 01-2 2H4a2 2 0 01-2-2V6z'></path>
</svg>
<a class='hover:underline truncate' title='Webapp-Development in Haskell' href='Coding/Haskell/Webapp-Example'>
Webapp-Development in Haskell
</a>
<span class='text-gray-300' title='2 children inside'>
2
</span>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
</div>
</div>
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg class='w-4 h-4 flex-shrink-0 inline' fill='none' stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z'>
</path>
</svg>
<a class='hover:underline truncate' title='Openapi-generator' href='Coding/OpenAPI'>
Openapi-generator
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
</div>
</div>
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg xmlns='http://www.w3.org/2000/svg' class='w-4 h-4 flex-shrink-0 inline text-gray-700' viewBox='0 0 20 20' fill='currentColor'>
<path fill-rule='evenodd' d='M2 6a2 2 0 012-2h4l2 2h4a2 2 0 012 2v1H8a3 3 0 00-3 3v1.5a1.5 1.5 0 01-3 0V6z' clip-rule='evenodd'></path>
<path d='M6 12a2 2 0 012-2h8a2 2 0 012 2v2a2 2 0 01-2 2H2h2a2 2 0 002-2v-2z'></path>
</svg>
<a class='font-bold hover:underline truncate' title='Health' href='Health'>
Health
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg class='w-4 h-4 flex-shrink-0 inline' fill='none' stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z'>
</path>
</svg>
<a class='hover:underline truncate' title='Mental Health' href='Health/Issues'>
Mental Health
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
</div>
</div>
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg class='w-4 h-4 flex-shrink-0 inline' fill='none' stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z'>
</path>
</svg>
<a class='hover:underline truncate' title='Logik für Dummies' href='Logik'>
Logik für Dummies
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
</div>
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg xmlns='http://www.w3.org/2000/svg' class='w-4 h-4 flex-shrink-0 inline text-gray-700' viewBox='0 0 20 20' fill='currentColor'>
<path fill-rule='evenodd' d='M2 6a2 2 0 012-2h4l2 2h4a2 2 0 012 2v1H8a3 3 0 00-3 3v1.5a1.5 1.5 0 01-3 0V6z' clip-rule='evenodd'></path>
<path d='M6 12a2 2 0 012-2h8a2 2 0 012 2v2a2 2 0 01-2 2H2h2a2 2 0 002-2v-2z'></path>
</svg>
<a class='font-bold hover:underline truncate' title='Opinions' href='Opinions'>
Opinions
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg class='w-4 h-4 flex-shrink-0 inline' fill='none' stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z'>
</path>
</svg>
<a class='hover:underline truncate' title='Editors' href='Opinions/Editors'>
Editors
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
</div>
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg class='w-4 h-4 flex-shrink-0 inline' fill='none' stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z'>
</path>
</svg>
<a class='hover:underline truncate' title='Keyboard-Layouts' href='Opinions/Layout'>
Keyboard-Layouts
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
</div>
</div>
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg xmlns='http://www.w3.org/2000/svg' class='w-4 h-4 flex-shrink-0 inline text-gray-700' viewBox='0 0 20 20' fill='currentColor'>
<path fill-rule='evenodd' d='M2 6a2 2 0 012-2h4l2 2h4a2 2 0 012 2v1H8a3 3 0 00-3 3v1.5a1.5 1.5 0 01-3 0V6z' clip-rule='evenodd'></path>
<path d='M6 12a2 2 0 012-2h8a2 2 0 012 2v2a2 2 0 01-2 2H2h2a2 2 0 002-2v-2z'></path>
</svg>
<a class='font-bold hover:underline truncate' title='Stuff' href='Stuff'>
Stuff
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg class='w-4 h-4 flex-shrink-0 inline' fill='none' stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z'>
</path>
</svg>
<a class='hover:underline truncate' title='Die Bielefeld-Verschwörung' href='Stuff/Bielefeldverschwoerung'>
Die Bielefeld-Verschwörung
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
</div>
</div>
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg class='w-4 h-4 flex-shrink-0 inline' fill='none' stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z'>
</path>
</svg>
<a class='hover:underline truncate' title='Todo' href='TODO'>
Todo
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
</div>
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg xmlns='http://www.w3.org/2000/svg' class='w-4 h-4 flex-shrink-0 inline text-gray-700' viewBox='0 0 20 20' fill='currentColor'>
<path fill-rule='evenodd' d='M2 6a2 2 0 012-2h4l2 2h4a2 2 0 012 2v1H8a3 3 0 00-3 3v1.5a1.5 1.5 0 01-3 0V6z' clip-rule='evenodd'></path>
<path d='M6 12a2 2 0 012-2h8a2 2 0 012 2v2a2 2 0 01-2 2H2h2a2 2 0 002-2v-2z'></path>
</svg>
<a class='font-bold hover:underline truncate' title='Uni' href='Uni'>
Uni
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg class='w-4 h-4 flex-shrink-0 inline' fill='none' stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z'>
</path>
</svg>
<a class='hover:underline truncate' title='Studium generale / University-Life' href='Uni/Extracurricular'>
Studium generale / University-Life
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
</div>
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg class='w-4 h-4 flex-shrink-0 inline' fill='none' stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z'>
</path>
</svg>
<a class='hover:underline truncate' title='Wie lerne ich richtig an der Uni?' href='Uni/Lernerfolg_an_der_Uni'>
Wie lerne ich richtig an der Uni?
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
</div>
</div>
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg xmlns='http://www.w3.org/2000/svg' class='w-4 h-4 flex-shrink-0 inline text-gray-700' viewBox='0 0 20 20' fill='currentColor'>
<path fill-rule='evenodd' d='M2 6a2 2 0 012-2h4l2 2h4a2 2 0 012 2v1H8a3 3 0 00-3 3v1.5a1.5 1.5 0 01-3 0V6z' clip-rule='evenodd'></path>
<path d='M6 12a2 2 0 012-2h8a2 2 0 012 2v2a2 2 0 01-2 2H2h2a2 2 0 002-2v-2z'></path>
</svg>
<a class='font-bold hover:underline truncate' title='Unix' href='Unix'>
Unix
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg class='w-4 h-4 flex-shrink-0 inline' fill='none' stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z'>
</path>
</svg>
<a class='hover:underline truncate' title='SSH-Filter' href='Unix/SSH-Filter'>
SSH-Filter
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
</div>
</div>
</div>
</nav>
<!-- Main body column -->
<div class='flex-1 w-full overflow-x-auto bg-white'>
<main class='px-4 py-4'>
<!-- DoNotFormat -->
<!-- DoNotFormat -->
<nav id='uptree' class='flipped tree' style='transform-origin: 50%;'>
<ul class='root'>
<li>
<ul>
<li>
<div class='text-gray-900 forest-link'>
<a href='Coding/Haskell/Advantages'>
Talks und Posts zu Haskell
</a>
</div>
<ul>
<li>
<div class='text-gray-900 forest-link'>
<a href='Coding/Haskell'>
Haskell
</a>
</div>
<ul>
<li>
<div class='text-gray-900 forest-link'>
<a href='Coding'>
Coding
</a>
</div>
<ul>
<li>
<div class='text-gray-900 forest-link'>
<a href=''>
Home
</a>
</div>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>
<div class='text-gray-900 forest-link'>
<a href='Coding/Haskell/FFPiH'>
Fortgeschrittene funktionale Programmierung in Haskell
</a>
</div>
<ul>
<li>
<div class='text-gray-900 forest-link'>
<a href='About'>
About me - Drezil
</a>
</div>
</li>
<li>
<div class='text-gray-900 forest-link'>
<a href='Uni/Extracurricular'>
Studium generale / University-Life
</a>
</div>
<ul>
<li>
<div class='text-gray-900 forest-link'>
<a href='Health/Issues'>
Mental Health
</a>
</div>
<ul>
<li>
<div class='text-gray-900 forest-link'>
<a href='Health'>
Health
</a>
</div>
</li>
</ul>
</li>
<li>
<div class='text-gray-900 forest-link'>
<a href='Uni'>
Uni
</a>
</div>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</nav>
<h1 class='flex items-end justify-center mb-4 p-3 bg-purple-100 text-5xl font-extrabold text-black rounded'>
<a class='z-40 tracking-tighter '>
Lenses
</a>
</h1>
<article class='overflow-auto'>
<!-- What goes in this file will appear on top of note body-->
<h2 id='wofür-brauchen-wir-das-überhaupt' class='mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Wofür brauchen wir das überhaupt?</h2>
<p class='mb-3'>
Die Idee dahinter ist, dass man Zugriffsabstraktionen über Daten verknüpfen kann. Als einfachen Datenstruktur kann man einen Record mit der entsprechenden Syntax nehmen.
</p>
<h3 id='beispiel' class='mt-6 mb-2 text-3xl font-bold text-gray-700'>Beispiel</h3><div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>data Person = P { name :: String
, addr :: Address
, salary :: Int }
data Address = A { road :: String
, city :: String
, postcode :: String }
-- autogeneriert unten anderem: addr :: Person -&gt; Address
setName :: String -&gt; Person -&gt; Person
setName n p = p { name = n } --record update notation
setPostcode :: String -&gt; Person -&gt; Person
setPostcode pc p
= p { addr = addr p { postcode = pc } }
-- update of a record inside a record</code></pre></div><h3 id='probleme' class='mt-6 mb-2 text-3xl font-bold text-gray-700'>Probleme</h3>
<p class='mb-3'>
Probleme mit diesem Code:
</p>
<ul class='my-3 ml-6 space-y-1 list-disc'>
<li>
für 1-Dimensionale Felder ist die record-syntax ok.
</li>
<li>
tiefere Ebenen nur umständlich zu erreichen
</li>
<li>
eigentlich wollen wir nur pe in p setzen, müssen aber über addr etc. gehen.
</li>
<li>
wir brauchen wissen über die “Zwischenstrukturen”, an denen wir nicht interessiert sind
</li>
</ul>
<h3 id='was-wir-gern-hätten' class='mt-6 mb-2 text-3xl font-bold text-gray-700'>Was wir gern hätten</h3><div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>data Person = P { name :: String
, addr :: Address
, salary :: Int }
-- a lens for each field
lname :: Lens' Person String
laddr :: Lens' Person Adress
lsalary :: Lens' Person Int
-- getter/setter for them
view :: Lens' s a -&gt; s -&gt; a
set :: Lens' s a -&gt; a -&gt; s -&gt; s
-- lens-composition
composeL :: Lens' s1 s2 -&gt; Lens s2 a -&gt; Lens' s1 a</code></pre></div><h3 id='wie-uns-das-hilft' class='mt-6 mb-2 text-3xl font-bold text-gray-700'>Wie uns das hilft</h3>
<p class='mb-3'>
Mit diesen Dingen (wenn wir sie hätten) könnte man dann
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>data Person = P { name :: String
, addr :: Address
, salary :: Int }
data Address = A { road :: String
, city :: String
, postcode :: String }
setPostcode :: String -&gt; Person -&gt; Person
setPostcode pc p
= set (laddr `composeL` lpostcode) pc p</code></pre></div>
<p class='mb-3'>
machen und wäre fertig.
</p>
<h2 id='trivialer-ansatz' class='mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Trivialer Ansatz</h2><h3 id='gettersetter-als-lens-methoden' class='mt-6 mb-2 text-3xl font-bold text-gray-700'>Getter/Setter als Lens-Methoden</h3><div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>data LensR s a = L { viewR :: s -&gt; a
, setR :: a -&gt; s -&gt; s }
composeL (L v1 u1) (L v2 u2)
= L (\s -&gt; v2 (v1 s))
(\a s -&gt; u1 (u2 a (v1 s)) s)</code></pre></div><h3 id='wieso-ist-das-schlecht' class='mt-6 mb-2 text-3xl font-bold text-gray-700'>Wieso ist das schlecht?</h3>
<ul class='my-3 ml-6 space-y-1 list-disc'>
<li>
extrem ineffizient
</li>
</ul>
<p class='mb-3'>
Auslesen traversiert die Datenstruktur, dann wird die Funktion angewendet und zum setzen wird die Datenstruktur erneut traversiert:
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>over :: LensR s a -&gt; (a -&gt; a) -&gt; s -&gt; s
over ln f s = setR l (f (viewR l s)) s</code></pre></div>
<ul class='my-3 ml-6 space-y-1 list-disc'>
<li>
Lösung: modify-funktion hinzufügen
</li>
</ul>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>data LensR s a
= L { viewR :: s -&gt; a
, setR :: a -&gt; s -&gt; s
, mod :: (a-&gt;a) -&gt; s -&gt; s
, modM :: (a-&gt;Maybe a) -&gt; s -&gt; Maybe s
, modIO :: (a-&gt;IO a) -&gt; s -&gt; IO s }</code></pre></div>
<p class='mb-3'>
Neues Problem: Für jeden Spezialfall muss die Lens erweitert werden.
</p>
<h3 id='something-in-common' class='mt-6 mb-2 text-3xl font-bold text-gray-700'>Something in common</h3>
<p class='mb-3'>
Man kann alle Monaden abstrahieren. Functor reicht schon:
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>data LensR s a
= L { viewR :: s -&gt; a
, setR :: a -&gt; s -&gt; s
, mod :: (a-&gt;a) -&gt; s -&gt; s
, modF :: Functor f =&gt; (a-&gt;f a) -&gt; s -&gt; f s }</code></pre></div>
<p class='mb-3'>
Idee: Die 3 darüberliegenden durch modF ausdrücken.
</p>
<h3 id='typ-einer-lens' class='mt-6 mb-2 text-3xl font-bold text-gray-700'>Typ einer Lens</h3>
<p class='mb-3'>
Wenn man das berücksichtigt, dann hat einen Lens folgenden Typ:
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>type Lens' s a = forall f. Functor f
=&gt; (a -&gt; f a) -&gt; s -&gt; f s</code></pre></div>
<p class='mb-3'>
Allerdings haben wir dann noch unseren getter/setter:
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>data LensR s a = L { viewR :: s -&gt; a
, setR :: a -&gt; s -&gt; s }</code></pre></div>
<p class='mb-3'>
Stellt sich raus: Die sind isomorph! Auch wenn die von den Typen her komplett anders aussehen.
</p>
<h2 id='benutzen-einer-lens-als-setter' class='mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Benutzen einer Lens als Setter</h2><div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>set :: Lens' s a -&gt; (a -&gt; s -&gt; s)
set ln a s = --...umm...
--:t ln =&gt; (a -&gt; f a) -&gt; s -&gt; f s
-- =&gt; get s out of f s to return it</code></pre></div>
<p class='mb-3'>
Wir können für f einfach die “Identity”-Monade nehmen, die wir nachher wegcasten können.
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>newtype Identity a = Identity a
-- Id :: a -&gt; Identity a
runIdentity :: Identity s -&gt; s
runIdentity (Identity x) = x
instance Functor Identity where
fmap f (Identity x) = Identity (f x)</code></pre></div>
<p class='mb-3'>
somit ist set einfach nur
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>set :: Lens' s a -&gt; (a -&gt; s -&gt; s)
set ln x s
= runIdentity (ls set_fld s)
where
set_fld :: a -&gt; Identity a
set_fld _ = Identity x
-- a was the OLD value.
-- We throw that away and set the new value</code></pre></div>
<p class='mb-3'>
oder kürzer (für nerds wie den Autor der Lens-Lib)
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>set :: Lens' s a -&gt; (a -&gt; s -&gt; s)
set ln x = runIdentity . ln (Identity . const x)</code></pre></div><h2 id='benutzen-einer-lens-als-modify' class='mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Benutzen einer Lens als Modify</h2>
<p class='mb-3'>
Dasselbe wie Set, nur dass wir den Parameter nicht entsorgen, sondern in die mitgelieferte Funktion stopfen.
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>over :: Lens' s a -&gt; (a -&gt; a) -&gt; s -&gt; s
over ln f = runIdentity . ln (Identity . f)</code></pre></div><h2 id='benutzen-einer-lens-als-getter' class='mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Benutzen einer Lens als Getter</h2><div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>view :: Lens' s a -&gt; (s -&gt; a)
view ln s = --...umm...
--:t ln =&gt; (a -&gt; f a) -&gt; s -&gt; f s
-- =&gt; get a out of the (f s) return-value
-- Wait, WHAT?</code></pre></div>
<p class='mb-3'>
Auch hier gibt es einen netten Funktor. Wir packen das “a” einfach in das “f” und werfen das “s” am Ende weg.
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>newtype Const v a = Const v
getConst :: Const v a -&gt; v
getConst (Const x) = x
instance Functor (Const v) where
fmap f (Const x) = Const x
-- throw f away. Nothing changes our const!</code></pre></div>
<p class='mb-3'>
somit ergibt sich
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>view :: Lens' s a -&gt; (s -&gt; a)
view ln s
= getConst (ln Const s)
-- Const :: s -&gt; Const a s</code></pre></div>
<p class='mb-3'>
oder nerdig
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>view :: Lens' s a -&gt; (s -&gt; a)
view ln = getConst . ln Const</code></pre></div><h2 id='lenses-bauen' class='mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Lenses bauen</h2>
<p class='mb-3'>
Nochmal kurz der Typ:
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>type Lens' s a = forall f. Functor f
=&gt; (a -&gt; f a) -&gt; s -&gt; f s</code></pre></div>
<p class='mb-3'>
Für unser Personen-Beispiel vom Anfang:
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>data Person = P { _name :: String, _salary :: Int }
name :: Lens' Person String
-- name :: Functor f =&gt; (String -&gt; f String)
-- -&gt; Person -&gt; f Person
name elt_fn (P n s)
= fmap (\n' -&gt; P n' s) (elt_fn n)
-- fmap :: Functor f =&gt; (a-&gt;b) -&gt; f a -&gt; f b - der Funktor, der alles verknüpft
-- \n' -&gt; .. :: String -&gt; Person - Funktion um das Element zu lokalisieren (WO wird ersetzt/gelesen/...)
-- elt_fn n :: f String - Funktion um das Element zu verändern (setzen, ändern, ...)</code></pre></div>
<p class='mb-3'>
Die Lambda-Funktion ersetzt einfach den Namen. Häufig sieht man auch
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>name elt_fn (P n s)
= (\n' -&gt; P n' s) &lt;$&gt; (elt_fn n)
-- | Focus | |Function|</code></pre></div><h2 id='wie-funktioniert-das-intern' class='mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Wie funktioniert das intern?</h2><div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>view name (P {_name="Fred", _salary=100})
-- inline view-function
= getConst (name Const (P {_name="Fred", _salary=100})
-- inline name
= getConst (fmap (\n' -&gt; P n' 100) (Const "Fred"))
-- fmap f (Const x) = Const x - Definition von Const
= getConst (Const "Fred")
-- getConst (Const x) = x
= "Fred"</code></pre></div>
<p class='mb-3'>
Dieser Aufruf hat KEINE Runtime-Kosten, weil der Compiler direkt die Adresse des Feldes einsetzen kann. Der gesamte Boilerplate-Code wird vom Compiler wegoptimiert.
</p>
<p class='mb-3'>
Dies gilt für jeden Funktor mit newtype, da das nur ein Typalias ist.
</p>
<h2 id='composing-lenses-und-deren-benutzung' class='mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Composing Lenses und deren Benutzung</h2>
<p class='mb-3'>
Wie sehen denn die Typen aus?
</p>
<p class='mb-3'>
Wir wollen ein
</p>
<blockquote class='py-0.5 px-4 mb-3 italic border-l-4 bg-gray-50 text-gray-600 border-gray-400 quote'>
<p class='mb-3'>
Lens s1 s2 -&gt; Lens s2 a -&gt; Lens s1 a
</p>
</blockquote>
<p class='mb-3'>
Wir haben 2 Lenses
</p>
<blockquote class='py-0.5 px-4 mb-3 italic border-l-4 bg-gray-50 text-gray-600 border-gray-400 quote'>
<p class='mb-3'>
ln1 :: (s2 -&gt; f s2) -&gt; (s1 -&gt; f s1) ln2 :: (a -&gt; f a) -&gt; (s2 -&gt; f s2)
</p>
</blockquote>
<p class='mb-3'>
wenn man scharf hinsieht, kann man die verbinden
</p>
<blockquote class='py-0.5 px-4 mb-3 italic border-l-4 bg-gray-50 text-gray-600 border-gray-400 quote'>
<p class='mb-3'>
ln1 . ln2 :: (a -&gt; f s) -&gt; (s1 -&gt; f s1)
</p>
</blockquote>
<p class='mb-3'>
und erhält eine Lens. Sogar die Gewünschte!<br />Somit ist Lens-Composition einfach nur Function-Composition (.).
</p>
<h2 id='automatisieren-mit-template-haskell' class='mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Automatisieren mit Template-Haskell</h2>
<p class='mb-3'>
Der Code um die Lenses zu bauen ist für records immer Identisch:
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>data Person = P { _name :: String, _salary :: Int }
name :: Lens' Person String
name elt_fn (P n s) = (\n' -&gt; P n' s) &lt;$&gt; (elt_fn n)</code></pre></div>
<p class='mb-3'>
Daher kann man einfach
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>import Control.Lens.TH
data Person = P { _name :: String, _salary :: Int }
$(makeLenses ''Person)</code></pre></div>
<p class='mb-3'>
nehmen, was einem eine Lens für “name” und eine Lens für “salary” generiert.<br />Mit anderen Templates kann man auch weitere Dinge steuern (etwa wofür Lenses generiert werden, welches Prefix (statt _) man haben will etc. pp.).
</p>
<p class='mb-3'>
Will man das aber haben, muss man selbst in den Control.Lens.TH-Code schauen.
</p>
<h2 id='lenses-für-den-beispielcode' class='mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Lenses für den Beispielcode</h2><div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>import Control.Lens.TH
data Person = P { _name :: String
, _addr :: Address
, _salary :: Int }
data Address = A { _road :: String
, _city :: String
, _postcode :: String }
$(makeLenses ''Person)
$(makeLenses ''Address)
setPostcode :: String -&gt; Person -&gt; Person
setPostcode pc p = set (addr . postcode) pc p</code></pre></div><h2 id='shortcuts-mit-line-noise' class='mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Shortcuts mit “Line-Noise”</h2><div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>-- ...
setPostcode :: String -&gt; Person -&gt; Person
setPostcode pc p = addr . postcode .~ pc $ p
-- | Focus |set|to what|in where
getPostcode :: Person -&gt; String
getPostcode p = p ^. $ addr . postcode
-- |from|get| Focus |</code></pre></div>
<p class='mb-3'>
Es gibt drölf-zillionen weitere Infix-Operatoren (für Folds, Listenkonvertierungen, -traversierungen, …)
</p>
<h2 id='virtuelle-felder' class='mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Virtuelle Felder</h2>
<p class='mb-3'>
Man kann mit Lenses sogar Felder emulieren, die gar nicht da sind. Angenommen folgender Code:
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>data Temp = T { _fahrenheit :: Float }
$(makeLenses ''Temp)
-- liefert Lens: fahrenheit :: Lens Temp Float
centigrade :: Lens Temp Float
centigrade centi_fn (T faren)
= (\centi' -&gt; T (cToF centi'))
&lt;$&gt; (centi_fn (fToC faren))
-- cToF & fToC as Converter-Functions defined someplace else</code></pre></div>
<p class='mb-3'>
Hiermit kann man dann auch Funktionen, die auf Grad-Celsius rechnen auf Daten anwenden, die eigenlich nur Fahrenheit speichern, aber eine Umrechnung bereitstellen. Analog kann man auch einen Zeit-Datentypen definieren, der intern mit Sekunden rechnet (und somit garantiert frei von Fehlern wie -3 Minuten oder 37 Stunden ist)
</p>
<h2 id='non-record-strukturen' class='mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Non-Record Strukturen</h2>
<p class='mb-3'>
Das ganze kann man auch parametrisieren und auf Non-Record-Strukturen anwenden. Beispielhaft an einer Map verdeutlicht:
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>-- from Data.Lens.At
at :: Ord k =&gt; k -&gt; Lens' (Map k v) (Maybe v)
-- oder identisch, wenn man die Lens' auflöst:
at :: Ord k, forall f. Functor f =&gt; k -&gt; (Maybe v -&gt; f Maybe v) -&gt; Map k v -&gt; f Map k v
at k mb_fn m
= wrap &lt;$&gt; (mb_fn mv)
where
mv = Map.lookup k m
wrap :: Maybe v -&gt; Map k v
wrap (Just v') = Map.insert k v' m
wrap Nothing = case mv of
Nothing -&gt; m
Just _ -&gt; Map.delete k m
-- mb_fn :: Maybe v -&gt; f Maybe v</code></pre></div><h2 id='weitere-beispiele' class='mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Weitere Beispiele</h2>
<ul class='my-3 ml-6 space-y-1 list-disc'>
<li>
<p class='mb-3'>
Bitfields auf Strukturen die Bits haben (Ints, …) in Data.Bits.Lens
</p>
</li>
<li>
<p class='mb-3'>
Web-scraper in Package hexpat-lens
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>p ^.. _HTML' . to allNodes
. traverse . named "a"
. traverse . ix "href"
. filtered isLocal
. to trimSpaces</code></pre></div>
<p class='mb-3'>
Zieht alle externen Links aus dem gegebenen HTML-Code in p um weitere ziele fürs crawlen zu finden.
</p>
</li>
</ul>
<h2 id='erweiterungen' class='mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Erweiterungen</h2>
<p class='mb-3'>
Bisher hatten wir Lenses nur auf Funktoren F. Die nächstmächtigere Klasse ist Applicative.
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>type Traversal' s a = forall f. Applicative f
=&gt; (a -&gt; f a) -&gt; (s -&gt; f s)</code></pre></div>
<p class='mb-3'>
Da wir den Container identisch lassen (weder s noch a wurde angefasst) muss sich etwas anderes ändern. Statt eines einzelnen Focus erhalten wir viele Foci.
</p>
<p class='mb-3'>
Was ist ein Applicative überhaupt? Eine schwächere Monade (nur 1x Anwendung und kein Bind - dafür kann man die beliebig oft hintereinanderhängen).
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>class Functor f =&gt; Applicative f where
pure :: a -&gt; f a
(&lt;*&gt;) :: f (a -&gt; b) -&gt; f a -&gt; f b
-- Monade als Applicative:
pure = return
mf &lt;*&gt; mx = do { f &lt;- mf; x &lt;- mx; return (f x) }</code></pre></div>
<p class='mb-3'>
Recap: Was macht eine Lens:
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>data Adress = A { _road :: String
, _city :: String
, _postcode :: String }
road :: Lens' Adress String
road elt_fn (A r c p) = (\r' -&gt; A r' c p) &lt;$&gt; (elt_fn r)
-- | "Hole" | | Thing to put in|</code></pre></div>
<p class='mb-3'>
Wenn man nun road & city gleichzeitig bearbeiten will:
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>addr_strs :: Traversal' Address String
addr_strs elt_fn (A r c p)
= ... (\r' c' -&gt; A r' c' p) .. (elt_fn r) .. (elt_fn c) ..
-- | function with 2 "Holes"| first Thing | second Thing</code></pre></div>
<p class='mb-3'>
fmap kann nur 1 Loch stopfen, aber nicht mit n Löchern umgehen. Applicative mit &lt;*&gt; kann das.<br />Somit gibt sich
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>addr_strs :: Traversal' Address String
addr_strs elt_fn (A r c p)
= pure (\r' c' -&gt; A r' c' p) &lt;*&gt; (elt_fn r) &lt;*&gt; (elt_fn c)
-- lift in Appl. | function with 2 "Holes"| first Thing | second Thing
-- oder kürzer
addr_strs :: Traversal' Address String
addr_strs elt_fn (A r c p)
= (\r' c' -&gt; A r' c' p) &lt;$&gt; (elt_fn r) &lt;*&gt; (elt_fn c)
-- pure x &lt;*&gt; y == x &lt;$&gt; y</code></pre></div>
<p class='mb-3'>
Wie würd eine modify-funktion aussehen?
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>over :: Lens' s a -&gt; (a -&gt; a) -&gt; s -&gt; s
over ln f = runIdentity . ln (Identity . f)
over :: Traversal' s a -&gt; (a -&gt; a) -&gt; s -&gt; s
over ln f = runIdentity . ln (Identity . f)</code></pre></div>
<p class='mb-3'>
Der Code ist derselbe - nur der Typ ist generischer. Auch die anderen Dinge funktioniert diese Erweiterung (für Identity und Const muss man noch ein paar dummy-Instanzen schreiben um sie von Functor auf Applicative oder Monad zu heben
</p>
<ul class='my-3 ml-6 space-y-1 list-disc'>
<li>
konkret reicht hier die Instanzierung von Monoid). In der Lens-Library ist daher meist Monad m statt Functor f gefordert.
</li>
</ul>
<h2 id='wozu-dienen-die-erweiterungen' class='mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Wozu dienen die Erweiterungen?</h2>
<p class='mb-3'>
Man kann mit Foci sehr selektiv vorgehen. Auch kann man diese durch Funktionen steuern. Beispisweise eine Funktion anwenden auf
</p>
<ul class='my-3 ml-6 space-y-1 list-disc'>
<li>
Jedes 2. Listenelement
</li>
<li>
Alle graden Elemente in einem Baum
</li>
<li>
Alle Namen in einer Tabelle, deren Gehalt &gt; 10.000€ ist
</li>
</ul>
<p class='mb-3'>
Traversals und Lenses kann man trivial kombinieren (<code class='py-0.5 px-0.5 bg-gray-100'>lens . lens</code> =&gt; <code class='py-0.5 px-0.5 bg-gray-100'>lens</code>, <code class='py-0.5 px-0.5 bg-gray-100'>lens . traversal</code> =&gt; <code class='py-0.5 px-0.5 bg-gray-100'>traversal</code> etc.)
</p>
<h2 id='wie-es-in-lens-wirklich-aussieht' class='mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Wie es in Lens wirklich aussieht</h2>
<p class='mb-3'>
In diesem Artikel wurde nur auf Monomorphic Lenses eingegangen. In der richtigen Library ist eine Lens
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>type Lens' s a = Lens s s a a
type Lens s t a b = forall f. Functor f =&gt; (a -&gt; f b) -&gt; (s -&gt; f t)</code></pre></div>
<p class='mb-3'>
sodass sich auch die Typen ändern können um z.B. automatisch einen Konvertierten (sicheren) Typen aus einer unsicheren Datenstruktur zu geben.
</p>
<p class='mb-3'>
Die modify-Funktion over ist auch
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>&gt; over :: Profunctor p =&gt; Setting p s t a b -&gt; p a b -&gt; s -&gt; t</code></pre></div>
<blockquote class='py-0.5 px-4 mb-3 italic border-l-4 bg-gray-50 text-gray-600 border-gray-400 quote'>
<p class='mb-3'>
<em>Edward is deeply in thrall to abstractionitis</em> - Simon Peyton Jones
</p>
</blockquote>
<p class='mb-3'>
Lens alleine definiert 39 newtypes, 34 data-types und 194 Typsynonyme…<br />Ausschnitt
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>-- traverseOf :: Functor f =&gt; Iso s t a b -&gt; (a -&gt; f b) -&gt; s -&gt; f t
-- traverseOf :: Functor f =&gt; Lens s t a b -&gt; (a -&gt; f b) -&gt; s -&gt; f t
-- traverseOf :: Applicative f =&gt; Traversal s t a b -&gt; (a -&gt; f b) -&gt; s -&gt; f t
traverseOf :: Over p f s t a b -&gt; p a (f b) -&gt; s -&gt; f t</code></pre></div>
<p class='mb-3'>
dafuq?
</p>
<!-- div class="flex items-center justify-center mt-2">
<ema:metadata>
<with var="template">
<a class="text-gray-300 hover:text-${theme}-600 text-sm" title="Edit this page on GitHub"
href="${value:editBaseUrl}/${ema:note:source-path}">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
</a>
</with>
</ema:metadata>
</div -->
</article>
<div class='flex flex-col lg:flex-row lg:space-x-2'>
<div class='flex-1 p-4 mt-8 bg-gray-100 rounded'>
<header class='mb-2 text-xl font-semibold text-gray-500'>Links to this page</header>
<ul class='space-y-1'>
<li>
<a class='text-purple-600 mavenLinkBold hover:bg-purple-50' href='Coding/Haskell/Advantages'>
Talks und Posts zu Haskell
</a>
<div class='mb-4 overflow-auto text-sm text-gray-500'>
<div class='pl-2 mt-2 border-l-2 border-purple-200 hover:border-purple-500'>
<div><a href='https://skillsmatter.com/skillscasts/4251-lenses-compositional-data-access-and-manipulation' class='text-gray-600 hover:underline' data-linkicon='external' target='_blank' rel='noopener'>Lenses</a> (Registrierung nötig - kostenfrei), siehe auch: <a href='Coding/Haskell/Lenses' class='text-gray-600 font-bold hover:bg-gray-50' data-wikilink-type='WikiLinkBranch'>Lenses</a></div>
</div>
</div>
</li>
<li>
<a class='text-purple-600 mavenLinkBold hover:bg-purple-50' href='Coding/Haskell/FFPiH'>
Fortgeschrittene funktionale Programmierung in Haskell
</a>
<div class='mb-4 overflow-auto text-sm text-gray-500'>
<div class='pl-2 mt-2 border-l-2 border-purple-200 hover:border-purple-500'>
<div>Umschreiben von explizitem Argument-Passing hin zu Monad-Transformers mit stateful <a href='Coding/Haskell/Lenses' class='text-gray-600 font-bold hover:bg-gray-50' data-wikilink-type='WikiLinkBranch'>lenses</a></div>
</div>
</div>
</li>
</ul>
</div>
</div>
<section class='flex flex-wrap items-end justify-center my-4 space-x-2 space-y-2 font-mono text-sm'>
</section>
<!-- What goes in this file will at the very end of the main div -->
</main>
</div>
</div>
<footer class='flex items-center justify-center mt-2 mb-8 space-x-4 text-center text-gray-800'>
<div>
<a href='' title='Go to Home page'>
<svg xmlns='http://www.w3.org/2000/svg' class='w-6 h-6 hover:text-purple-700' fill='none' viewBox='0 0 24 24' stroke='currentColor'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6'></path>
</svg>
</a>
</div>
<div>
<a href='-/all' title='View Index'>
<svg class='w-6 h-6 hover:text-purple-700' fill='none' stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4'>
</path>
</svg>
</a>
</div>
<div>
<a href='https://emanote.srid.ca' target='_blank' title='Generated by Emanote 1.0.3.11'>
<img class='w-6 h-6 hover:text-purple-700' src='_emanote-static/emanote-logo.svg' />
</a>
</div>
<div>
<a href='-/tags' title='View tags'>
<svg class='w-6 h-6 hover:text-purple-700' fill='none' stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z'>
</path>
</svg>
</a>
</div>
<div>
<a href='-/tasks' title='View tasks'>
<svg xmlns='http://www.w3.org/2000/svg' class='w-6 h-6 hover:text-purple-700' fill='none' viewBox='0 0 24 24' stroke='currentColor'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z'></path>
</svg>
</a>
</div>
</footer>
</div>
<div id='stork-search-container' class='hidden fixed w-screen h-screen inset-0 backdrop-filter backdrop-blur-sm'>
<div class='fixed w-screen h-screen inset-0' onclick='window.emanote.stork.toggleSearch()'></div>
<div class='container mx-auto p-10 mt-10'>
<div class='stork-wrapper-flat container mx-auto'>
<input id='stork-search-input' data-stork='emanote-search' class='stork-input' placeholder='Search (Ctrl+K) ...' />
<div data-stork='emanote-search-output' class='stork-output'></div>
</div>
</div>
</div>
</body>
</html>