1198 lines
39 KiB
HTML
1198 lines
39 KiB
HTML
<!DOCTYPE html>
|
||
<html lang='en'>
|
||
|
||
<head>
|
||
<meta charset='UTF-8' />
|
||
<meta name='viewport' content='width=device-width, initial-scale=1' />
|
||
<title>
|
||
*-Morpisms – Home
|
||
</title>
|
||
|
||
|
||
<meta property='og:description' content='Backup eines Blogposts eines Kommilitonen:' />
|
||
<meta property='og:site_name' content='Home' />
|
||
<meta property='og:image' content />
|
||
<meta property='og:type' content='website' />
|
||
<meta property='og:title' content='*-Morpisms' />
|
||
|
||
|
||
<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=ecadcd50-ecaf-4d6e-98ea-7932c3b3d351' rel='stylesheet' type='text/css' />
|
||
|
||
<!-- Heist error element -->
|
||
<style>
|
||
strong.error {
|
||
color: lightcoral;
|
||
font-size: 90%;
|
||
font-family: monospace;
|
||
}
|
||
</style>
|
||
<!-- What goes in this file will appear on near the end of <head>--><link rel='preload' href='_emanote-static/fonts/Maven_Pro/MavenPro-VariableFont_wght.ttf' as='font' type='font/ttf' crossorigin />
|
||
|
||
<style>
|
||
@font-face {
|
||
font-family: 'MavenPro';
|
||
/* FIXME: This ought to be: ${ema:emanoteStaticLayerUrl}/fonts/Maven_Pro/MavenPro-VariableFont_wght.ttf */
|
||
src: url(_emanote-static/fonts/Maven_Pro/MavenPro-VariableFont_wght.ttf) format("truetype");
|
||
font-display: swap;
|
||
}
|
||
|
||
body {
|
||
font-family: 'MavenPro', sans-serif;
|
||
/* font-variation-settings: 'wght'300; */
|
||
}
|
||
|
||
a.mavenLinkBold {
|
||
font-variation-settings: 'wght'500;
|
||
}
|
||
|
||
strong {
|
||
font-variation-settings: 'wght'500;
|
||
}
|
||
|
||
h1,
|
||
h2,
|
||
h3,
|
||
h4,
|
||
h5,
|
||
h6,
|
||
header,
|
||
.header-font {
|
||
font-family: 'MavenPro', sans-serif;
|
||
}
|
||
</style>
|
||
|
||
|
||
<link rel='stylesheet' href='_emanote-static/inverted-tree.css' />
|
||
|
||
<link rel='stylesheet' href='https://files.stork-search.net/releases/v1.5.0/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='https://files.stork-search.net/releases/v1.5.0/stork.js'></script>
|
||
|
||
|
||
<script data-emanote-base-url='/'>
|
||
window.emanote = {};
|
||
window.emanote.stork = {
|
||
searchShown: false,
|
||
toggleSearch: function () {
|
||
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;
|
||
},
|
||
|
||
init: function () {
|
||
const indexName = 'emanote-search'; // used to match input[data-stork] attribute value
|
||
const baseUrl = document.currentScript.getAttribute('data-emanote-base-url') || '/';
|
||
const indexUrl = baseUrl + '-/stork.st';
|
||
if (document.readyState !== 'complete') {
|
||
window.addEventListener('load', function () {
|
||
stork.register(indexName, indexUrl);
|
||
});
|
||
|
||
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 {
|
||
// Override existing on Ema's hot-reload
|
||
stork.register(indexName, indexUrl, { 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='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>
|
||
|
||
<li class='inline-flex items-center'>
|
||
<a class='px-1 font-bold' href='Haskell/Code%20Snippets'>
|
||
Code-Snippets
|
||
</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-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='About' href='About'>
|
||
About
|
||
</a>
|
||
|
||
|
||
<span class='text-gray-300' title='3 children inside'>
|
||
3
|
||
</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 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='Android' href='Android'>
|
||
Android
|
||
</a>
|
||
|
||
|
||
<span class='text-gray-300' title='1 children inside'>
|
||
1
|
||
</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 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='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 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='Code-Snippets' href='Haskell/Code%20Snippets'>
|
||
Code-Snippets
|
||
</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='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='*-Morpisms' href='Haskell/Code%20Snippets/Morphisms'>
|
||
*-Morpisms
|
||
</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='Monoid? Da war doch was…' href='Haskell/Code%20Snippets/Monoid'>
|
||
Monoid? Da war doch was…
|
||
</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='Fortgeschrittene funktionale Programmierung in Haskell' href='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='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='Lenses' href='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 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='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='Webapp-Development in Haskell' href='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='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-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='Uni' href='Uni'>
|
||
Uni
|
||
</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 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='Unix' href='Unix'>
|
||
Unix
|
||
</a>
|
||
|
||
|
||
<span class='text-gray-300' title='1 children inside'>
|
||
1
|
||
</span>
|
||
|
||
|
||
</div>
|
||
|
||
<!-- Node's children forest, displayed only on active trees
|
||
TODO: Use <details> to toggle visibility?
|
||
-->
|
||
|
||
</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='Haskell/Advantages'>
|
||
Talks und Posts zu Haskell
|
||
</a>
|
||
</div>
|
||
|
||
|
||
<ul>
|
||
|
||
<li>
|
||
|
||
<div class='text-gray-900 forest-link'>
|
||
<a href='Haskell'>
|
||
Haskell
|
||
</a>
|
||
</div>
|
||
|
||
|
||
<ul>
|
||
|
||
<li>
|
||
|
||
<div class='text-gray-900 forest-link'>
|
||
<a href=''>
|
||
Home
|
||
</a>
|
||
</div>
|
||
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
<div class='text-gray-900 forest-link'>
|
||
<a href='Haskell/Code%20Snippets'>
|
||
Code-Snippets
|
||
</a>
|
||
</div>
|
||
|
||
|
||
</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 '>
|
||
*-Morpisms
|
||
</a>
|
||
</h1>
|
||
<article class='overflow-auto'>
|
||
<!-- What goes in this file will appear on top of note body-->
|
||
|
||
<p class='mb-3'>
|
||
<strong>Backup eines Blogposts eines Kommilitonen:</strong>
|
||
</p>
|
||
|
||
<p class='mb-3'>
|
||
This weekend I spend some time on Morphisms.
|
||
</p>
|
||
|
||
<p class='mb-3'>
|
||
Knowing that this might sound daunting to many dabbling Haskellers (like I am), I decided to write a real short MergeSort hylomorphism quickstarter.
|
||
</p>
|
||
|
||
<hr class='mb-3' />
|
||
|
||
<p class='mb-3'>
|
||
For those who need a refresher: MergeSort works by creating a balanced binary tree from the input list and directly collapsing it back into itself while treating the children as sorted lists and merging these with an O(n) algorithm.
|
||
</p>
|
||
|
||
<hr class='mb-3' />
|
||
|
||
<p class='mb-3'>
|
||
First the usual prelude:
|
||
</p>
|
||
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>{-# LANGUAGE DeriveFunctor #-}
|
||
{-# LANGUAGE TypeFamilies #-}
|
||
|
||
import Data.Functor.Foldable
|
||
import Data.List (splitAt, unfoldr)</code></pre></div>
|
||
<hr class='mb-3' />
|
||
|
||
<p class='mb-3'>
|
||
We will use a binary tree like this. Note that there is no explicit recursion used, but <code class='py-0.5 px-0.5 bg-gray-100'>NodeF</code> has two <em>holes</em>. These will eventually filled later.
|
||
</p>
|
||
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>data TreeF c f = EmptyF | LeafF c | NodeF f f
|
||
deriving (Eq, Show, Functor)</code></pre></div>
|
||
<hr class='mb-3' />
|
||
|
||
<p class='mb-3'>
|
||
Aside: We could use this as a <em>normal</em> binary tree by wrapping it in <code class='py-0.5 px-0.5 bg-gray-100'>Fix</code>: <code class='py-0.5 px-0.5 bg-gray-100'>type Tree a = Fix (TreeF a)</code> But this would require us to write our tree like <code class='py-0.5 px-0.5 bg-gray-100'>Fix (NodeF (Fix (LeafF 'l')) (Fix (LeafF 'r')))</code> which would get tedious fast. Luckily Edward build a much better way to do this into <em>recursion-schemes</em>. I will touch on this later.
|
||
</p>
|
||
|
||
<hr class='mb-3' />
|
||
|
||
<p class='mb-3'>
|
||
Without further ado we start to write a Coalgebra, which in my book is just a scary name for “function that is used to construct datastructures”.
|
||
</p>
|
||
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>unflatten :: [a] -> TreeF a [a]
|
||
unflatten ( []) = EmptyF
|
||
unflatten (x:[]) = LeafF x
|
||
unflatten ( xs) = NodeF l r where (l,r) = splitAt (length xs `div` 2) xs</code></pre></div>
|
||
<p class='mb-3'>
|
||
From the type signature it’s immediately obvious, that we take a list of ’a’s and use it to create a part of our tree.
|
||
</p>
|
||
|
||
<p class='mb-3'>
|
||
The nice thing is that due to the fact that we haven’t commited to a type in our tree nodes we can just put lists in there.
|
||
</p>
|
||
|
||
<hr class='mb-3' />
|
||
|
||
<p class='mb-3'>
|
||
Aside: At this point we could use this Coalgebra to construct (unsorted) binary trees from lists:
|
||
</p>
|
||
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>example1 = ana unflatten [1,3] == Fix (NodeF (Fix (LeafF 1)) (Fix (LeafF 3)))</code></pre></div>
|
||
<hr class='mb-3' />
|
||
|
||
<p class='mb-3'>
|
||
On to our sorting, tree-collapsing Algebra. Which again is just a creepy word for “function that is used to deconstruct datastructures”.
|
||
</p>
|
||
|
||
<p class='mb-3'>
|
||
The function <code class='py-0.5 px-0.5 bg-gray-100'>mergeList</code> is defined below and just merges two sorted lists into one sorted list in O(n), I would probably take this from the <code class='py-0.5 px-0.5 bg-gray-100'>ordlist</code> package if I were to implement this <em>for real</em>.
|
||
</p>
|
||
|
||
<p class='mb-3'>
|
||
Again we see that we can just construct our sorted output list from a <code class='py-0.5 px-0.5 bg-gray-100'>TreeF</code> that apparently contains just lists.
|
||
</p>
|
||
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>flatten :: Ord a => TreeF a [a] -> [a]
|
||
flatten EmptyF = []
|
||
flatten (LeafF c) = [c]
|
||
flatten (NodeF l r) = mergeLists l r </code></pre></div>
|
||
<hr class='mb-3' />
|
||
|
||
<p class='mb-3'>
|
||
Aside: We could use a Coalgebra to deconstruct trees:
|
||
</p>
|
||
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>example2 = cata flatten (Fix (NodeF (Fix (LeafF 3)) (Fix (LeafF 1)))) == [1,3]</code></pre></div>
|
||
<hr class='mb-3' />
|
||
|
||
<p class='mb-3'>
|
||
Now we just combine the Coalgebra and the Algebra with one from the functions from Edwards <code class='py-0.5 px-0.5 bg-gray-100'>recursion-schemes</code> library:
|
||
</p>
|
||
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>mergeSort :: Ord a => [a] -> [a]
|
||
mergeSort = hylo flatten unflatten
|
||
|
||
example3 = mergeSort [5,2,7,9,1,4] == [1,2,4,5,7,9] </code></pre></div>
|
||
<hr class='mb-3' />
|
||
|
||
<p class='mb-3'>
|
||
What have we gained?
|
||
</p>
|
||
|
||
<p class='mb-3'>
|
||
We have implemented a MergeSort variant in 9 lines of code, not counting the <code class='py-0.5 px-0.5 bg-gray-100'>mergeLists</code> function below. Not bad, but <a href='http://en.literateprograms.org/Merge_sort_(Haskell)' class='text-purple-600 hover:underline' target='_blank' rel='noopener'>this implementation</a> is not much longer.
|
||
</p>
|
||
|
||
<p class='mb-3'>
|
||
On the other hand the morphism based implementation cleanly describes what happens during construction and deconstruction of our intermediate structure.
|
||
</p>
|
||
|
||
<p class='mb-3'>
|
||
My guess is that, as soon as the algortihms get more complex, this will really make a difference.
|
||
</p>
|
||
|
||
<hr class='mb-3' />
|
||
|
||
<p class='mb-3'>
|
||
At this point I wasn’t sure if this was useful or remotely applicable. Telling someone “I spend a whole weekend learning about Hylomorphism” isn’t something the cool developer kids do.
|
||
</p>
|
||
|
||
<p class='mb-3'>
|
||
It appeared to me that maybe I should have a look at the Core to see what the compiler finally comes up with (edited for brevity):
|
||
</p>
|
||
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'> mergeSort :: [Integer] -> [Integer]
|
||
mergeSort =
|
||
\ (x :: [Integer]) ->
|
||
case x of wild {
|
||
[] -> [];
|
||
: x1 ds ->
|
||
case ds of _ {
|
||
[] -> : x1 ([]);
|
||
: ipv ipv1 ->
|
||
unfoldr
|
||
lvl9
|
||
(let {
|
||
p :: ([Integer], [Integer])
|
||
p =
|
||
case $wlenAcc wild 0 of ww { __DEFAULT ->
|
||
case divInt# ww 2 of ww4 { __DEFAULT ->
|
||
case tagToEnum# (<# ww4 0) of _ {
|
||
False ->
|
||
case $wsplitAt# ww4 wild of _ { (# ww2, ww3 #) -> (ww2, ww3) };
|
||
True -> ([], wild)
|
||
}
|
||
}
|
||
} } in
|
||
(case p of _ { (x2, ds1) -> mergeSort x2 },
|
||
case p of _ { (ds1, y) -> mergeSort y }))
|
||
}
|
||
}
|
||
end Rec }</code></pre></div>
|
||
<p class='mb-3'>
|
||
While I am not really competent in reading Core and this is actually the first time I bothered to try, it is immediately obvious that there is no trace of any intermediate tree structure.
|
||
</p>
|
||
|
||
<p class='mb-3'>
|
||
This is when it struck me. I was dazzled and amazed. And am still. Although we are writing our algorithm as if we are working on a real tree structure the library and the compiler are able to just remove the whole intermediate step.
|
||
</p>
|
||
|
||
<hr class='mb-3' />
|
||
|
||
<p class='mb-3'>
|
||
Aftermath:
|
||
</p>
|
||
|
||
<p class='mb-3'>
|
||
In the beginning I promised a way to work on non-functor data structures. Actually that was how I began to work with the <code class='py-0.5 px-0.5 bg-gray-100'>recursion-schemes</code> library.
|
||
</p>
|
||
|
||
<p class='mb-3'>
|
||
We are able to create a ‘normal’ version of our tree from above:
|
||
</p>
|
||
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>data Tree c = Empty | Leaf c | Node (Tree c) (Tree c)
|
||
deriving (Eq, Show)</code></pre></div>
|
||
<p class='mb-3'>
|
||
But we can not use this directly with our (Co-)Algebras. Luckily Edward build a little bit of type magic into the library:
|
||
</p>
|
||
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>type instance Base (Tree c) = (TreeF c)
|
||
|
||
instance Unfoldable (Tree c) where
|
||
embed EmptyF = Empty
|
||
embed (LeafF c) = Leaf c
|
||
embed (NodeF l r) = Node l r
|
||
|
||
instance Foldable (Tree c) where
|
||
project Empty = EmptyF
|
||
project (Leaf c) = LeafF c
|
||
project (Node l r) = NodeF l r</code></pre></div>
|
||
<p class='mb-3'>
|
||
Without going into detail by doing this we establish a relationship between <code class='py-0.5 px-0.5 bg-gray-100'>Tree</code> and <code class='py-0.5 px-0.5 bg-gray-100'>TreeF</code> and teach the compiler how to translate between these types.
|
||
</p>
|
||
|
||
<p class='mb-3'>
|
||
Now we can use our Alebra on our non functor type:
|
||
</p>
|
||
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>example4 = cata flatten (Node (Leaf 'l') (Leaf 'r')) == "lr"</code></pre></div>
|
||
<p class='mb-3'>
|
||
The great thing about this is that, looking at the Core output again, there is no traces of the <code class='py-0.5 px-0.5 bg-gray-100'>TreeF</code> structure to be found. As far as I can tell, the algorithm is working directly on our <code class='py-0.5 px-0.5 bg-gray-100'>Tree</code> type.
|
||
</p>
|
||
|
||
<hr class='mb-3' />
|
||
|
||
<p class='mb-3'>
|
||
Literature:
|
||
</p>
|
||
|
||
<ul class='my-3 ml-6 space-y-1 list-disc'>
|
||
|
||
<li>
|
||
<a href='https://www.fpcomplete.com/user/bartosz/understanding-algebras' class='text-purple-600 hover:underline' target='_blank' rel='noopener'>Understanding F-Algebras</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href='http://www.timphilipwilliams.com/slides.html' class='text-purple-600 hover:underline' target='_blank' rel='noopener'>Recursion Schemes by Example</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href='http://comonad.com/reader/2009/recursion-schemes/' class='text-purple-600 hover:underline' target='_blank' rel='noopener'>Recursion Schemes: A Field Guide</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href='http://stackoverflow.com/questions/6941904/recursion-schemes-for-dummies' class='text-purple-600 hover:underline' target='_blank' rel='noopener'>This StackOverflow question</a>
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
<hr class='mb-3' />
|
||
|
||
<p class='mb-3'>
|
||
Appendix:
|
||
</p>
|
||
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>mergeLists :: Ord a => [a] -> [a] -> [a]
|
||
mergeLists = curry $ unfoldr c where
|
||
c ([], []) = Nothing
|
||
c ([], y:ys) = Just (y, ([], ys))
|
||
c (x:xs, []) = Just (x, (xs, []))
|
||
c (x:xs, y:ys) | x <= y = Just (x, (xs, y:ys))
|
||
| x > y = Just (y, (x:xs, ys))</code></pre></div>
|
||
<!-- 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='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://www.youtube.com/watch?v=9EGYSb9vov8' class='text-gray-600 hover:underline' target='_blank' rel='noopener'>Unifying Structured Recursion Schemes</a> aka. <a href='Haskell/Code%20Snippets/Morphisms' class='text-gray-600 font-bold hover:bg-gray-50' data-wikilink-type='WikiLinkNormal'>The Morphism-Zoo</a></div>
|
||
</div>
|
||
|
||
<div class='pl-2 mt-2 border-l-2 border-purple-200 hover:border-purple-500'>
|
||
<div><a href='https://www.fpcomplete.com/user/bartosz/understanding-algebras' class='text-gray-600 hover:underline' target='_blank' rel='noopener'>Understanding F-Algebras</a> schöne Erklärung. Man könnte danach anfangen den <a href='Haskell/Code%20Snippets/Morphisms' class='text-gray-600 font-bold hover:bg-gray-50' data-wikilink-type='WikiLinkBranch'>Morphismen-zoo</a> zu verstehen…</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 0.7.3.0'>
|
||
<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>
|