quarto/dist/Coding/Haskell/Lenses.html
Nicole Dresselhaus ce0c52a66a initial
2025-05-09 21:47:18 +02:00

1534 lines
110 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 xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head>
<meta charset="utf-8">
<meta name="generator" content="quarto-1.7.23">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<meta name="dcterms.date" content="2018-01-01">
<title>Lenses Nicole Dresselhaus</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
ul.task-list li input[type="checkbox"] {
width: 0.8em;
margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */
vertical-align: middle;
}
/* CSS for syntax highlighting */
html { -webkit-text-size-adjust: 100%; }
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
}
pre.numberSource { margin-left: 3em; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
</style>
<script src="../../site_libs/quarto-nav/quarto-nav.js"></script>
<script src="../../site_libs/quarto-nav/headroom.min.js"></script>
<script src="../../site_libs/clipboard/clipboard.min.js"></script>
<script src="../../site_libs/quarto-search/autocomplete.umd.js"></script>
<script src="../../site_libs/quarto-search/fuse.min.js"></script>
<script src="../../site_libs/quarto-search/quarto-search.js"></script>
<meta name="quarto:offset" content="../../">
<script src="../../site_libs/quarto-html/quarto.js" type="module"></script>
<script src="../../site_libs/quarto-html/tabsets/tabsets.js" type="module"></script>
<script src="../../site_libs/quarto-html/popper.min.js"></script>
<script src="../../site_libs/quarto-html/tippy.umd.min.js"></script>
<script src="../../site_libs/quarto-html/anchor.min.js"></script>
<link href="../../site_libs/quarto-html/tippy.css" rel="stylesheet">
<link href="../../site_libs/quarto-html/quarto-syntax-highlighting-dark-2c84ecb840a13f4c7993f9e5648f0c14.css" rel="stylesheet" class="quarto-color-scheme quarto-color-alternate" id="quarto-text-highlighting-styles">
<link href="../../site_libs/quarto-html/quarto-syntax-highlighting-6cf5824034cebd0380a5b9c74c43f006.css" rel="stylesheet" class="quarto-color-scheme" id="quarto-text-highlighting-styles">
<script src="../../site_libs/bootstrap/bootstrap.min.js"></script>
<link href="../../site_libs/bootstrap/bootstrap-icons.css" rel="stylesheet">
<link href="../../site_libs/bootstrap/bootstrap-ec71cb1e120c0dd41819aca960e74e38.min.css" rel="stylesheet" append-hash="true" class="quarto-color-scheme" id="quarto-bootstrap" data-mode="light">
<link href="../../site_libs/bootstrap/bootstrap-dark-6ed95ce66646ab2447a87e45f81c21f3.min.css" rel="stylesheet" append-hash="true" class="quarto-color-scheme quarto-color-alternate" id="quarto-bootstrap" data-mode="dark">
<link href="../../site_libs/bootstrap/bootstrap-ec71cb1e120c0dd41819aca960e74e38.min.css" rel="stylesheet" append-hash="true" class="quarto-color-scheme-extra" id="quarto-bootstrap" data-mode="light">
<script id="quarto-search-options" type="application/json">{
"location": "navbar",
"copy-button": false,
"collapse-after": 3,
"panel-placement": "end",
"type": "overlay",
"limit": 50,
"keyboard-shortcut": [
"f",
"/",
"s"
],
"show-item-context": false,
"language": {
"search-no-results-text": "No results",
"search-matching-documents-text": "matching documents",
"search-copy-link-title": "Copy link to search",
"search-hide-matches-text": "Hide additional matches",
"search-more-match-text": "more match in this document",
"search-more-matches-text": "more matches in this document",
"search-clear-button-title": "Clear",
"search-text-placeholder": "",
"search-detached-cancel-button-title": "Cancel",
"search-submit-button-title": "Submit",
"search-label": "Search"
}
}</script>
<meta property="og:title" content="Lenses Nicole Dresselhaus">
<meta property="og:description" content="Ramblings of a madwoman">
<meta property="og:site_name" content="Nicole Dresselhaus">
</head>
<body class="nav-sidebar docked nav-fixed quarto-light"><script id="quarto-html-before-body" type="application/javascript">
const toggleBodyColorMode = (bsSheetEl) => {
const mode = bsSheetEl.getAttribute("data-mode");
const bodyEl = window.document.querySelector("body");
if (mode === "dark") {
bodyEl.classList.add("quarto-dark");
bodyEl.classList.remove("quarto-light");
} else {
bodyEl.classList.add("quarto-light");
bodyEl.classList.remove("quarto-dark");
}
}
const toggleBodyColorPrimary = () => {
const bsSheetEl = window.document.querySelector("link#quarto-bootstrap:not([rel=disabled-stylesheet])");
if (bsSheetEl) {
toggleBodyColorMode(bsSheetEl);
}
}
window.setColorSchemeToggle = (alternate) => {
const toggles = window.document.querySelectorAll('.quarto-color-scheme-toggle');
for (let i=0; i < toggles.length; i++) {
const toggle = toggles[i];
if (toggle) {
if (alternate) {
toggle.classList.add("alternate");
} else {
toggle.classList.remove("alternate");
}
}
}
};
const toggleColorMode = (alternate) => {
// Switch the stylesheets
const primaryStylesheets = window.document.querySelectorAll('link.quarto-color-scheme:not(.quarto-color-alternate)');
const alternateStylesheets = window.document.querySelectorAll('link.quarto-color-scheme.quarto-color-alternate');
manageTransitions('#quarto-margin-sidebar .nav-link', false);
if (alternate) {
// note: dark is layered on light, we don't disable primary!
enableStylesheet(alternateStylesheets);
for (const sheetNode of alternateStylesheets) {
if (sheetNode.id === "quarto-bootstrap") {
toggleBodyColorMode(sheetNode);
}
}
} else {
disableStylesheet(alternateStylesheets);
enableStylesheet(primaryStylesheets)
toggleBodyColorPrimary();
}
manageTransitions('#quarto-margin-sidebar .nav-link', true);
// Switch the toggles
window.setColorSchemeToggle(alternate)
// Hack to workaround the fact that safari doesn't
// properly recolor the scrollbar when toggling (#1455)
if (navigator.userAgent.indexOf('Safari') > 0 && navigator.userAgent.indexOf('Chrome') == -1) {
manageTransitions("body", false);
window.scrollTo(0, 1);
setTimeout(() => {
window.scrollTo(0, 0);
manageTransitions("body", true);
}, 40);
}
}
const disableStylesheet = (stylesheets) => {
for (let i=0; i < stylesheets.length; i++) {
const stylesheet = stylesheets[i];
stylesheet.rel = 'disabled-stylesheet';
}
}
const enableStylesheet = (stylesheets) => {
for (let i=0; i < stylesheets.length; i++) {
const stylesheet = stylesheets[i];
if(stylesheet.rel !== 'stylesheet') { // for Chrome, which will still FOUC without this check
stylesheet.rel = 'stylesheet';
}
}
}
const manageTransitions = (selector, allowTransitions) => {
const els = window.document.querySelectorAll(selector);
for (let i=0; i < els.length; i++) {
const el = els[i];
if (allowTransitions) {
el.classList.remove('notransition');
} else {
el.classList.add('notransition');
}
}
}
const isFileUrl = () => {
return window.location.protocol === 'file:';
}
window.hasAlternateSentinel = () => {
let styleSentinel = getColorSchemeSentinel();
if (styleSentinel !== null) {
return styleSentinel === "alternate";
} else {
return false;
}
}
const setStyleSentinel = (alternate) => {
const value = alternate ? "alternate" : "default";
if (!isFileUrl()) {
window.localStorage.setItem("quarto-color-scheme", value);
} else {
localAlternateSentinel = value;
}
}
const getColorSchemeSentinel = () => {
if (!isFileUrl()) {
const storageValue = window.localStorage.getItem("quarto-color-scheme");
return storageValue != null ? storageValue : localAlternateSentinel;
} else {
return localAlternateSentinel;
}
}
const toggleGiscusIfUsed = (isAlternate, darkModeDefault) => {
const baseTheme = document.querySelector('#giscus-base-theme')?.value ?? 'light';
const alternateTheme = document.querySelector('#giscus-alt-theme')?.value ?? 'dark';
let newTheme = '';
if(darkModeDefault) {
newTheme = isAlternate ? baseTheme : alternateTheme;
} else {
newTheme = isAlternate ? alternateTheme : baseTheme;
}
const changeGiscusTheme = () => {
// From: https://github.com/giscus/giscus/issues/336
const sendMessage = (message) => {
const iframe = document.querySelector('iframe.giscus-frame');
if (!iframe) return;
iframe.contentWindow.postMessage({ giscus: message }, 'https://giscus.app');
}
sendMessage({
setConfig: {
theme: newTheme
}
});
}
const isGiscussLoaded = window.document.querySelector('iframe.giscus-frame') !== null;
if (isGiscussLoaded) {
changeGiscusTheme();
}
};
const queryPrefersDark = window.matchMedia('(prefers-color-scheme: dark)');
const darkModeDefault = queryPrefersDark.matches;
document.querySelector('link.quarto-color-scheme-extra').rel = 'disabled-stylesheet';
let localAlternateSentinel = darkModeDefault ? 'alternate' : 'default';
// Dark / light mode switch
window.quartoToggleColorScheme = () => {
// Read the current dark / light value
let toAlternate = !window.hasAlternateSentinel();
toggleColorMode(toAlternate);
setStyleSentinel(toAlternate);
toggleGiscusIfUsed(toAlternate, darkModeDefault);
};
queryPrefersDark.addEventListener("change", e => {
if(window.localStorage.getItem("quarto-color-scheme") !== null)
return;
const alternate = e.matches
toggleColorMode(alternate);
localAlternateSentinel = e.matches ? 'alternate' : 'default'; // this is used alongside local storage!
toggleGiscusIfUsed(alternate, darkModeDefault);
});
// Switch to dark mode if need be
if (window.hasAlternateSentinel()) {
toggleColorMode(true);
} else {
toggleColorMode(false);
}
</script>
<div id="quarto-search-results"></div>
<header id="quarto-header" class="headroom fixed-top">
<nav class="navbar navbar-expand-lg " data-bs-theme="dark">
<div class="navbar-container container-fluid">
<div class="navbar-brand-container mx-auto">
<a class="navbar-brand" href="../../index.html">
<span class="navbar-title">Nicole Dresselhaus</span>
</a>
</div>
<div id="quarto-search" class="" title="Search"></div>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarCollapse" aria-controls="navbarCollapse" role="menu" aria-expanded="false" aria-label="Toggle navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarCollapse">
<ul class="navbar-nav navbar-nav-scroll me-auto">
<li class="nav-item">
<a class="nav-link" href="../../index.html"> <i class="bi bi-house" role="img">
</i>
<span class="menu-text">Home</span></a>
</li>
<li class="nav-item">
<a class="nav-link active" href="../../About/index.html" aria-current="page"> <i class="bi bi-file-person" role="img">
</i>
<span class="menu-text">About</span></a>
</li>
</ul>
<ul class="navbar-nav navbar-nav-scroll ms-auto">
<li class="nav-item compact">
<a class="nav-link" href="../../index.xml"> <i class="bi bi-rss" role="img">
</i>
<span class="menu-text"></span></a>
</li>
</ul>
</div> <!-- /navcollapse -->
<div class="quarto-navbar-tools">
<a href="" class="quarto-color-scheme-toggle quarto-navigation-tool px-1" onclick="window.quartoToggleColorScheme(); return false;" title="Toggle dark mode"><i class="bi"></i></a>
<a href="" class="quarto-reader-toggle quarto-navigation-tool px-1" onclick="window.quartoToggleReader(); return false;" title="Toggle reader mode">
<div class="quarto-reader-toggle-btn">
<i class="bi"></i>
</div>
</a>
</div>
</div> <!-- /container-fluid -->
</nav>
<nav class="quarto-secondary-nav">
<div class="container-fluid d-flex">
<button type="button" class="quarto-btn-toggle btn" data-bs-toggle="collapse" role="button" data-bs-target=".quarto-sidebar-collapse-item" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
<i class="bi bi-layout-text-sidebar-reverse"></i>
</button>
<nav class="quarto-page-breadcrumbs" aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item">Serious</li><li class="breadcrumb-item">Coding</li><li class="breadcrumb-item"><a href="../../Coding/Haskell/Advantages.html">Haskell</a></li><li class="breadcrumb-item"><a href="../../Coding/Haskell/Lenses.html">Lenses</a></li></ol></nav>
<a class="flex-grow-1" role="navigation" data-bs-toggle="collapse" data-bs-target=".quarto-sidebar-collapse-item" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
</a>
</div>
</nav>
</header>
<!-- content -->
<div id="quarto-content" class="quarto-container page-columns page-rows-contents page-layout-article page-navbar">
<!-- sidebar -->
<nav id="quarto-sidebar" class="sidebar collapse collapse-horizontal quarto-sidebar-collapse-item sidebar-navigation docked overflow-auto">
<div class="sidebar-menu-container">
<ul class="list-unstyled mt-1">
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-1" role="navigation" aria-expanded="true">
<span class="menu-text">Serious</span></a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-1" role="navigation" aria-expanded="true" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-1" class="collapse list-unstyled sidebar-section depth1 show">
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-2" role="navigation" aria-expanded="false">
<span class="menu-text">Writing</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-2" role="navigation" aria-expanded="false" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-2" class="collapse list-unstyled sidebar-section depth2 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../Writing/documentation.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../Writing/ner4all-case-study.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Case Study: Local LLM-Based NER with n8n and Ollama</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../Writing/Obsidian-RAG.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze</span></a>
</div>
</li>
</ul>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-3" role="navigation" aria-expanded="true">
<span class="menu-text">Coding</span></a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-3" role="navigation" aria-expanded="true" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-3" class="collapse list-unstyled sidebar-section depth2 show">
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-4" role="navigation" aria-expanded="true">
<span class="menu-text">Haskell</span></a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-4" role="navigation" aria-expanded="true" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-4" class="collapse list-unstyled sidebar-section depth3 show">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../Coding/Haskell/Advantages.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Talks und Posts zu Haskell</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../Coding/Haskell/FFPiH.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Fortgeschrittene funktionale Programmierung in Haskell</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../Coding/Haskell/Lenses.html" class="sidebar-item-text sidebar-link active">
<span class="menu-text">Lenses</span></a>
</div>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-5" role="navigation" aria-expanded="false">
<span class="menu-text">Code Snippets</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-5" role="navigation" aria-expanded="false" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-5" class="collapse list-unstyled sidebar-section depth4 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../Coding/Haskell/Code Snippets/Monoid.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Monoid? Da war doch was…</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../Coding/Haskell/Code Snippets/Morphisms.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">*-Morpisms</span></a>
</div>
</li>
</ul>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a href="../../Coding/Haskell/Webapp-Example/index.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Webapp-Development in Haskell</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-6" role="navigation" aria-expanded="false" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-6" class="collapse list-unstyled sidebar-section depth4 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../Coding/Haskell/Webapp-Example/Main.hs.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Webapp-Example: Main.hs</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../Coding/Haskell/Webapp-Example/MyService_Types.hs.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Webapp-Example: MyService/Types.hs</span></a>
</div>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-7" role="navigation" aria-expanded="false">
<span class="menu-text">Health</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-7" role="navigation" aria-expanded="false" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-7" class="collapse list-unstyled sidebar-section depth2 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../Health/Issues.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Mental Health</span></a>
</div>
</li>
</ul>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-8" role="navigation" aria-expanded="false">
<span class="menu-text">Uni</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-8" role="navigation" aria-expanded="false" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-8" class="collapse list-unstyled sidebar-section depth2 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../Uni/Lernerfolg_an_der_Uni.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Wie lerne ich richtig an der Uni?</span></a>
</div>
</li>
</ul>
</li>
</ul>
</li>
<li class="px-0"><hr class="sidebar-divider hi "></li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-9" role="navigation" aria-expanded="true">
<span class="menu-text">Fun</span></a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-9" role="navigation" aria-expanded="true" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-9" class="collapse list-unstyled sidebar-section depth1 show">
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-10" role="navigation" aria-expanded="false">
<span class="menu-text">Opinions</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-10" role="navigation" aria-expanded="false" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-10" class="collapse list-unstyled sidebar-section depth2 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../Opinions/Editors.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Editors</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../Opinions/Keyboard-Layout.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Keyboard-Layout</span></a>
</div>
</li>
</ul>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-11" role="navigation" aria-expanded="false">
<span class="menu-text">Stuff</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-11" role="navigation" aria-expanded="false" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-11" class="collapse list-unstyled sidebar-section depth2 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../Stuff/Bielefeldverschwoerung.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Die Bielefeld-Verschwörung</span></a>
</div>
</li>
</ul>
</li>
</ul>
</li>
<li class="px-0"><hr class="sidebar-divider hi "></li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-12" role="navigation" aria-expanded="true">
<span class="menu-text">Info</span></a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-12" role="navigation" aria-expanded="true" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-12" class="collapse list-unstyled sidebar-section depth1 show">
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a href="../../About/index.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">About me</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-13" role="navigation" aria-expanded="false" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-13" class="collapse list-unstyled sidebar-section depth2 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../About/Experience.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Highlights of my experiences in the programming world</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../About/Extracurricular.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Studium generale / University-Life</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../About/Work.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Work-Experience</span></a>
</div>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</nav>
<div id="quarto-sidebar-glass" class="quarto-sidebar-collapse-item" data-bs-toggle="collapse" data-bs-target=".quarto-sidebar-collapse-item"></div>
<!-- margin-sidebar -->
<div id="quarto-margin-sidebar" class="sidebar margin-sidebar">
<nav id="TOC" role="doc-toc" class="toc-active">
<h2 id="toc-title">On this page</h2>
<ul>
<li><a href="#wofür-brauchen-wir-das-überhaupt" id="toc-wofür-brauchen-wir-das-überhaupt" class="nav-link active" data-scroll-target="#wofür-brauchen-wir-das-überhaupt">Wofür brauchen wir das überhaupt?</a>
<ul class="collapse">
<li><a href="#beispiel" id="toc-beispiel" class="nav-link" data-scroll-target="#beispiel">Beispiel</a></li>
<li><a href="#problem" id="toc-problem" class="nav-link" data-scroll-target="#problem">Problem</a></li>
<li><a href="#was-wir-gern-hätten" id="toc-was-wir-gern-hätten" class="nav-link" data-scroll-target="#was-wir-gern-hätten">Was wir gern hätten</a></li>
<li><a href="#wie-uns-das-hilft" id="toc-wie-uns-das-hilft" class="nav-link" data-scroll-target="#wie-uns-das-hilft">Wie uns das hilft</a></li>
</ul></li>
<li><a href="#trivialer-ansatz" id="toc-trivialer-ansatz" class="nav-link" data-scroll-target="#trivialer-ansatz">Trivialer Ansatz</a>
<ul class="collapse">
<li><a href="#gettersetter-also-lens-methoden" id="toc-gettersetter-also-lens-methoden" class="nav-link" data-scroll-target="#gettersetter-also-lens-methoden">Getter/Setter also Lens-Methoden</a></li>
<li><a href="#wieso-ist-das-schlecht" id="toc-wieso-ist-das-schlecht" class="nav-link" data-scroll-target="#wieso-ist-das-schlecht">Wieso ist das schlecht?</a></li>
<li><a href="#something-in-common" id="toc-something-in-common" class="nav-link" data-scroll-target="#something-in-common">Something in common</a></li>
<li><a href="#typ-einer-lens" id="toc-typ-einer-lens" class="nav-link" data-scroll-target="#typ-einer-lens">Typ einer Lens</a></li>
</ul></li>
<li><a href="#benutzen-einer-lens-also-setter" id="toc-benutzen-einer-lens-also-setter" class="nav-link" data-scroll-target="#benutzen-einer-lens-also-setter">Benutzen einer Lens also Setter</a></li>
<li><a href="#benutzen-einer-lens-also-modify" id="toc-benutzen-einer-lens-also-modify" class="nav-link" data-scroll-target="#benutzen-einer-lens-also-modify">Benutzen einer Lens also Modify</a></li>
<li><a href="#benutzen-einer-lens-also-getter" id="toc-benutzen-einer-lens-also-getter" class="nav-link" data-scroll-target="#benutzen-einer-lens-also-getter">Benutzen einer Lens also Getter</a></li>
<li><a href="#lenses-bauen" id="toc-lenses-bauen" class="nav-link" data-scroll-target="#lenses-bauen">Lenses bauen</a></li>
<li><a href="#wie-funktioniert-das-intern" id="toc-wie-funktioniert-das-intern" class="nav-link" data-scroll-target="#wie-funktioniert-das-intern">Wie funktioniert das intern?</a></li>
<li><a href="#composing-lenses-und-deren-benutzung" id="toc-composing-lenses-und-deren-benutzung" class="nav-link" data-scroll-target="#composing-lenses-und-deren-benutzung">Composing Lenses und deren Benutzung</a></li>
<li><a href="#automatisieren-mit-template-haskell" id="toc-automatisieren-mit-template-haskell" class="nav-link" data-scroll-target="#automatisieren-mit-template-haskell">Automatisieren mit Template-Haskell</a></li>
<li><a href="#lenses-für-den-beispielcode" id="toc-lenses-für-den-beispielcode" class="nav-link" data-scroll-target="#lenses-für-den-beispielcode">Lenses für den Beispielcode</a></li>
<li><a href="#shortcuts-mit-line-noise" id="toc-shortcuts-mit-line-noise" class="nav-link" data-scroll-target="#shortcuts-mit-line-noise">Shortcuts mit “Line-Noise”</a></li>
<li><a href="#virtuelle-felder" id="toc-virtuelle-felder" class="nav-link" data-scroll-target="#virtuelle-felder">Virtuelle Felder</a></li>
<li><a href="#non-record-strukturen" id="toc-non-record-strukturen" class="nav-link" data-scroll-target="#non-record-strukturen">Non-Record Strukturen</a></li>
<li><a href="#weitere-beispiele" id="toc-weitere-beispiele" class="nav-link" data-scroll-target="#weitere-beispiele">Weitere Beispiele</a></li>
<li><a href="#erweiterungen" id="toc-erweiterungen" class="nav-link" data-scroll-target="#erweiterungen">Erweiterungen</a></li>
<li><a href="#wozu-dienen-die-erweiterungen" id="toc-wozu-dienen-die-erweiterungen" class="nav-link" data-scroll-target="#wozu-dienen-die-erweiterungen">Wozu dienen die Erweiterungen?</a></li>
<li><a href="#wie-es-in-lens-wirklich-aussieht" id="toc-wie-es-in-lens-wirklich-aussieht" class="nav-link" data-scroll-target="#wie-es-in-lens-wirklich-aussieht">Wie es in Lens wirklich aussieht</a></li>
</ul>
</nav>
</div>
<!-- main -->
<main class="content" id="quarto-document-content">
<header id="title-block-header" class="quarto-title-block default"><nav class="quarto-page-breadcrumbs quarto-title-breadcrumbs d-none d-lg-block" aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item">Serious</li><li class="breadcrumb-item">Coding</li><li class="breadcrumb-item"><a href="../../Coding/Haskell/Advantages.html">Haskell</a></li><li class="breadcrumb-item"><a href="../../Coding/Haskell/Lenses.html">Lenses</a></li></ol></nav>
<div class="quarto-title">
<h1 class="title">Lenses</h1>
<div class="quarto-categories">
<div class="quarto-category">Article</div>
<div class="quarto-category">Haskell</div>
</div>
</div>
<div class="quarto-title-meta">
<div>
<div class="quarto-title-meta-heading">Published</div>
<div class="quarto-title-meta-contents">
<p class="date">January 1, 2018</p>
</div>
</div>
</div>
</header>
<section id="wofür-brauchen-wir-das-überhaupt" class="level2">
<h2 class="anchored" data-anchor-id="wofür-brauchen-wir-das-überhaupt">Wofür brauchen wir das überhaupt?</h2>
<p>Die Idee dahinter ist, dass man Zugriffsabstraktionen über Daten verknüpfen<br>
kann. Also einfachen Datenstruktur kann man einen Record mit der entsprechenden<br>
Syntax nehmen.</p>
<section id="beispiel" class="level3">
<h3 class="anchored" data-anchor-id="beispiel">Beispiel</h3>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Person</span> <span class="ot">=</span> <span class="dt">P</span> {<span class="ot"> name ::</span> <span class="dt">String</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> addr ::</span> <span class="dt">Address</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> salary ::</span> <span class="dt">Int</span> }</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Address</span> <span class="ot">=</span> <span class="dt">A</span> {<span class="ot"> road ::</span> <span class="dt">String</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> city ::</span> <span class="dt">String</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> postcode ::</span> <span class="dt">String</span> }</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="co">-- autogeneriert unten anderem: addr :: Person -&gt; Address</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="ot"> setName ::</span> <span class="dt">String</span> <span class="ot">-&gt;</span> <span class="dt">Person</span> <span class="ot">-&gt;</span> <span class="dt">Person</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a> setName n p <span class="ot">=</span> p { name <span class="ot">=</span> n } <span class="co">--record update notation</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="ot"> setPostcode ::</span> <span class="dt">String</span> <span class="ot">-&gt;</span> <span class="dt">Person</span> <span class="ot">-&gt;</span> <span class="dt">Person</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a> setPostcode pc p</span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> p { addr <span class="ot">=</span> addr p { postcode <span class="ot">=</span> pc } }</span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a> <span class="co">-- update of a record inside a record</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</section>
<section id="problem" class="level3">
<h3 class="anchored" data-anchor-id="problem">Problem</h3>
<p>Problem mit diesem Code:</p>
<ul>
<li>für 1-Dimensionale Felder ist die record-syntax ok.</li>
<li>tiefere Ebenen nur umständlich zu erreichen</li>
<li>eigentlich wollen wir nur pe in p setzen, müssen aber über addr etc. gehen.</li>
<li>wir brauchen wissen über die “Zwischenstrukturen”, an denen wir nicht<br>
interessiert sind</li>
</ul>
</section>
<section id="was-wir-gern-hätten" class="level3">
<h3 class="anchored" data-anchor-id="was-wir-gern-hätten">Was wir gern hätten</h3>
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Person</span> <span class="ot">=</span> <span class="dt">P</span> {<span class="ot"> name ::</span> <span class="dt">String</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> addr ::</span> <span class="dt">Address</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> salary ::</span> <span class="dt">Int</span> }</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="co">-- a lens for each field</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="ot">lname ::</span> <span class="dt">Lens'</span> <span class="dt">Person</span> <span class="dt">String</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="ot">laddr ::</span> <span class="dt">Lens'</span> <span class="dt">Person</span> <span class="dt">Adress</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="ot">lsalary ::</span> <span class="dt">Lens'</span> <span class="dt">Person</span> <span class="dt">Int</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="co">-- getter/setter for them</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="ot">view ::</span> <span class="dt">Lens'</span> s a <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> a</span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="ot">set ::</span> <span class="dt">Lens'</span> s a <span class="ot">-&gt;</span> a <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> s</span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="co">-- lens-composition</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a><span class="ot">composeL ::</span> <span class="dt">Lens'</span> s1 s2 <span class="ot">-&gt;</span> <span class="dt">Lens</span> s2 a <span class="ot">-&gt;</span> <span class="dt">Lens'</span> s1 a</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</section>
<section id="wie-uns-das-hilft" class="level3">
<h3 class="anchored" data-anchor-id="wie-uns-das-hilft">Wie uns das hilft</h3>
<p>Mit diesen Dingen (wenn wir sie hätten) könnte man dann</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Person</span> <span class="ot">=</span> <span class="dt">P</span> {<span class="ot"> name ::</span> <span class="dt">String</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> addr ::</span> <span class="dt">Address</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> salary ::</span> <span class="dt">Int</span> }</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Address</span> <span class="ot">=</span> <span class="dt">A</span> {<span class="ot"> road ::</span> <span class="dt">String</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> city ::</span> <span class="dt">String</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> postcode ::</span> <span class="dt">String</span> }</span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="ot">setPostcode ::</span> <span class="dt">String</span> <span class="ot">-&gt;</span> <span class="dt">Person</span> <span class="ot">-&gt;</span> <span class="dt">Person</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>setPostcode pc p</span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> set (laddr <span class="ot">`composeL`</span> lpostcode) pc p</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>machen und wäre fertig.</p>
</section>
</section>
<section id="trivialer-ansatz" class="level2">
<h2 class="anchored" data-anchor-id="trivialer-ansatz">Trivialer Ansatz</h2>
<section id="gettersetter-also-lens-methoden" class="level3">
<h3 class="anchored" data-anchor-id="gettersetter-also-lens-methoden">Getter/Setter also Lens-Methoden</h3>
<div class="sourceCode" id="cb4"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">LensR</span> s a <span class="ot">=</span> <span class="dt">L</span> {<span class="ot"> viewR ::</span> s <span class="ot">-&gt;</span> a</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> setR ::</span> a <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> s }</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>composeL (<span class="dt">L</span> v1 u1) (<span class="dt">L</span> v2 u2)</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> <span class="dt">L</span> (\s <span class="ot">-&gt;</span> v2 (v1 s))</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a> (\a s <span class="ot">-&gt;</span> u1 (u2 a (v1 s)) s)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</section>
<section id="wieso-ist-das-schlecht" class="level3">
<h3 class="anchored" data-anchor-id="wieso-ist-das-schlecht">Wieso ist das schlecht?</h3>
<ul>
<li>extrem ineffizient</li>
</ul>
<p>Auslesen traversiert die Datenstruktur, dann wird die Function angewendet und<br>
zum setzen wird die Datenstruktur erneut traversiert:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="ot">over ::</span> <span class="dt">LensR</span> s a <span class="ot">-&gt;</span> (a <span class="ot">-&gt;</span> a) <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> s</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>over ln f s <span class="ot">=</span> setR l (f (viewR l s)) s</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<ul>
<li>Lösung: modify-funktion hinzufügen</li>
</ul>
<div class="sourceCode" id="cb6"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">LensR</span> s a</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> <span class="dt">L</span> {<span class="ot"> viewR ::</span> s <span class="ot">-&gt;</span> a</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> setR ::</span> a <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> s</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> mod ::</span> (a<span class="ot">-&gt;</span>a) <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> s</span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> modM ::</span> (a<span class="ot">-&gt;</span><span class="dt">Maybe</span> a) <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> <span class="dt">Maybe</span> s</span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> modIO ::</span> (a<span class="ot">-&gt;</span><span class="dt">IO</span> a) <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> <span class="dt">IO</span> s }</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Neues Problem: Für jeden Spezialfall muss die Lens erweitert werden.</p>
</section>
<section id="something-in-common" class="level3">
<h3 class="anchored" data-anchor-id="something-in-common">Something in common</h3>
<p>Man kann alle Monaden abstrahieren. Functor reicht schon:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">LensR</span> s a</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> <span class="dt">L</span> {<span class="ot"> viewR ::</span> s <span class="ot">-&gt;</span> a</span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> setR ::</span> a <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> s</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> mod ::</span> (a<span class="ot">-&gt;</span>a) <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> s</span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> modF ::</span> <span class="dt">Functor</span> f <span class="ot">=&gt;</span> (a<span class="ot">-&gt;</span>f a) <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> f s }</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Idee: Die 3 darüberliegenden durch modF ausdrücken.</p>
</section>
<section id="typ-einer-lens" class="level3">
<h3 class="anchored" data-anchor-id="typ-einer-lens">Typ einer Lens</h3>
<p>Wenn man das berücksichtigt, dann hat einen Lens folgenden Typ:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Lens'</span> s a <span class="ot">=</span> <span class="kw">forall</span> f<span class="op">.</span> <span class="dt">Functor</span> f</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a> <span class="ot">=&gt;</span> (a <span class="ot">-&gt;</span> f a) <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> f s</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Allerdings haben wir dann noch unseren getter/setter:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">LensR</span> s a <span class="ot">=</span> <span class="dt">L</span> {<span class="ot"> viewR ::</span> s <span class="ot">-&gt;</span> a</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> setR ::</span> a <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> s }</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Stellt sich raus: Die sind isomorph! Auch wenn die von den Typen her komplett<br>
anders aussehen.</p>
</section>
</section>
<section id="benutzen-einer-lens-also-setter" class="level2">
<h2 class="anchored" data-anchor-id="benutzen-einer-lens-also-setter">Benutzen einer Lens also Setter</h2>
<div class="sourceCode" id="cb10"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="ot">set ::</span> <span class="dt">Lens'</span> s a <span class="ot">-&gt;</span> (a <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> s)</span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>set ln a s <span class="ot">=</span> <span class="op">--...</span>umm<span class="op">...</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a><span class="op">--:</span>t ln <span class="ot">=&gt;</span> (a <span class="ot">-&gt;</span> f a) <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> f s</span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a><span class="co">-- =&gt; get s out of f s to return it</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Wir können für f einfach die “Identity”-Monade nehmen, die wir nachher wegcasten<br>
können.</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="kw">newtype</span> <span class="dt">Identity</span> a <span class="ot">=</span> <span class="dt">Identity</span> a</span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="co">-- Id :: a -&gt; Identity a</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a><span class="ot">runIdentity ::</span> <span class="dt">Identity</span> s <span class="ot">-&gt;</span> s</span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a>runIdentity (<span class="dt">Identity</span> x) <span class="ot">=</span> x</span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Functor</span> <span class="dt">Identity</span> <span class="kw">where</span></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a> <span class="fu">fmap</span> f (<span class="dt">Identity</span> x) <span class="ot">=</span> <span class="dt">Identity</span> (f x)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>somit ist set einfach nur</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="ot">set ::</span> <span class="dt">Lens'</span> s a <span class="ot">-&gt;</span> (a <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> s)</span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>set ln x s</span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> runIdentity (ls set_fld s)</span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a><span class="ot"> set_fld ::</span> a <span class="ot">-&gt;</span> <span class="dt">Identity</span> a</span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a> set_fld _ <span class="ot">=</span> <span class="dt">Identity</span> x</span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a> <span class="co">-- a was the OLD value.</span></span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a> <span class="co">-- We throw that away and set the new value</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>oder kürzer (für nerds wie den Author der Lens-Lib)</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="ot">set ::</span> <span class="dt">Lens'</span> s a <span class="ot">-&gt;</span> (a <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> s)</span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a>set ln x <span class="ot">=</span> runIdentity <span class="op">.</span> ln (<span class="dt">Identity</span> <span class="op">.</span> <span class="fu">const</span> x)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</section>
<section id="benutzen-einer-lens-also-modify" class="level2">
<h2 class="anchored" data-anchor-id="benutzen-einer-lens-also-modify">Benutzen einer Lens also Modify</h2>
<p>Dasselbe wie Set, nur dass wir den Parameter nicht entsorgen, sondern in die<br>
mitgelieferte Function stopfen.</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="ot">over ::</span> <span class="dt">Lens'</span> s a <span class="ot">-&gt;</span> (a <span class="ot">-&gt;</span> a) <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> s</span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a>over ln f <span class="ot">=</span> runIdentity <span class="op">.</span> ln (<span class="dt">Identity</span> <span class="op">.</span> f)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</section>
<section id="benutzen-einer-lens-also-getter" class="level2">
<h2 class="anchored" data-anchor-id="benutzen-einer-lens-also-getter">Benutzen einer Lens also Getter</h2>
<div class="sourceCode" id="cb15"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="ot">view ::</span> <span class="dt">Lens'</span> s a <span class="ot">-&gt;</span> (s <span class="ot">-&gt;</span> a)</span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a>view ln s <span class="ot">=</span> <span class="op">--...</span>umm<span class="op">...</span></span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a><span class="op">--:</span>t ln <span class="ot">=&gt;</span> (a <span class="ot">-&gt;</span> f a) <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> f s</span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a><span class="co">-- =&gt; get a out of the (f s) return-value</span></span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a><span class="co">-- Wait, WHAT?</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Auch hier gibt es einen netten Funktor. Wir packen das “a” einfach in das “f”<br>
und werfen das “s” am End weg.</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="kw">newtype</span> <span class="dt">Const</span> v a <span class="ot">=</span> <span class="dt">Const</span> v</span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a><span class="ot">getConst ::</span> <span class="dt">Const</span> v a <span class="ot">-&gt;</span> v</span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a>getConst (<span class="dt">Const</span> x) <span class="ot">=</span> x</span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Functor</span> (<span class="dt">Const</span> v) <span class="kw">where</span></span>
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a> <span class="fu">fmap</span> f (<span class="dt">Const</span> x) <span class="ot">=</span> <span class="dt">Const</span> x</span>
<span id="cb16-8"><a href="#cb16-8" aria-hidden="true" tabindex="-1"></a> <span class="co">-- throw f away. Nothing changes our const!</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>somit ergibt sich</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="ot">view ::</span> <span class="dt">Lens'</span> s a <span class="ot">-&gt;</span> (s <span class="ot">-&gt;</span> a)</span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a>view ln s</span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> getConst (ln <span class="dt">Const</span> s)</span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a> <span class="co">-- Const :: s -&gt; Const a s</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>oder nerdig</p>
<div class="sourceCode" id="cb18"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="ot">view ::</span> <span class="dt">Lens'</span> s a <span class="ot">-&gt;</span> (s <span class="ot">-&gt;</span> a)</span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a>view ln <span class="ot">=</span> getConst <span class="op">.</span> ln <span class="dt">Const</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</section>
<section id="lenses-bauen" class="level2">
<h2 class="anchored" data-anchor-id="lenses-bauen">Lenses bauen</h2>
<p>Nochmal kurz der Typ:</p>
<div class="sourceCode" id="cb19"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Lens'</span> s a <span class="ot">=</span> <span class="kw">forall</span> f<span class="op">.</span> <span class="dt">Functor</span> f</span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a> <span class="ot">=&gt;</span> (a <span class="ot">-&gt;</span> f a) <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> f s</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Für unser Personen-Beispiel vom Anfang:</p>
<div class="sourceCode" id="cb20"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Person</span> <span class="ot">=</span> <span class="dt">P</span> {<span class="ot"> _name ::</span> <span class="dt">String</span>,<span class="ot"> _salary ::</span> <span class="dt">Int</span> }</span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a><span class="ot">name ::</span> <span class="dt">Lens'</span> <span class="dt">Person</span> <span class="dt">String</span></span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a><span class="co">-- name :: Functor f =&gt; (String -&gt; f String)</span></span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a><span class="co">-- -&gt; Person -&gt; f Person</span></span>
<span id="cb20-6"><a href="#cb20-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb20-7"><a href="#cb20-7" aria-hidden="true" tabindex="-1"></a>name elt_fn (<span class="dt">P</span> n s)</span>
<span id="cb20-8"><a href="#cb20-8" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> <span class="fu">fmap</span> (\n' <span class="ot">-&gt;</span> <span class="dt">P</span> n' s) (elt_fn n)</span>
<span id="cb20-9"><a href="#cb20-9" aria-hidden="true" tabindex="-1"></a><span class="co">-- fmap :: Functor f =&gt; (a-&gt;b) -&gt; f a -&gt; f b - der Funktor, der alles verknüpft</span></span>
<span id="cb20-10"><a href="#cb20-10" aria-hidden="true" tabindex="-1"></a><span class="co">-- \n' -&gt; .. :: String -&gt; Person - Funktion um das Element zu lokalisieren (WO wird ersetzt/gelesen/...)</span></span>
<span id="cb20-11"><a href="#cb20-11" aria-hidden="true" tabindex="-1"></a><span class="co">-- elt_fn n :: f String - Funktion um das Element zu verändern (setzen, ändern, ...)</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Die Lambda-Funktion ersetzt einfach den Namen. Häufig sieht man auch</p>
<div class="sourceCode" id="cb21"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a>name elt_fn (<span class="dt">P</span> n s)</span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> (\n' <span class="ot">-&gt;</span> <span class="dt">P</span> n' s) <span class="op">&lt;$&gt;</span> (elt_fn n)</span>
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a><span class="co">-- | Focus | |Function|</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</section>
<section id="wie-funktioniert-das-intern" class="level2">
<h2 class="anchored" data-anchor-id="wie-funktioniert-das-intern">Wie funktioniert das intern?</h2>
<div class="sourceCode" id="cb22"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a>view name (<span class="dt">P</span> {_name<span class="ot">=</span><span class="st">"Fred"</span>, _salary<span class="ot">=</span><span class="dv">100</span>})</span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a> <span class="co">-- inline view-function</span></span>
<span id="cb22-3"><a href="#cb22-3" aria-hidden="true" tabindex="-1"></a><span class="ot">=</span> getConst (name <span class="dt">Const</span> (<span class="dt">P</span> {_name<span class="ot">=</span><span class="st">"Fred"</span>, _salary<span class="ot">=</span><span class="dv">100</span>})</span>
<span id="cb22-4"><a href="#cb22-4" aria-hidden="true" tabindex="-1"></a> <span class="co">-- inline name</span></span>
<span id="cb22-5"><a href="#cb22-5" aria-hidden="true" tabindex="-1"></a><span class="ot">=</span> getConst (<span class="fu">fmap</span> (\n' <span class="ot">-&gt;</span> <span class="dt">P</span> n' <span class="dv">100</span>) (<span class="dt">Const</span> <span class="st">"Fred"</span>))</span>
<span id="cb22-6"><a href="#cb22-6" aria-hidden="true" tabindex="-1"></a> <span class="co">-- fmap f (Const x) = Const x - Definition von Const</span></span>
<span id="cb22-7"><a href="#cb22-7" aria-hidden="true" tabindex="-1"></a><span class="ot">=</span> getConst (<span class="dt">Const</span> <span class="st">"Fred"</span>)</span>
<span id="cb22-8"><a href="#cb22-8" aria-hidden="true" tabindex="-1"></a> <span class="co">-- getConst (Const x) = x</span></span>
<span id="cb22-9"><a href="#cb22-9" aria-hidden="true" tabindex="-1"></a><span class="ot">=</span> <span class="st">"Fred"</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Dieser Aufruf hat KEINE Runtime-Kosten, weil der Compiler direkt die Address des<br>
Feldes einsetzen kann. Der gesamte Boilerplate-Code wird vom Compiler<br>
wegoptimiert.</p>
<p>Dies gilt für jeden Funktor mit newtype, da das nur ein Typalias ist.</p>
</section>
<section id="composing-lenses-und-deren-benutzung" class="level2">
<h2 class="anchored" data-anchor-id="composing-lenses-und-deren-benutzung">Composing Lenses und deren Benutzung</h2>
<p>Wie sehen denn die Typen aus?</p>
<p>Wir wollen ein</p>
<blockquote class="blockquote">
<p>Lens s1 s2 -&gt; Lens s2 a -&gt; Lens s1 a</p>
</blockquote>
<p>Wir haben 2 Lenses</p>
<blockquote class="blockquote">
<p>ln1 :: (s2 -&gt; f s2) -&gt; (s1 -&gt; f s1)<br>
ln2 :: (a -&gt; f a) -&gt; (s2 -&gt; f s2)</p>
</blockquote>
<p>wenn man scharf hinsieht, kann man die verbinden</p>
<blockquote class="blockquote">
<p>ln1 . ln2 :: (a -&gt; f s) -&gt; (s1 -&gt; f s1)</p>
</blockquote>
<p>und erhält eine Lens. Sogar die Gewünschte!<br>
Somit ist Lens-Composition einfach nur Function-Composition (.).</p>
</section>
<section id="automatisieren-mit-template-haskell" class="level2">
<h2 class="anchored" data-anchor-id="automatisieren-mit-template-haskell">Automatisieren mit Template-Haskell</h2>
<p>Der Code um die Lenses zu bauen ist für records immer Identisch:</p>
<div class="sourceCode" id="cb23"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Person</span> <span class="ot">=</span> <span class="dt">P</span> {<span class="ot"> _name ::</span> <span class="dt">String</span>,<span class="ot"> _salary ::</span> <span class="dt">Int</span> }</span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a><span class="ot">name ::</span> <span class="dt">Lens'</span> <span class="dt">Person</span> <span class="dt">String</span></span>
<span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a>name elt_fn (<span class="dt">P</span> n s) <span class="ot">=</span> (\n' <span class="ot">-&gt;</span> <span class="dt">P</span> n' s) <span class="op">&lt;$&gt;</span> (elt_fn n)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Daher kann man einfach</p>
<div class="sourceCode" id="cb24"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Control.Lens.TH</span></span>
<span id="cb24-2"><a href="#cb24-2" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Person</span> <span class="ot">=</span> <span class="dt">P</span> {<span class="ot"> _name ::</span> <span class="dt">String</span>,<span class="ot"> _salary ::</span> <span class="dt">Int</span> }</span>
<span id="cb24-3"><a href="#cb24-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb24-4"><a href="#cb24-4" aria-hidden="true" tabindex="-1"></a><span class="op">$</span>(makeLenses '<span class="dt">'Person</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>nehmen, was einem eine Lens für “name” und eine Lens für “salary” generiert.<br>
Mit anderen Templates kann man auch weitere Dinge steuern (etwa wofür Lenses<br>
generiert werden, welches Prefix (statt _) man haben will etc. pp.).</p>
<p>Will man das aber haben, muss man selbst in den Control.Lens.TH-Code schauen.</p>
</section>
<section id="lenses-für-den-beispielcode" class="level2">
<h2 class="anchored" data-anchor-id="lenses-für-den-beispielcode">Lenses für den Beispielcode</h2>
<div class="sourceCode" id="cb25"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Control.Lens.TH</span></span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Person</span> <span class="ot">=</span> <span class="dt">P</span> {<span class="ot"> _name ::</span> <span class="dt">String</span></span>
<span id="cb25-4"><a href="#cb25-4" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> _addr ::</span> <span class="dt">Address</span></span>
<span id="cb25-5"><a href="#cb25-5" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> _salary ::</span> <span class="dt">Int</span> }</span>
<span id="cb25-6"><a href="#cb25-6" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Address</span> <span class="ot">=</span> <span class="dt">A</span> {<span class="ot"> _road ::</span> <span class="dt">String</span></span>
<span id="cb25-7"><a href="#cb25-7" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> _city ::</span> <span class="dt">String</span></span>
<span id="cb25-8"><a href="#cb25-8" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> _postcode ::</span> <span class="dt">String</span> }</span>
<span id="cb25-9"><a href="#cb25-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-10"><a href="#cb25-10" aria-hidden="true" tabindex="-1"></a><span class="op">$</span>(makeLenses '<span class="dt">'Person</span>)</span>
<span id="cb25-11"><a href="#cb25-11" aria-hidden="true" tabindex="-1"></a><span class="op">$</span>(makeLenses '<span class="dt">'Address</span>)</span>
<span id="cb25-12"><a href="#cb25-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-13"><a href="#cb25-13" aria-hidden="true" tabindex="-1"></a><span class="ot">setPostcode ::</span> <span class="dt">String</span> <span class="ot">-&gt;</span> <span class="dt">Person</span> <span class="ot">-&gt;</span> <span class="dt">Person</span></span>
<span id="cb25-14"><a href="#cb25-14" aria-hidden="true" tabindex="-1"></a>setPostcode pc p <span class="ot">=</span> set (addr <span class="op">.</span> postcode) pc p</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</section>
<section id="shortcuts-mit-line-noise" class="level2">
<h2 class="anchored" data-anchor-id="shortcuts-mit-line-noise">Shortcuts mit “Line-Noise”</h2>
<div class="sourceCode" id="cb26"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- ...</span></span>
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a><span class="ot">setPostcode ::</span> <span class="dt">String</span> <span class="ot">-&gt;</span> <span class="dt">Person</span> <span class="ot">-&gt;</span> <span class="dt">Person</span></span>
<span id="cb26-4"><a href="#cb26-4" aria-hidden="true" tabindex="-1"></a>setPostcode pc p <span class="ot">=</span> addr <span class="op">.</span> postcode <span class="op">.~</span> pc <span class="op">$</span> p</span>
<span id="cb26-5"><a href="#cb26-5" aria-hidden="true" tabindex="-1"></a><span class="co">-- | Focus |set|to what|in where</span></span>
<span id="cb26-6"><a href="#cb26-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb26-7"><a href="#cb26-7" aria-hidden="true" tabindex="-1"></a><span class="ot">getPostcode ::</span> <span class="dt">Person</span> <span class="ot">-&gt;</span> <span class="dt">String</span></span>
<span id="cb26-8"><a href="#cb26-8" aria-hidden="true" tabindex="-1"></a>getPostcode p <span class="ot">=</span> p <span class="op">^.</span> <span class="op">$</span> addr <span class="op">.</span> postcode</span>
<span id="cb26-9"><a href="#cb26-9" aria-hidden="true" tabindex="-1"></a><span class="co">-- |from|get| Focus |</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Es gibt drölf-zillionen weitere Infix-Operatoren (für Folds,<br>
Listenkonvertierungen, -traversierungen, …)</p>
</section>
<section id="virtuelle-felder" class="level2">
<h2 class="anchored" data-anchor-id="virtuelle-felder">Virtuelle Felder</h2>
<p>Man kann mit Lenses sogar Felder emulieren, die gar nicht da sind. Angenommen<br>
folgender Code:</p>
<div class="sourceCode" id="cb27"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Temp</span> <span class="ot">=</span> <span class="dt">T</span> {<span class="ot"> _fahrenheit ::</span> <span class="dt">Float</span> }</span>
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb27-3"><a href="#cb27-3" aria-hidden="true" tabindex="-1"></a><span class="op">$</span>(makeLenses '<span class="dt">'Temp</span>)</span>
<span id="cb27-4"><a href="#cb27-4" aria-hidden="true" tabindex="-1"></a><span class="co">-- liefert Lens: fahrenheit :: Lens Temp Float</span></span>
<span id="cb27-5"><a href="#cb27-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb27-6"><a href="#cb27-6" aria-hidden="true" tabindex="-1"></a><span class="ot">centigrade ::</span> <span class="dt">Lens</span> <span class="dt">Temp</span> <span class="dt">Float</span></span>
<span id="cb27-7"><a href="#cb27-7" aria-hidden="true" tabindex="-1"></a>centigrade centi_fn (<span class="dt">T</span> faren)</span>
<span id="cb27-8"><a href="#cb27-8" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> (\centi' <span class="ot">-&gt;</span> <span class="dt">T</span> (cToF centi'))</span>
<span id="cb27-9"><a href="#cb27-9" aria-hidden="true" tabindex="-1"></a> <span class="op">&lt;$&gt;</span> (centi_fn (fToC faren))</span>
<span id="cb27-10"><a href="#cb27-10" aria-hidden="true" tabindex="-1"></a><span class="co">-- cToF &amp; fToC as Converter-Functions defined someplace else</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Hiermit kann man dann auch Funktionen, die auf Grad-Celsius rechnen auf Daten<br>
anwenden, die eigenlich nur Fahrenheit speichern, aber eine Umrechnung<br>
bereitstellen. Analog kann man auch einen Zeit-Datentypen definieren, der<br>
intern mit Sekunden rechnet (und somit garantiert frei von Fehlern wie -3<br>
Minuten oder 37 Stunden ist)</p>
</section>
<section id="non-record-strukturen" class="level2">
<h2 class="anchored" data-anchor-id="non-record-strukturen">Non-Record Strukturen</h2>
<p>Das ganze kann man auch parametrisieren und auf Non-Record-Strukturen anwenden.<br>
Beispielhaft an einer Map verdeutlicht:</p>
<div class="sourceCode" id="cb28"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- from Data.Lens.At</span></span>
<span id="cb28-2"><a href="#cb28-2" aria-hidden="true" tabindex="-1"></a><span class="ot">at ::</span> <span class="dt">Ord</span> k <span class="ot">=&gt;</span> k <span class="ot">-&gt;</span> <span class="dt">Lens'</span> (<span class="dt">Map</span> k v) (<span class="dt">Maybe</span> v)</span>
<span id="cb28-3"><a href="#cb28-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb28-4"><a href="#cb28-4" aria-hidden="true" tabindex="-1"></a><span class="co">-- oder identisch, wenn man die Lens' auflöst:</span></span>
<span id="cb28-5"><a href="#cb28-5" aria-hidden="true" tabindex="-1"></a><span class="ot">at ::</span> <span class="dt">Ord</span> k, <span class="kw">forall</span> f<span class="op">.</span> <span class="dt">Functor</span> f <span class="ot">=&gt;</span> k <span class="ot">-&gt;</span> (<span class="dt">Maybe</span> v <span class="ot">-&gt;</span> f <span class="dt">Maybe</span> v) <span class="ot">-&gt;</span> <span class="dt">Map</span> k v <span class="ot">-&gt;</span> f <span class="dt">Map</span> k v</span>
<span id="cb28-6"><a href="#cb28-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb28-7"><a href="#cb28-7" aria-hidden="true" tabindex="-1"></a>at k mb_fn m</span>
<span id="cb28-8"><a href="#cb28-8" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> wrap <span class="op">&lt;$&gt;</span> (mb_fn mv)</span>
<span id="cb28-9"><a href="#cb28-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb28-10"><a href="#cb28-10" aria-hidden="true" tabindex="-1"></a> mv <span class="ot">=</span> Map.lookup k m</span>
<span id="cb28-11"><a href="#cb28-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb28-12"><a href="#cb28-12" aria-hidden="true" tabindex="-1"></a><span class="ot"> wrap ::</span> <span class="dt">Maybe</span> v <span class="ot">-&gt;</span> <span class="dt">Map</span> k v</span>
<span id="cb28-13"><a href="#cb28-13" aria-hidden="true" tabindex="-1"></a> wrap (<span class="dt">Just</span> v') <span class="ot">=</span> Map.insert k v' m</span>
<span id="cb28-14"><a href="#cb28-14" aria-hidden="true" tabindex="-1"></a> wrap <span class="dt">Nothing</span> <span class="ot">=</span> <span class="kw">case</span> mv <span class="kw">of</span></span>
<span id="cb28-15"><a href="#cb28-15" aria-hidden="true" tabindex="-1"></a> <span class="dt">Nothing</span> <span class="ot">-&gt;</span> m</span>
<span id="cb28-16"><a href="#cb28-16" aria-hidden="true" tabindex="-1"></a> <span class="dt">Just</span> _ <span class="ot">-&gt;</span> Map.delete k m</span>
<span id="cb28-17"><a href="#cb28-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb28-18"><a href="#cb28-18" aria-hidden="true" tabindex="-1"></a><span class="co">-- mb_fn :: Maybe v -&gt; f Maybe v</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</section>
<section id="weitere-beispiele" class="level2">
<h2 class="anchored" data-anchor-id="weitere-beispiele">Weitere Beispiele</h2>
<ul>
<li><p>Bitfields auf Strukturen die Bits haben (Ints, …) in Data.Bits.Lens</p></li>
<li><p>Web-scraper in Package hexpat-lens</p>
<div class="sourceCode" id="cb29"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a>p <span class="op">^..</span> _HTML' <span class="op">.</span> to allNodes</span>
<span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> <span class="fu">traverse</span> <span class="op">.</span> named <span class="st">"a"</span></span>
<span id="cb29-3"><a href="#cb29-3" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> <span class="fu">traverse</span> <span class="op">.</span> ix <span class="st">"href"</span></span>
<span id="cb29-4"><a href="#cb29-4" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> filtered isLocal</span>
<span id="cb29-5"><a href="#cb29-5" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> to trimSpaces</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Zieht alle externen Links aus dem gegebenen HTML-Code in p um weitere ziele<br>
fürs crawlen zu finden.</p></li>
</ul>
</section>
<section id="erweiterungen" class="level2">
<h2 class="anchored" data-anchor-id="erweiterungen">Erweiterungen</h2>
<p>Bisher hatten wir Lenses nur auf Funktoren F. Die nächstmächtigere Klasse ist<br>
Applicative.</p>
<div class="sourceCode" id="cb30"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Traversal'</span> s a <span class="ot">=</span> <span class="kw">forall</span> f<span class="op">.</span> <span class="dt">Applicative</span> f</span>
<span id="cb30-2"><a href="#cb30-2" aria-hidden="true" tabindex="-1"></a> <span class="ot">=&gt;</span> (a <span class="ot">-&gt;</span> f a) <span class="ot">-&gt;</span> (s <span class="ot">-&gt;</span> f s)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Da wir den Container identisch lassen (weder s noch a wurde angefasst) muss sich<br>
etwas anderes ändern. Statt eines einzelnen Focus erhalten wir viele Foci.</p>
<p>Was ist ein Applicative überhaupt? Eine schwächere Monade (nur 1x Anwendung und<br>
kein Bind - dafür kann man die beliebig oft hintereinanderhängen).</p>
<div class="sourceCode" id="cb31"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> <span class="dt">Functor</span> f <span class="ot">=&gt;</span> <span class="dt">Applicative</span> f <span class="kw">where</span></span>
<span id="cb31-2"><a href="#cb31-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> pure ::</span> a <span class="ot">-&gt;</span> f a</span>
<span id="cb31-3"><a href="#cb31-3" aria-hidden="true" tabindex="-1"></a><span class="ot"> (&lt;*&gt;) ::</span> f (a <span class="ot">-&gt;</span> b) <span class="ot">-&gt;</span> f a <span class="ot">-&gt;</span> f b</span>
<span id="cb31-4"><a href="#cb31-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb31-5"><a href="#cb31-5" aria-hidden="true" tabindex="-1"></a><span class="co">-- Monade als Applicative:</span></span>
<span id="cb31-6"><a href="#cb31-6" aria-hidden="true" tabindex="-1"></a><span class="fu">pure</span> <span class="ot">=</span> <span class="fu">return</span></span>
<span id="cb31-7"><a href="#cb31-7" aria-hidden="true" tabindex="-1"></a>mf <span class="op">&lt;*&gt;</span> mx <span class="ot">=</span> <span class="kw">do</span> { f <span class="ot">&lt;-</span> mf; x <span class="ot">&lt;-</span> mx; <span class="fu">return</span> (f x) }</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Recap: Was macht eine Lens:</p>
<div class="sourceCode" id="cb32"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Adress</span> <span class="ot">=</span> <span class="dt">A</span> {<span class="ot"> _road ::</span> <span class="dt">String</span></span>
<span id="cb32-2"><a href="#cb32-2" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> _city ::</span> <span class="dt">String</span></span>
<span id="cb32-3"><a href="#cb32-3" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> _postcode ::</span> <span class="dt">String</span> }</span>
<span id="cb32-4"><a href="#cb32-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb32-5"><a href="#cb32-5" aria-hidden="true" tabindex="-1"></a><span class="ot">road ::</span> <span class="dt">Lens'</span> <span class="dt">Adress</span> <span class="dt">String</span></span>
<span id="cb32-6"><a href="#cb32-6" aria-hidden="true" tabindex="-1"></a>road elt_fn (<span class="dt">A</span> r c p) <span class="ot">=</span> (\r' <span class="ot">-&gt;</span> <span class="dt">A</span> r' c p) <span class="op">&lt;$&gt;</span> (elt_fn r)</span>
<span id="cb32-7"><a href="#cb32-7" aria-hidden="true" tabindex="-1"></a><span class="co">-- | "Hole" | | Thing to put in|</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Wenn man nun road &amp; city gleichzeitig bearbeiten will:</p>
<div class="sourceCode" id="cb33"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb33-1"><a href="#cb33-1" aria-hidden="true" tabindex="-1"></a><span class="ot">addr_strs ::</span> <span class="dt">Traversal'</span> <span class="dt">Address</span> <span class="dt">String</span></span>
<span id="cb33-2"><a href="#cb33-2" aria-hidden="true" tabindex="-1"></a>addr_strs elt_fn (<span class="dt">A</span> r c p)</span>
<span id="cb33-3"><a href="#cb33-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> <span class="op">...</span> (\r' c' <span class="ot">-&gt;</span> <span class="dt">A</span> r' c' p) <span class="op">..</span> (elt_fn r) <span class="op">..</span> (elt_fn c) <span class="op">..</span></span>
<span id="cb33-4"><a href="#cb33-4" aria-hidden="true" tabindex="-1"></a><span class="co">-- | function with 2 "Holes"| first Thing | second Thing</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>fmap kann nur 1 Loch stopfen, aber nicht mit n Löchern umgehen. Applicative mit<br>
&lt;*&gt; kann das.<br>
Somit gibt sich</p>
<div class="sourceCode" id="cb34"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true" tabindex="-1"></a><span class="ot">addr_strs ::</span> <span class="dt">Traversal'</span> <span class="dt">Address</span> <span class="dt">String</span></span>
<span id="cb34-2"><a href="#cb34-2" aria-hidden="true" tabindex="-1"></a>addr_strs elt_fn (<span class="dt">A</span> r c p)</span>
<span id="cb34-3"><a href="#cb34-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> <span class="fu">pure</span> (\r' c' <span class="ot">-&gt;</span> <span class="dt">A</span> r' c' p) <span class="op">&lt;*&gt;</span> (elt_fn r) <span class="op">&lt;*&gt;</span> (elt_fn c)</span>
<span id="cb34-4"><a href="#cb34-4" aria-hidden="true" tabindex="-1"></a><span class="co">-- lift in Appl. | function with 2 "Holes"| first Thing | second Thing</span></span>
<span id="cb34-5"><a href="#cb34-5" aria-hidden="true" tabindex="-1"></a><span class="co">-- oder kürzer</span></span>
<span id="cb34-6"><a href="#cb34-6" aria-hidden="true" tabindex="-1"></a><span class="ot">addr_strs ::</span> <span class="dt">Traversal'</span> <span class="dt">Address</span> <span class="dt">String</span></span>
<span id="cb34-7"><a href="#cb34-7" aria-hidden="true" tabindex="-1"></a>addr_strs elt_fn (<span class="dt">A</span> r c p)</span>
<span id="cb34-8"><a href="#cb34-8" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> (\r' c' <span class="ot">-&gt;</span> <span class="dt">A</span> r' c' p) <span class="op">&lt;$&gt;</span> (elt_fn r) <span class="op">&lt;*&gt;</span> (elt_fn c)</span>
<span id="cb34-9"><a href="#cb34-9" aria-hidden="true" tabindex="-1"></a><span class="co">-- pure x &lt;*&gt; y == x &lt;$&gt; y</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Wie würd eine modify-funktion aussehen?</p>
<div class="sourceCode" id="cb35"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb35-1"><a href="#cb35-1" aria-hidden="true" tabindex="-1"></a><span class="ot">over ::</span> <span class="dt">Lens'</span> s a <span class="ot">-&gt;</span> (a <span class="ot">-&gt;</span> a) <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> s</span>
<span id="cb35-2"><a href="#cb35-2" aria-hidden="true" tabindex="-1"></a>over ln f <span class="ot">=</span> runIdentity <span class="op">.</span> ln (<span class="dt">Identity</span> <span class="op">.</span> f)</span>
<span id="cb35-3"><a href="#cb35-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb35-4"><a href="#cb35-4" aria-hidden="true" tabindex="-1"></a><span class="ot">over ::</span> <span class="dt">Traversal'</span> s a <span class="ot">-&gt;</span> (a <span class="ot">-&gt;</span> a) <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> s</span>
<span id="cb35-5"><a href="#cb35-5" aria-hidden="true" tabindex="-1"></a>over ln f <span class="ot">=</span> runIdentity <span class="op">.</span> ln (<span class="dt">Identity</span> <span class="op">.</span> f)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Der Code ist derselbe - nur der Typ ist generischer. Auch die anderen Dinge<br>
funktioniert diese Erweiterung (für Identity und Const muss man noch ein paar<br>
dummy-Instanzen schreiben um sie von Functor auf Applicative oder Monad zu heben</p>
<ul>
<li>konkret reicht hier die Instanzierung von Monoid). In der Lens-Library ist<br>
daher meist Monad m statt Functor f gefordert.</li>
</ul>
</section>
<section id="wozu-dienen-die-erweiterungen" class="level2">
<h2 class="anchored" data-anchor-id="wozu-dienen-die-erweiterungen">Wozu dienen die Erweiterungen?</h2>
<p>Man kann mit Foci sehr selektiv vorgehen. Auch kann man diese durch Funktionen<br>
steuern. Beispisweise eine Function anwenden auf</p>
<ul>
<li>Jedes 2. Listenelement</li>
<li>Alle graden Elemente in einem Baum</li>
<li>Alle Namen in einer Tabelle, deren Gehalt &gt; 10.000€ ist</li>
</ul>
<p>Traversals und Lenses kann man trivial kombinieren (<code>lens . lens</code> =&gt; <code>lens</code>,<br>
<code>lens . traversal</code> =&gt; <code>traversal</code> etc.)</p>
</section>
<section id="wie-es-in-lens-wirklich-aussieht" class="level2">
<h2 class="anchored" data-anchor-id="wie-es-in-lens-wirklich-aussieht">Wie es in Lens wirklich aussieht</h2>
<p>In diesem Artikel wurde nur auf Monomorphic Lenses eingegangen. In der richtigen<br>
Library ist eine Lens</p>
<div class="sourceCode" id="cb36"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb36-1"><a href="#cb36-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Lens'</span> s a <span class="ot">=</span> <span class="dt">Lens</span> s s a a</span>
<span id="cb36-2"><a href="#cb36-2" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Lens</span> s t a b <span class="ot">=</span> <span class="kw">forall</span> f<span class="op">.</span> <span class="dt">Functor</span> f <span class="ot">=&gt;</span> (a <span class="ot">-&gt;</span> f b) <span class="ot">-&gt;</span> (s <span class="ot">-&gt;</span> f t)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>sodass sich auch die Typen ändern können um z.B. automatisch einen Konvertierten<br>
(sicheren) Typen aus einer unsicheren Datenstruktur zu geben.</p>
<p>Die modify-Funktion over ist auch</p>
<div class="sourceCode" id="cb37"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb37-1"><a href="#cb37-1" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;</span><span class="ot"> over ::</span> <span class="dt">Profunctor</span> p <span class="ot">=&gt;</span> <span class="dt">Setting</span> p s t a b <span class="ot">-&gt;</span> p a b <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> t</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<blockquote class="blockquote">
<p><em>Edward is deeply in thrall to abstractionitis</em> - Simon Peyton Jones</p>
</blockquote>
<p>Lens alleine definiert 39 newtypes, 34 data-types und 194 Typsynonyme…<br>
Ausschnitt</p>
<div class="sourceCode" id="cb38"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb38-1"><a href="#cb38-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- traverseOf :: Functor f =&gt; Iso s t a b -&gt; (a -&gt; f b) -&gt; s -&gt; f t</span></span>
<span id="cb38-2"><a href="#cb38-2" aria-hidden="true" tabindex="-1"></a><span class="co">-- traverseOf :: Functor f =&gt; Lens s t a b -&gt; (a -&gt; f b) -&gt; s -&gt; f t</span></span>
<span id="cb38-3"><a href="#cb38-3" aria-hidden="true" tabindex="-1"></a><span class="co">-- traverseOf :: Applicative f =&gt; Traversal s t a b -&gt; (a -&gt; f b) -&gt; s -&gt; f t</span></span>
<span id="cb38-4"><a href="#cb38-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb38-5"><a href="#cb38-5" aria-hidden="true" tabindex="-1"></a><span class="ot">traverseOf ::</span> <span class="dt">Over</span> p f s t a b <span class="ot">-&gt;</span> p a (f b) <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> f t</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>dafuq?</p>
</section>
</main> <!-- /main -->
<script id="quarto-html-after-body" type="application/javascript">
window.document.addEventListener("DOMContentLoaded", function (event) {
// Ensure there is a toggle, if there isn't float one in the top right
if (window.document.querySelector('.quarto-color-scheme-toggle') === null) {
const a = window.document.createElement('a');
a.classList.add('top-right');
a.classList.add('quarto-color-scheme-toggle');
a.href = "";
a.onclick = function() { try { window.quartoToggleColorScheme(); } catch {} return false; };
const i = window.document.createElement("i");
i.classList.add('bi');
a.appendChild(i);
window.document.body.appendChild(a);
}
window.setColorSchemeToggle(window.hasAlternateSentinel())
const icon = "";
const anchorJS = new window.AnchorJS();
anchorJS.options = {
placement: 'right',
icon: icon
};
anchorJS.add('.anchored');
const isCodeAnnotation = (el) => {
for (const clz of el.classList) {
if (clz.startsWith('code-annotation-')) {
return true;
}
}
return false;
}
const onCopySuccess = function(e) {
// button target
const button = e.trigger;
// don't keep focus
button.blur();
// flash "checked"
button.classList.add('code-copy-button-checked');
var currentTitle = button.getAttribute("title");
button.setAttribute("title", "Copied!");
let tooltip;
if (window.bootstrap) {
button.setAttribute("data-bs-toggle", "tooltip");
button.setAttribute("data-bs-placement", "left");
button.setAttribute("data-bs-title", "Copied!");
tooltip = new bootstrap.Tooltip(button,
{ trigger: "manual",
customClass: "code-copy-button-tooltip",
offset: [0, -8]});
tooltip.show();
}
setTimeout(function() {
if (tooltip) {
tooltip.hide();
button.removeAttribute("data-bs-title");
button.removeAttribute("data-bs-toggle");
button.removeAttribute("data-bs-placement");
}
button.setAttribute("title", currentTitle);
button.classList.remove('code-copy-button-checked');
}, 1000);
// clear code selection
e.clearSelection();
}
const getTextToCopy = function(trigger) {
const codeEl = trigger.previousElementSibling.cloneNode(true);
for (const childEl of codeEl.children) {
if (isCodeAnnotation(childEl)) {
childEl.remove();
}
}
return codeEl.innerText;
}
const clipboard = new window.ClipboardJS('.code-copy-button:not([data-in-quarto-modal])', {
text: getTextToCopy
});
clipboard.on('success', onCopySuccess);
if (window.document.getElementById('quarto-embedded-source-code-modal')) {
const clipboardModal = new window.ClipboardJS('.code-copy-button[data-in-quarto-modal]', {
text: getTextToCopy,
container: window.document.getElementById('quarto-embedded-source-code-modal')
});
clipboardModal.on('success', onCopySuccess);
}
var localhostRegex = new RegExp(/^(?:http|https):\/\/localhost\:?[0-9]*\//);
var mailtoRegex = new RegExp(/^mailto:/);
var filterRegex = new RegExp("https:\/\/nicole\.dresselhaus\.cloud");
var isInternal = (href) => {
return filterRegex.test(href) || localhostRegex.test(href) || mailtoRegex.test(href);
}
// Inspect non-navigation links and adorn them if external
var links = window.document.querySelectorAll('a[href]:not(.nav-link):not(.navbar-brand):not(.toc-action):not(.sidebar-link):not(.sidebar-item-toggle):not(.pagination-link):not(.no-external):not([aria-hidden]):not(.dropdown-item):not(.quarto-navigation-tool):not(.about-link)');
for (var i=0; i<links.length; i++) {
const link = links[i];
if (!isInternal(link.href)) {
// undo the damage that might have been done by quarto-nav.js in the case of
// links that we want to consider external
if (link.dataset.originalHref !== undefined) {
link.href = link.dataset.originalHref;
}
// target, if specified
link.setAttribute("target", "_blank");
if (link.getAttribute("rel") === null) {
link.setAttribute("rel", "noopener");
}
// default icon
link.classList.add("external");
}
}
function tippyHover(el, contentFn, onTriggerFn, onUntriggerFn) {
const config = {
allowHTML: true,
maxWidth: 500,
delay: 100,
arrow: false,
appendTo: function(el) {
return el.parentElement;
},
interactive: true,
interactiveBorder: 10,
theme: 'quarto',
placement: 'bottom-start',
};
if (contentFn) {
config.content = contentFn;
}
if (onTriggerFn) {
config.onTrigger = onTriggerFn;
}
if (onUntriggerFn) {
config.onUntrigger = onUntriggerFn;
}
window.tippy(el, config);
}
const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]');
for (var i=0; i<noterefs.length; i++) {
const ref = noterefs[i];
tippyHover(ref, function() {
// use id or data attribute instead here
let href = ref.getAttribute('data-footnote-href') || ref.getAttribute('href');
try { href = new URL(href).hash; } catch {}
const id = href.replace(/^#\/?/, "");
const note = window.document.getElementById(id);
if (note) {
return note.innerHTML;
} else {
return "";
}
});
}
const xrefs = window.document.querySelectorAll('a.quarto-xref');
const processXRef = (id, note) => {
// Strip column container classes
const stripColumnClz = (el) => {
el.classList.remove("page-full", "page-columns");
if (el.children) {
for (const child of el.children) {
stripColumnClz(child);
}
}
}
stripColumnClz(note)
if (id === null || id.startsWith('sec-')) {
// Special case sections, only their first couple elements
const container = document.createElement("div");
if (note.children && note.children.length > 2) {
container.appendChild(note.children[0].cloneNode(true));
for (let i = 1; i < note.children.length; i++) {
const child = note.children[i];
if (child.tagName === "P" && child.innerText === "") {
continue;
} else {
container.appendChild(child.cloneNode(true));
break;
}
}
if (window.Quarto?.typesetMath) {
window.Quarto.typesetMath(container);
}
return container.innerHTML
} else {
if (window.Quarto?.typesetMath) {
window.Quarto.typesetMath(note);
}
return note.innerHTML;
}
} else {
// Remove any anchor links if they are present
const anchorLink = note.querySelector('a.anchorjs-link');
if (anchorLink) {
anchorLink.remove();
}
if (window.Quarto?.typesetMath) {
window.Quarto.typesetMath(note);
}
if (note.classList.contains("callout")) {
return note.outerHTML;
} else {
return note.innerHTML;
}
}
}
for (var i=0; i<xrefs.length; i++) {
const xref = xrefs[i];
tippyHover(xref, undefined, function(instance) {
instance.disable();
let url = xref.getAttribute('href');
let hash = undefined;
if (url.startsWith('#')) {
hash = url;
} else {
try { hash = new URL(url).hash; } catch {}
}
if (hash) {
const id = hash.replace(/^#\/?/, "");
const note = window.document.getElementById(id);
if (note !== null) {
try {
const html = processXRef(id, note.cloneNode(true));
instance.setContent(html);
} finally {
instance.enable();
instance.show();
}
} else {
// See if we can fetch this
fetch(url.split('#')[0])
.then(res => res.text())
.then(html => {
const parser = new DOMParser();
const htmlDoc = parser.parseFromString(html, "text/html");
const note = htmlDoc.getElementById(id);
if (note !== null) {
const html = processXRef(id, note);
instance.setContent(html);
}
}).finally(() => {
instance.enable();
instance.show();
});
}
} else {
// See if we can fetch a full url (with no hash to target)
// This is a special case and we should probably do some content thinning / targeting
fetch(url)
.then(res => res.text())
.then(html => {
const parser = new DOMParser();
const htmlDoc = parser.parseFromString(html, "text/html");
const note = htmlDoc.querySelector('main.content');
if (note !== null) {
// This should only happen for chapter cross references
// (since there is no id in the URL)
// remove the first header
if (note.children.length > 0 && note.children[0].tagName === "HEADER") {
note.children[0].remove();
}
const html = processXRef(null, note);
instance.setContent(html);
}
}).finally(() => {
instance.enable();
instance.show();
});
}
}, function(instance) {
});
}
let selectedAnnoteEl;
const selectorForAnnotation = ( cell, annotation) => {
let cellAttr = 'data-code-cell="' + cell + '"';
let lineAttr = 'data-code-annotation="' + annotation + '"';
const selector = 'span[' + cellAttr + '][' + lineAttr + ']';
return selector;
}
const selectCodeLines = (annoteEl) => {
const doc = window.document;
const targetCell = annoteEl.getAttribute("data-target-cell");
const targetAnnotation = annoteEl.getAttribute("data-target-annotation");
const annoteSpan = window.document.querySelector(selectorForAnnotation(targetCell, targetAnnotation));
const lines = annoteSpan.getAttribute("data-code-lines").split(",");
const lineIds = lines.map((line) => {
return targetCell + "-" + line;
})
let top = null;
let height = null;
let parent = null;
if (lineIds.length > 0) {
//compute the position of the single el (top and bottom and make a div)
const el = window.document.getElementById(lineIds[0]);
top = el.offsetTop;
height = el.offsetHeight;
parent = el.parentElement.parentElement;
if (lineIds.length > 1) {
const lastEl = window.document.getElementById(lineIds[lineIds.length - 1]);
const bottom = lastEl.offsetTop + lastEl.offsetHeight;
height = bottom - top;
}
if (top !== null && height !== null && parent !== null) {
// cook up a div (if necessary) and position it
let div = window.document.getElementById("code-annotation-line-highlight");
if (div === null) {
div = window.document.createElement("div");
div.setAttribute("id", "code-annotation-line-highlight");
div.style.position = 'absolute';
parent.appendChild(div);
}
div.style.top = top - 2 + "px";
div.style.height = height + 4 + "px";
div.style.left = 0;
let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter");
if (gutterDiv === null) {
gutterDiv = window.document.createElement("div");
gutterDiv.setAttribute("id", "code-annotation-line-highlight-gutter");
gutterDiv.style.position = 'absolute';
const codeCell = window.document.getElementById(targetCell);
const gutter = codeCell.querySelector('.code-annotation-gutter');
gutter.appendChild(gutterDiv);
}
gutterDiv.style.top = top - 2 + "px";
gutterDiv.style.height = height + 4 + "px";
}
selectedAnnoteEl = annoteEl;
}
};
const unselectCodeLines = () => {
const elementsIds = ["code-annotation-line-highlight", "code-annotation-line-highlight-gutter"];
elementsIds.forEach((elId) => {
const div = window.document.getElementById(elId);
if (div) {
div.remove();
}
});
selectedAnnoteEl = undefined;
};
// Handle positioning of the toggle
window.addEventListener(
"resize",
throttle(() => {
elRect = undefined;
if (selectedAnnoteEl) {
selectCodeLines(selectedAnnoteEl);
}
}, 10)
);
function throttle(fn, ms) {
let throttle = false;
let timer;
return (...args) => {
if(!throttle) { // first call gets through
fn.apply(this, args);
throttle = true;
} else { // all the others get throttled
if(timer) clearTimeout(timer); // cancel #2
timer = setTimeout(() => {
fn.apply(this, args);
timer = throttle = false;
}, ms);
}
};
}
// Attach click handler to the DT
const annoteDls = window.document.querySelectorAll('dt[data-target-cell]');
for (const annoteDlNode of annoteDls) {
annoteDlNode.addEventListener('click', (event) => {
const clickedEl = event.target;
if (clickedEl !== selectedAnnoteEl) {
unselectCodeLines();
const activeEl = window.document.querySelector('dt[data-target-cell].code-annotation-active');
if (activeEl) {
activeEl.classList.remove('code-annotation-active');
}
selectCodeLines(clickedEl);
clickedEl.classList.add('code-annotation-active');
} else {
// Unselect the line
unselectCodeLines();
clickedEl.classList.remove('code-annotation-active');
}
});
}
const findCites = (el) => {
const parentEl = el.parentElement;
if (parentEl) {
const cites = parentEl.dataset.cites;
if (cites) {
return {
el,
cites: cites.split(' ')
};
} else {
return findCites(el.parentElement)
}
} else {
return undefined;
}
};
var bibliorefs = window.document.querySelectorAll('a[role="doc-biblioref"]');
for (var i=0; i<bibliorefs.length; i++) {
const ref = bibliorefs[i];
const citeInfo = findCites(ref);
if (citeInfo) {
tippyHover(citeInfo.el, function() {
var popup = window.document.createElement('div');
citeInfo.cites.forEach(function(cite) {
var citeDiv = window.document.createElement('div');
citeDiv.classList.add('hanging-indent');
citeDiv.classList.add('csl-entry');
var biblioDiv = window.document.getElementById('ref-' + cite);
if (biblioDiv) {
citeDiv.innerHTML = biblioDiv.innerHTML;
}
popup.appendChild(citeDiv);
});
return popup.innerHTML;
});
}
}
});
</script>
</div> <!-- /content -->
</body></html>