split web-dev

This commit is contained in:
2022-08-25 05:26:25 +02:00
parent 5e48571fda
commit da8594c678
36 changed files with 5399 additions and 642 deletions

View File

@ -47,7 +47,7 @@
<link href='tailwind.css?instanceId=faa07eb7-0f7a-4cb2-8347-d9aa01265a0e' rel='stylesheet' type='text/css' />
<link href='tailwind.css?instanceId=e7df680a-6a6d-4eef-bcd7-f91ac333071d' rel='stylesheet' type='text/css' />
<!-- Heist error element -->
<style>
@ -811,6 +811,42 @@
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg xmlns='http://www.w3.org/2000/svg' class='w-4 h-4 flex-shrink-0 inline text-gray-700' viewBox='0 0 20 20' fill='currentColor'>
<path fill-rule='evenodd' d='M2 6a2 2 0 012-2h4l2 2h4a2 2 0 012 2v1H8a3 3 0 00-3 3v1.5a1.5 1.5 0 01-3 0V6z' clip-rule='evenodd'></path>
<path d='M6 12a2 2 0 012-2h8a2 2 0 012 2v2a2 2 0 01-2 2H2h2a2 2 0 002-2v-2z'></path>
</svg>
<a class='font-bold hover:underline truncate' title='Webapp-Development in Haskell' href='Haskell/Webapp-Example'>
Webapp-Development in Haskell
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
<!-- Variable bindings for this tree-->
@ -831,8 +867,8 @@
</svg>
<a class='hover:underline truncate' title='Webapp-Development in Haskell' href='Haskell/Webapp_Development'>
Webapp-Development in Haskell
<a class='hover:underline truncate' title='Webapp-Example: Main.hs' href='Haskell/Webapp-Example/Main.hs'>
Webapp-Example: Main.hs
</a>
@ -844,6 +880,48 @@
</div>
<!-- Variable bindings for this tree-->
<!-- Rendering of this tree -->
<div class='pl-2'>
<!-- Node's rootLabel-->
<div class='flex items-center my-2 space-x-2 justify-left'>
<svg class='w-4 h-4 flex-shrink-0 inline' fill='none' stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z'>
</path>
</svg>
<a class='hover:underline truncate' title='Webapp-Example: MyService/Types.hs' href='Haskell/Webapp-Example/MyService_Types.hs'>
Webapp-Example: MyService/Types.hs
</a>
</div>
<!-- Node's children forest, displayed only on active trees
TODO: Use <details> to toggle visibility?
-->
</div>
</div>
@ -1169,7 +1247,7 @@
<p class='mb-3'>
Die Idee dahinter ist, dass man Zugriffsabstraktionen über Daten verknüpfen kann. Als einfachen Datenstruktur kann man einen Record mit der entsprechenden Syntax nehmen.
</p>
<h3 id='beispiel' class='mt-6 mb-2 text-3xl font-bold text-gray-700'>Beispiel</h3><div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>data Person = P { name :: String
<h3 id='beispiel' class='mt-6 mb-2 text-3xl font-bold text-gray-700'>Beispiel</h3><div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>data Person = P { name :: String
, addr :: Address
, salary :: Int }
data Address = A { road :: String
@ -1207,7 +1285,7 @@ data Address = A { road :: String
</li>
</ul>
<h3 id='was-wir-gern-hätten' class='mt-6 mb-2 text-3xl font-bold text-gray-700'>Was wir gern hätten</h3><div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>data Person = P { name :: String
<h3 id='was-wir-gern-hätten' class='mt-6 mb-2 text-3xl font-bold text-gray-700'>Was wir gern hätten</h3><div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>data Person = P { name :: String
, addr :: Address
, salary :: Int }
-- a lens for each field
@ -1222,7 +1300,7 @@ composeL :: Lens' s1 s2 -&gt; Lens s2 a -&gt; Lens' s1 a</code></pre></div><h3 i
<p class='mb-3'>
Mit diesen Dingen (wenn wir sie hätten) könnte man dann
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>data Person = P { name :: String
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>data Person = P { name :: String
, addr :: Address
, salary :: Int }
data Address = A { road :: String
@ -1234,7 +1312,7 @@ setPostcode pc p
<p class='mb-3'>
machen und wäre fertig.
</p>
<h2 id='trivialer-ansatz' class='inline-block mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Trivialer Ansatz</h2><h3 id='gettersetter-als-lens-methoden' class='mt-6 mb-2 text-3xl font-bold text-gray-700'>Getter/Setter als Lens-Methoden</h3><div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>data LensR s a = L { viewR :: s -&gt; a
<h2 id='trivialer-ansatz' class='inline-block mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Trivialer Ansatz</h2><h3 id='gettersetter-als-lens-methoden' class='mt-6 mb-2 text-3xl font-bold text-gray-700'>Getter/Setter als Lens-Methoden</h3><div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>data LensR s a = L { viewR :: s -&gt; a
, setR :: a -&gt; s -&gt; s }
composeL (L v1 u1) (L v2 u2)
@ -1251,7 +1329,7 @@ composeL (L v1 u1) (L v2 u2)
<p class='mb-3'>
Auslesen traversiert die Datenstruktur, dann wird die Funktion angewendet und zum setzen wird die Datenstruktur erneut traversiert:
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>over :: LensR s a -&gt; (a -&gt; a) -&gt; s -&gt; s
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>over :: LensR s a -&gt; (a -&gt; a) -&gt; s -&gt; s
over ln f s = setR l (f (viewR l s)) s</code></pre></div>
<ul class='my-3 ml-6 space-y-1 list-disc'>
@ -1260,7 +1338,7 @@ over ln f s = setR l (f (viewR l s)) s</code></pre></div>
</li>
</ul>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>data LensR s a
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>data LensR s a
= L { viewR :: s -&gt; a
, setR :: a -&gt; s -&gt; s
, mod :: (a-&gt;a) -&gt; s -&gt; s
@ -1273,7 +1351,7 @@ over ln f s = setR l (f (viewR l s)) s</code></pre></div>
<p class='mb-3'>
Man kann alle Monaden abstrahieren. Functor reicht schon:
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>data LensR s a
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>data LensR s a
= L { viewR :: s -&gt; a
, setR :: a -&gt; s -&gt; s
, mod :: (a-&gt;a) -&gt; s -&gt; s
@ -1295,14 +1373,14 @@ over ln f s = setR l (f (viewR l s)) s</code></pre></div>
<p class='mb-3'>
Stellt sich raus: Die sind isomorph! Auch wenn die von den Typen her komplett anders aussehen.
</p>
<h2 id='benutzen-einer-lens-als-setter' class='inline-block mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Benutzen einer Lens als Setter</h2><div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>set :: Lens' s a -&gt; (a -&gt; s -&gt; s)
<h2 id='benutzen-einer-lens-als-setter' class='inline-block mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Benutzen einer Lens als Setter</h2><div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>set :: Lens' s a -&gt; (a -&gt; s -&gt; s)
set ln a s = --...umm...
--:t ln =&gt; (a -&gt; f a) -&gt; s -&gt; f s
-- =&gt; get s out of f s to return it</code></pre></div>
<p class='mb-3'>
Wir können für f einfach die “Identity”-Monade nehmen, die wir nachher wegcasten können.
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>newtype Identity a = Identity a
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>newtype Identity a = Identity a
-- Id :: a -&gt; Identity a
runIdentity :: Identity s -&gt; s
@ -1313,7 +1391,7 @@ instance Functor Identity where
<p class='mb-3'>
somit ist set einfach nur
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>set :: Lens' s a -&gt; (a -&gt; s -&gt; s)
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>set :: Lens' s a -&gt; (a -&gt; s -&gt; s)
set ln x s
= runIdentity (ls set_fld s)
where
@ -1330,7 +1408,7 @@ set ln x = runIdentity . ln (Identity . const x)</code></pre></div><h2 id='benut
Dasselbe wie Set, nur dass wir den Parameter nicht entsorgen, sondern in die mitgelieferte Funktion stopfen.
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>over :: Lens' s a -&gt; (a -&gt; a) -&gt; s -&gt; s
over ln f = runIdentity . ln (Identity . f)</code></pre></div><h2 id='benutzen-einer-lens-als-getter' class='inline-block mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Benutzen einer Lens als Getter</h2><div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>view :: Lens' s a -&gt; (s -&gt; a)
over ln f = runIdentity . ln (Identity . f)</code></pre></div><h2 id='benutzen-einer-lens-als-getter' class='inline-block mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Benutzen einer Lens als Getter</h2><div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>view :: Lens' s a -&gt; (s -&gt; a)
view ln s = --...umm...
--:t ln =&gt; (a -&gt; f a) -&gt; s -&gt; f s
-- =&gt; get a out of the (f s) return-value
@ -1338,7 +1416,7 @@ view ln s = --...umm...
<p class='mb-3'>
Auch hier gibt es einen netten Funktor. Wir packen das “a” einfach in das “f” und werfen das “s” am Ende weg.
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>newtype Const v a = Const v
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>newtype Const v a = Const v
getConst :: Const v a -&gt; v
getConst (Const x) = x
@ -1349,7 +1427,7 @@ instance Functor (Const v) where
<p class='mb-3'>
somit ergibt sich
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>view :: Lens' s a -&gt; (s -&gt; a)
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>view :: Lens' s a -&gt; (s -&gt; a)
view ln s
= getConst (ln Const s)
-- Const :: s -&gt; Const a s</code></pre></div>
@ -1366,7 +1444,7 @@ view ln = getConst . ln Const</code></pre></div><h2 id='lenses-bauen' class='inl
<p class='mb-3'>
Für unser Personen-Beispiel vom Anfang:
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>data Person = P { _name :: String, _salary :: Int }
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>data Person = P { _name :: String, _salary :: Int }
name :: Lens' Person String
-- name :: Functor f =&gt; (String -&gt; f String)
@ -1382,7 +1460,7 @@ name elt_fn (P n s)
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>name elt_fn (P n s)
= (\n' -&gt; P n' s) &lt;$&gt; (elt_fn n)
-- | Focus | |Function|</code></pre></div><h2 id='wie-funktioniert-das-intern' class='inline-block mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Wie funktioniert das intern?</h2><div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>view name (P {_name="Fred", _salary=100})
-- | Focus | |Function|</code></pre></div><h2 id='wie-funktioniert-das-intern' class='inline-block mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Wie funktioniert das intern?</h2><div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>view name (P {_name="Fred", _salary=100})
-- inline view-function
= getConst (name Const (P {_name="Fred", _salary=100})
-- inline name
@ -1446,14 +1524,14 @@ name elt_fn (P n s)
<p class='mb-3'>
Der Code um die Lenses zu bauen ist für records immer Identisch:
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>data Person = P { _name :: String, _salary :: Int }
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>data Person = P { _name :: String, _salary :: Int }
name :: Lens' Person String
name elt_fn (P n s) = (\n' -&gt; P n' s) &lt;$&gt; (elt_fn n)</code></pre></div>
<p class='mb-3'>
Daher kann man einfach
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>import Control.Lens.TH
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>import Control.Lens.TH
data Person = P { _name :: String, _salary :: Int }
$(makeLenses ''Person)</code></pre></div>
@ -1464,7 +1542,7 @@ $(makeLenses ''Person)</code></pre></div>
<p class='mb-3'>
Will man das aber haben, muss man selbst in den Control.Lens.TH-Code schauen.
</p>
<h2 id='lenses-für-den-beispielcode' class='inline-block mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Lenses für den Beispielcode</h2><div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>import Control.Lens.TH
<h2 id='lenses-für-den-beispielcode' class='inline-block mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Lenses für den Beispielcode</h2><div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>import Control.Lens.TH
data Person = P { _name :: String
, _addr :: Address
@ -1477,7 +1555,7 @@ $(makeLenses ''Person)
$(makeLenses ''Address)
setPostcode :: String -&gt; Person -&gt; Person
setPostcode pc p = set (addr . postcode) pc p</code></pre></div><h2 id='shortcuts-mit-line-noise' class='inline-block mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Shortcuts mit “Line-Noise”</h2><div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>-- ...
setPostcode pc p = set (addr . postcode) pc p</code></pre></div><h2 id='shortcuts-mit-line-noise' class='inline-block mt-6 mb-4 text-4xl font-bold text-gray-700 border-b-2'>Shortcuts mit “Line-Noise”</h2><div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>-- ...
setPostcode :: String -&gt; Person -&gt; Person
setPostcode pc p = addr . postcode .~ pc $ p
@ -1493,7 +1571,7 @@ getPostcode p = p ^. $ addr . postcode
<p class='mb-3'>
Man kann mit Lenses sogar Felder emulieren, die gar nicht da sind. Angenommen folgender Code:
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>data Temp = T { _fahrenheit :: Float }
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>data Temp = T { _fahrenheit :: Float }
$(makeLenses ''Temp)
-- liefert Lens: fahrenheit :: Lens Temp Float
@ -1510,7 +1588,7 @@ centigrade centi_fn (T faren)
<p class='mb-3'>
Das ganze kann man auch parametrisieren und auf Non-Record-Strukturen anwenden. Beispielhaft an einer Map verdeutlicht:
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>-- from Data.Lens.At
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>-- from Data.Lens.At
at :: Ord k =&gt; k -&gt; Lens' (Map k v) (Maybe v)
-- oder identisch, wenn man die Lens' auflöst:
@ -1543,7 +1621,7 @@ at k mb_fn m
<p class='mb-3'>
Web-scraper in Package hexpat-lens
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>p ^.. _HTML' . to allNodes
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>p ^.. _HTML' . to allNodes
. traverse . named "a"
. traverse . ix "href"
. filtered isLocal
@ -1559,7 +1637,7 @@ at k mb_fn m
<p class='mb-3'>
Bisher hatten wir Lenses nur auf Funktoren F. Die nächstmächtigere Klasse ist Applicative.
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>type Traversal' s a = forall f. Applicative f
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>type Traversal' s a = forall f. Applicative f
=&gt; (a -&gt; f a) -&gt; (s -&gt; f s)</code></pre></div>
<p class='mb-3'>
Da wir den Container identisch lassen (weder s noch a wurde angefasst) muss sich etwas anderes ändern. Statt eines einzelnen Focus erhalten wir viele Foci.
@ -1568,7 +1646,7 @@ at k mb_fn m
<p class='mb-3'>
Was ist ein Applicative überhaupt? Eine schwächere Monade (nur 1x Anwendung und kein Bind - dafür kann man die beliebig oft hintereinanderhängen).
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>class Functor f =&gt; Applicative f where
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>class Functor f =&gt; Applicative f where
pure :: a -&gt; f a
(&lt;*&gt;) :: f (a -&gt; b) -&gt; f a -&gt; f b
@ -1578,7 +1656,7 @@ mf &lt;*&gt; mx = do { f &lt;- mf; x &lt;- mx; return (f x) }</code></pre></div>
<p class='mb-3'>
Recap: Was macht eine Lens:
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>data Adress = A { _road :: String
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>data Adress = A { _road :: String
, _city :: String
, _postcode :: String }
@ -1588,14 +1666,14 @@ road elt_fn (A r c p) = (\r' -&gt; A r' c p) &lt;$&gt; (elt_fn r)
<p class='mb-3'>
Wenn man nun road & city gleichzeitig bearbeiten will:
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>addr_strs :: Traversal' Address String
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>addr_strs :: Traversal' Address String
addr_strs elt_fn (A r c p)
= ... (\r' c' -&gt; A r' c' p) .. (elt_fn r) .. (elt_fn c) ..
-- | function with 2 "Holes"| first Thing | second Thing</code></pre></div>
<p class='mb-3'>
fmap kann nur 1 Loch stopfen, aber nicht mit n Löchern umgehen. Applicative mit &lt;*&gt; kann das.<br />Somit gibt sich
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>addr_strs :: Traversal' Address String
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>addr_strs :: Traversal' Address String
addr_strs elt_fn (A r c p)
= pure (\r' c' -&gt; A r' c' p) &lt;*&gt; (elt_fn r) &lt;*&gt; (elt_fn c)
-- lift in Appl. | function with 2 "Holes"| first Thing | second Thing
@ -1672,7 +1750,7 @@ type Lens s t a b = forall f. Functor f =&gt; (a -&gt; f b) -&gt; (s -&gt; f t)<
<p class='mb-3'>
Lens alleine definiert 39 newtypes, 34 data-types und 194 Typsynonyme…<br />Ausschnitt
</p>
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell numberLines'>-- traverseOf :: Functor f =&gt; Iso s t a b -&gt; (a -&gt; f b) -&gt; s -&gt; f t
<div class='py-0.5 mb-3 text-sm'><pre><code class='haskell language-haskell'>-- traverseOf :: Functor f =&gt; Iso s t a b -&gt; (a -&gt; f b) -&gt; s -&gt; f t
-- traverseOf :: Functor f =&gt; Lens s t a b -&gt; (a -&gt; f b) -&gt; s -&gt; f t
-- traverseOf :: Applicative f =&gt; Traversal s t a b -&gt; (a -&gt; f b) -&gt; s -&gt; f t