mirror of
https://github.com/ikoHSE/sc-lectures.git
synced 2024-10-05 17:37:15 +03:00
L4
This commit is contained in:
parent
b084722f75
commit
c738d304c6
678
4.html
Normal file
678
4.html
Normal file
@ -0,0 +1,678 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="generator" content="pandoc">
|
||||
<meta name="author" content="Ilya Kostyuchenko">
|
||||
<title>4</title>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui">
|
||||
<link rel="stylesheet" href="reveal.js/css/reset.css">
|
||||
<link rel="stylesheet" href="reveal.js/css/reveal.css">
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
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; }
|
||||
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;
|
||||
background-color: #232629;
|
||||
color: #7a7c7d;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ color: #ffffff; background-color: #000000; }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span. { color: #ffffff; } /* Normal */
|
||||
code span.al { color: #95da4c; background-color: #4d1f24; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #86d5a4; } /* Annotation */
|
||||
code span.at { color: #94cbf0; } /* Attribute */
|
||||
code span.bn { color: #ffb06a; } /* BaseN */
|
||||
code span.bu { color: #bdd1d3; } /* BuiltIn */
|
||||
code span.cf { color: #ffd998; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #a4e0ff; } /* Char */
|
||||
code span.cn { color: #73d9d9; font-weight: bold; } /* Constant */
|
||||
code span.co { color: #a1a6a8; } /* Comment */
|
||||
code span.cv { color: #b6c8c9; } /* CommentVar */
|
||||
code span.do { color: #a43340; } /* Documentation */
|
||||
code span.dt { color: #7cc0ec; } /* DataType */
|
||||
code span.dv { color: #f6b77f; } /* DecVal */
|
||||
code span.er { color: #e9848e; text-decoration: underline; } /* Error */
|
||||
code span.ex { color: #96d5ff; font-weight: bold; } /* Extension */
|
||||
code span.fl { color: #fdae67; } /* Float */
|
||||
code span.fu { color: #d487f4; } /* Function */
|
||||
code span.im { color: #76e8a6; } /* Import */
|
||||
code span.in { color: #f6b176; } /* Information */
|
||||
code span.kw { color: #ebebc9; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #ececde; } /* Operator */
|
||||
code span.ot { color: #86e9b0; } /* Other */
|
||||
code span.pp { color: #62e298; } /* Preprocessor */
|
||||
code span.re { color: #83c7f4; background-color: #153042; } /* RegionMarker */
|
||||
code span.sc { color: #80ccf4; } /* SpecialChar */
|
||||
code span.ss { color: #ff909b; } /* SpecialString */
|
||||
code span.st { color: #ff9f9f; } /* String */
|
||||
code span.va { color: #56d4d4; } /* Variable */
|
||||
code span.vs { color: #eca2a9; } /* VerbatimString */
|
||||
code span.wa { color: #eca2a9; } /* Warning */
|
||||
</style>
|
||||
<link rel="stylesheet" href="reveal.js/css/theme/superblack.css" id="theme">
|
||||
<!-- Printing and PDF exports -->
|
||||
<script>
|
||||
var link = document.createElement( 'link' );
|
||||
link.rel = 'stylesheet';
|
||||
link.type = 'text/css';
|
||||
link.href = window.location.search.match( /print-pdf/gi ) ? 'reveal.js/css/print/pdf.css' : 'reveal.js/css/print/paper.css';
|
||||
document.getElementsByTagName( 'head' )[0].appendChild( link );
|
||||
</script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="reveal.js/lib/js/html5shiv.js"></script>
|
||||
<![endif]-->
|
||||
<style>
|
||||
div.sourceCode {
|
||||
background-color: transparent;
|
||||
overflow: auto;
|
||||
margin: 0em;
|
||||
}
|
||||
|
||||
pre.sourceCode {
|
||||
margin: 8px auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="reveal">
|
||||
<div class="slides">
|
||||
|
||||
|
||||
<section id="функциональное-программирование" class="slide level1">
|
||||
<h1>Функциональное программирование</h1>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<h2 id="напоминалочка">Напоминалочка</h2>
|
||||
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1"></a><span class="kw">class</span> <span class="dt">Functor</span> f <span class="kw">where</span></span>
|
||||
<span id="cb1-2"><a href="#cb1-2"></a><span class="ot"> fmap ::</span> (a <span class="ot">-></span> b) <span class="ot">-></span> f a <span class="ot">-></span> f b</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1"></a><span class="kw">class</span> <span class="dt">Functor</span> f <span class="ot">=></span> <span class="dt">Applicative</span> f <span class="kw">where</span></span>
|
||||
<span id="cb2-2"><a href="#cb2-2"></a><span class="ot"> (<*>) ::</span> f (a <span class="ot">-></span> b) <span class="ot">-></span> f a <span class="ot">-></span> f b</span>
|
||||
<span id="cb2-3"><a href="#cb2-3"></a><span class="ot"> pure ::</span> a <span class="ot">-></span> f a</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1"></a><span class="kw">class</span> <span class="dt">Applicative</span> f <span class="ot">=></span> <span class="dt">Monad</span> f <span class="kw">where</span></span>
|
||||
<span id="cb3-2"><a href="#cb3-2"></a><span class="ot"> (>>=) ::</span> f a <span class="ot">-></span> (a <span class="ot">-></span> f b) <span class="ot">-></span> f b</span>
|
||||
<span id="cb3-3"><a href="#cb3-3"></a><span class="ot"> return ::</span> a <span class="ot">-></span> f a</span></code></pre></div>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<h2 id="either">Either</h2>
|
||||
<div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1"></a><span class="kw">data</span> <span class="dt">Either</span> a b <span class="ot">=</span> <span class="dt">Left</span> a <span class="op">|</span> <span class="dt">Right</span> b</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb5"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1"></a><span class="ot">throwError ::</span> e <span class="ot">-></span> <span class="dt">Either</span> e a</span>
|
||||
<span id="cb5-2"><a href="#cb5-2"></a>throwError <span class="ot">=</span> <span class="dt">Left</span></span></code></pre></div>
|
||||
<div class="sourceCode" id="cb6"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1"></a><span class="kw">data</span> <span class="dt">PaymentError</span> <span class="ot">=</span> <span class="dt">InsufficientBalance</span></span>
|
||||
<span id="cb6-2"><a href="#cb6-2"></a></span>
|
||||
<span id="cb6-3"><a href="#cb6-3"></a><span class="ot">pay ::</span> <span class="dt">Balance</span> <span class="ot">-></span> <span class="dt">Price</span> <span class="ot">-></span> <span class="dt">Either</span> <span class="dt">PaymentError</span> <span class="dt">Balance</span></span>
|
||||
<span id="cb6-4"><a href="#cb6-4"></a>pay balance price <span class="ot">=</span> <span class="kw">do</span></span>
|
||||
<span id="cb6-5"><a href="#cb6-5"></a> <span class="kw">let</span> newBalance <span class="ot">=</span> balance <span class="op">-</span> price</span>
|
||||
<span id="cb6-6"><a href="#cb6-6"></a> when (newBalance <span class="op"><</span> <span class="dv">0</span>) (throwError <span class="dt">InsufficientBalance</span>)</span>
|
||||
<span id="cb6-7"><a href="#cb6-7"></a> <span class="fu">return</span> newBalance</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb7"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1"></a><span class="ot">when ::</span> (<span class="dt">Applicative</span> f) <span class="ot">=></span> <span class="dt">Bool</span> <span class="ot">-></span> f () <span class="ot">-></span> f ()</span>
|
||||
<span id="cb7-2"><a href="#cb7-2"></a>when p s <span class="ot">=</span> <span class="kw">if</span> p <span class="kw">then</span> s <span class="kw">else</span> <span class="fu">pure</span> ()</span></code></pre></div>
|
||||
</section>
|
||||
<section id="state" class="slide level1">
|
||||
<h1>State</h1>
|
||||
<div class="sourceCode" id="cb8"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1"></a><span class="kw">data</span> <span class="dt">State</span> s a <span class="ot">=</span> <span class="dt">State</span> {<span class="ot"> runState ::</span> s <span class="ot">-></span> (a, s) }</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb9"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb9-1"><a href="#cb9-1"></a><span class="ot">get ::</span> <span class="dt">State</span> s s</span>
|
||||
<span id="cb9-2"><a href="#cb9-2"></a>get <span class="ot">=</span> <span class="dt">State</span> (\s <span class="ot">-></span> (s, s))</span>
|
||||
<span id="cb9-3"><a href="#cb9-3"></a></span>
|
||||
<span id="cb9-4"><a href="#cb9-4"></a><span class="ot">put ::</span> s <span class="ot">-></span> <span class="dt">State</span> s ()</span>
|
||||
<span id="cb9-5"><a href="#cb9-5"></a>put s <span class="ot">=</span> <span class="dt">State</span> (\_ <span class="ot">-></span> (_, s))</span></code></pre></div>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<div class="sourceCode" id="cb10"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb10-1"><a href="#cb10-1"></a><span class="ot">getNewId ::</span> <span class="dt">State</span> <span class="dt">TransactionId</span> <span class="dt">TransactionId</span></span>
|
||||
<span id="cb10-2"><a href="#cb10-2"></a>getNewId <span class="ot">=</span> <span class="kw">do</span></span>
|
||||
<span id="cb10-3"><a href="#cb10-3"></a> newId <span class="ot"><-</span> get</span>
|
||||
<span id="cb10-4"><a href="#cb10-4"></a> put (<span class="fu">succ</span> newId)</span>
|
||||
<span id="cb10-5"><a href="#cb10-5"></a> <span class="fu">return</span> newId</span>
|
||||
<span id="cb10-6"><a href="#cb10-6"></a></span>
|
||||
<span id="cb10-7"><a href="#cb10-7"></a><span class="kw">data</span> <span class="dt">Transaction</span> <span class="ot">=</span> <span class="dt">Transaction</span></span>
|
||||
<span id="cb10-8"><a href="#cb10-8"></a> {<span class="ot"> transactionId ::</span> <span class="dt">TransactionId</span>,</span>
|
||||
<span id="cb10-9"><a href="#cb10-9"></a><span class="ot"> transactionAmount ::</span> <span class="dt">Price</span></span>
|
||||
<span id="cb10-10"><a href="#cb10-10"></a> }</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb11"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb11-1"><a href="#cb11-1"></a><span class="ot">registerPayment ::</span> <span class="dt">Price</span> <span class="ot">-></span> <span class="dt">State</span> <span class="dt">TransactionId</span> <span class="dt">Transaction</span></span>
|
||||
<span id="cb11-2"><a href="#cb11-2"></a>registerPayment price <span class="ot">=</span> <span class="kw">do</span></span>
|
||||
<span id="cb11-3"><a href="#cb11-3"></a> newId <span class="ot"><-</span> getNewId</span>
|
||||
<span id="cb11-4"><a href="#cb11-4"></a> <span class="fu">return</span> <span class="dt">Transaction</span></span>
|
||||
<span id="cb11-5"><a href="#cb11-5"></a> { transactionId <span class="ot">=</span> newId</span>
|
||||
<span id="cb11-6"><a href="#cb11-6"></a> transactionAmount <span class="ot">=</span> price</span>
|
||||
<span id="cb11-7"><a href="#cb11-7"></a> }</span></code></pre></div>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<div class="sourceCode" id="cb12"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb12-1"><a href="#cb12-1"></a><span class="ot">registerPayment ::</span> <span class="dt">Price</span> <span class="ot">-></span> <span class="dt">State</span> <span class="dt">TransactionId</span> <span class="dt">Transaction</span></span>
|
||||
<span id="cb12-2"><a href="#cb12-2"></a><span class="ot">pay ::</span> <span class="dt">Balance</span> <span class="ot">-></span> <span class="dt">Price</span> <span class="ot">-></span> <span class="dt">Either</span> <span class="dt">PaymentError</span> <span class="dt">Balance</span></span></code></pre></div>
|
||||
<div class="sourceCode" id="cb13"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb13-1"><a href="#cb13-1"></a>payAndRegister balance price <span class="ot">=</span> <span class="kw">do</span></span>
|
||||
<span id="cb13-2"><a href="#cb13-2"></a> newBalance <span class="ot"><-</span> pay balance price</span>
|
||||
<span id="cb13-3"><a href="#cb13-3"></a> transacation <span class="ot"><-</span> registerPayment price</span>
|
||||
<span id="cb13-4"><a href="#cb13-4"></a> <span class="fu">return</span> (newBalance, transaction)</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb14"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb14-1"><a href="#cb14-1"></a><span class="ot">(>=>) ::</span> <span class="dt">Monad</span> m <span class="ot">=></span> (a <span class="ot">-></span> m b) <span class="ot">-></span> (b <span class="ot">-></span> m c) <span class="ot">-></span> (a <span class="ot">-></span> m c)</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb15"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb15-1"><a href="#cb15-1"></a><span class="co">-- Такого нет</span></span>
|
||||
<span id="cb15-2"><a href="#cb15-2"></a>(<span class="op">>=></span>)</span>
|
||||
<span id="cb15-3"><a href="#cb15-3"></a><span class="ot"> ::</span> (<span class="dt">Monad</span> m, <span class="dt">Monad</span> n)</span>
|
||||
<span id="cb15-4"><a href="#cb15-4"></a> <span class="ot">=></span> (a <span class="ot">-></span> m b) <span class="ot">-></span> (b <span class="ot">-></span> n c) <span class="ot">-></span> (a <span class="ot">-></span> <span class="op">?</span> c)</span></code></pre></div>
|
||||
<p><span class="fragment">На каждый чих нужно создавать новый тип?</span></p>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<p>Мы хотим:</p>
|
||||
<ol type="1">
|
||||
<li class="fragment">Отделить эффекты от конкретной монады</li>
|
||||
<li class="fragment">Получить возможность комбинировать разные эффекты</li>
|
||||
</ol>
|
||||
<p><span class="fragment">(И чтобы не надо было на каждый случай новый тип создавать)</span></p>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<div class="sourceCode" id="cb16"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb16-1"><a href="#cb16-1"></a><span class="ot">registerPayment ::</span> <span class="dt">Price</span> <span class="ot">-></span> <span class="dt">State</span> <span class="dt">TransactionId</span> <span class="dt">Transaction</span></span>
|
||||
<span id="cb16-2"><a href="#cb16-2"></a><span class="ot">pay ::</span> <span class="dt">Balance</span> <span class="ot">-></span> <span class="dt">Price</span> <span class="ot">-></span> <span class="dt">Either</span> <span class="dt">PaymentError</span> <span class="dt">Balance</span></span></code></pre></div>
|
||||
<div class="sourceCode" id="cb17"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb17-1"><a href="#cb17-1"></a>registerPayment</span>
|
||||
<span id="cb17-2"><a href="#cb17-2"></a><span class="ot"> ::</span> <span class="dt">MonadState</span> <span class="dt">TransactionId</span> m</span>
|
||||
<span id="cb17-3"><a href="#cb17-3"></a> <span class="ot">=></span> <span class="dt">Price</span> <span class="ot">-></span> m <span class="dt">Transaction</span></span>
|
||||
<span id="cb17-4"><a href="#cb17-4"></a>pay</span>
|
||||
<span id="cb17-5"><a href="#cb17-5"></a><span class="ot"> ::</span> <span class="dt">MonadError</span> <span class="dt">PaymentError</span> m</span>
|
||||
<span id="cb17-6"><a href="#cb17-6"></a> <span class="ot">=></span> <span class="dt">Balance</span> <span class="ot">-></span> <span class="dt">Price</span> <span class="ot">-></span> m <span class="dt">Balance</span></span></code></pre></div>
|
||||
<div class="sourceCode" id="cb18"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb18-1"><a href="#cb18-1"></a>payAndRegister</span>
|
||||
<span id="cb18-2"><a href="#cb18-2"></a><span class="ot"> ::</span> (<span class="dt">MonadState</span> <span class="dt">TransactionId</span> m, <span class="dt">MonadError</span> <span class="dt">PaymentError</span> m)</span>
|
||||
<span id="cb18-3"><a href="#cb18-3"></a> <span class="ot">=></span> <span class="dt">Balance</span> <span class="ot">-></span> <span class="dt">Price</span> <span class="ot">-></span> m (<span class="dt">Balance</span>, <span class="dt">Transaction</span>)</span></code></pre></div>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<p>На самом деле мы хотим чтобы понятие “state” не было привязано к конкретной монаде.</p>
|
||||
<p><span class="fragment">Мы хотим чтобы “базовые операции” не были привязаны к конкретной монаде.</span></p>
|
||||
<div class="sourceCode" id="cb19"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb19-1"><a href="#cb19-1"></a><span class="co">-- Было</span></span>
|
||||
<span id="cb19-2"><a href="#cb19-2"></a><span class="ot">get ::</span> <span class="dt">State</span> s s</span>
|
||||
<span id="cb19-3"><a href="#cb19-3"></a><span class="ot">put ::</span> s <span class="ot">-></span> <span class="dt">State</span> s ()</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb20"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb20-1"><a href="#cb20-1"></a><span class="co">-- Стало</span></span>
|
||||
<span id="cb20-2"><a href="#cb20-2"></a><span class="ot">get ::</span> <span class="dt">MonadState</span> s m <span class="ot">=></span> m s</span>
|
||||
<span id="cb20-3"><a href="#cb20-3"></a><span class="ot">put ::</span> <span class="dt">MonadState</span> s m <span class="ot">=></span> s <span class="ot">-></span> m ()</span></code></pre></div>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<h2 id="monadstate">MonadState</h2>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<!--
|
||||
```{ .haskell .fragment }
|
||||
class MonadState s m | m -> s where
|
||||
get :: m s
|
||||
put :: s -> m ()
|
||||
```
|
||||
|
||||
[`m -> s` означает что для каждого `m` может быть строго один `s`.]{ .fragment }
|
||||
|
||||
[(Это нужно чтобы когда вы делаете `get` было понятно что вам возвращать.)]{ .fragment } -->
|
||||
<div class="sourceCode" id="cb21"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb21-1"><a href="#cb21-1"></a><span class="kw">class</span> <span class="dt">Monad</span> m <span class="ot">=></span> <span class="dt">MonadState</span> s m <span class="kw">where</span></span>
|
||||
<span id="cb21-2"><a href="#cb21-2"></a><span class="ot"> get ::</span> m s</span>
|
||||
<span id="cb21-3"><a href="#cb21-3"></a><span class="ot"> put ::</span> s <span class="ot">-></span> m ()</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb22"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb22-1"><a href="#cb22-1"></a><span class="kw">data</span> <span class="dt">State</span> s a <span class="ot">=</span> <span class="dt">State</span> {<span class="ot"> runState ::</span> s <span class="ot">-></span> (a, s) }</span>
|
||||
<span id="cb22-2"><a href="#cb22-2"></a></span>
|
||||
<span id="cb22-3"><a href="#cb22-3"></a><span class="kw">instance</span> <span class="dt">MonadState</span> s (<span class="dt">State</span> s) <span class="kw">where</span></span>
|
||||
<span id="cb22-4"><a href="#cb22-4"></a> get <span class="ot">=</span> <span class="dt">State</span> (\s <span class="ot">-></span> (s, s))</span>
|
||||
<span id="cb22-5"><a href="#cb22-5"></a> put s <span class="ot">=</span> <span class="dt">State</span> (\_ <span class="ot">-></span> ((), s))</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb23"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb23-1"><a href="#cb23-1"></a><span class="ot">getNewId ::</span> <span class="dt">MonadState</span> <span class="dt">TransactionId</span> m <span class="ot">=></span> m <span class="dt">TransactionId</span></span>
|
||||
<span id="cb23-2"><a href="#cb23-2"></a>getNewId <span class="ot">=</span> <span class="kw">do</span></span>
|
||||
<span id="cb23-3"><a href="#cb23-3"></a> newId <span class="ot"><-</span> get</span>
|
||||
<span id="cb23-4"><a href="#cb23-4"></a> put (<span class="fu">succ</span> newId)</span>
|
||||
<span id="cb23-5"><a href="#cb23-5"></a> <span class="fu">return</span> newId</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb24"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb24-1"><a href="#cb24-1"></a><span class="ot">registerPayment ::</span> <span class="dt">Price</span> <span class="ot">-></span> <span class="dt">State</span> <span class="dt">TransactionId</span> <span class="dt">Transaction</span></span>
|
||||
<span id="cb24-2"><a href="#cb24-2"></a>registerPayment price <span class="ot">=</span> <span class="kw">do</span></span>
|
||||
<span id="cb24-3"><a href="#cb24-3"></a> newId <span class="ot"><-</span> getNewId</span>
|
||||
<span id="cb24-4"><a href="#cb24-4"></a> <span class="fu">return</span> <span class="dt">Transaction</span></span>
|
||||
<span id="cb24-5"><a href="#cb24-5"></a> { transactionId <span class="ot">=</span> newId</span>
|
||||
<span id="cb24-6"><a href="#cb24-6"></a> transactionAmount <span class="ot">=</span> price</span>
|
||||
<span id="cb24-7"><a href="#cb24-7"></a> }</span></code></pre></div>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<h2 id="monaderror">MonadError</h2>
|
||||
<div class="sourceCode" id="cb25"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb25-1"><a href="#cb25-1"></a><span class="kw">class</span> <span class="dt">Monad</span> m <span class="ot">=></span> <span class="dt">MonadError</span> e m <span class="kw">where</span></span>
|
||||
<span id="cb25-2"><a href="#cb25-2"></a><span class="ot"> throwError ::</span> e <span class="ot">-></span> m a</span>
|
||||
<span id="cb25-3"><a href="#cb25-3"></a><span class="ot"> catchError ::</span> m a <span class="ot">-></span> (e <span class="ot">-></span> m a) <span class="ot">-></span> m a</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb26"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb26-1"><a href="#cb26-1"></a><span class="kw">data</span> <span class="dt">Either</span> e a <span class="ot">=</span> <span class="dt">Left</span> e <span class="op">|</span> <span class="dt">Right</span> a</span>
|
||||
<span id="cb26-2"><a href="#cb26-2"></a></span>
|
||||
<span id="cb26-3"><a href="#cb26-3"></a><span class="kw">instance</span> <span class="dt">MonadError</span> e (<span class="dt">Either</span> e) <span class="kw">where</span></span>
|
||||
<span id="cb26-4"><a href="#cb26-4"></a> throwError e <span class="ot">=</span> <span class="dt">Left</span> e</span>
|
||||
<span id="cb26-5"><a href="#cb26-5"></a></span>
|
||||
<span id="cb26-6"><a href="#cb26-6"></a> catchError (<span class="dt">Left</span> e) f <span class="ot">=</span> f e</span>
|
||||
<span id="cb26-7"><a href="#cb26-7"></a> catchError (<span class="dt">Right</span> a) _ <span class="ot">=</span> <span class="dt">Right</span> a</span></code></pre></div>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<p>Мы хотим:</p>
|
||||
<ol type="1">
|
||||
<li class="fragment"><del>Отделить эффекты от конкретной монады</del></li>
|
||||
<li class="fragment">Получить возможность комбинировать разные эффекты</li>
|
||||
</ol>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<p>Начнем с функторов</p>
|
||||
<div class="sourceCode" id="cb27"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb27-1"><a href="#cb27-1"></a><span class="kw">data</span> <span class="dt">Compose</span> f g a <span class="ot">=</span> <span class="dt">Compose</span> {<span class="ot"> getCompose ::</span> f (g a) }</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb28"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb28-1"><a href="#cb28-1"></a><span class="kw">type</span> <span class="dt">ErrorStateFunctor</span> a <span class="ot">=</span></span>
|
||||
<span id="cb28-2"><a href="#cb28-2"></a> <span class="dt">Compose</span> (<span class="dt">Either</span> <span class="dt">PaymentError</span>) (<span class="dt">State</span> <span class="dt">TransactionId</span>) a</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb29"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb29-1"><a href="#cb29-1"></a><span class="co">-- Compose</span></span>
|
||||
<span id="cb29-2"><a href="#cb29-2"></a><span class="co">-- { getCompose ::</span></span>
|
||||
<span id="cb29-3"><a href="#cb29-3"></a><span class="co">-- Either PaymentError (State TransactionId a)</span></span>
|
||||
<span id="cb29-4"><a href="#cb29-4"></a><span class="co">-- }</span></span></code></pre></div>
|
||||
<div class="sourceCode" id="cb30"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb30-1"><a href="#cb30-1"></a><span class="kw">instance</span> (<span class="dt">Functor</span> f, <span class="dt">Functor</span> g) <span class="ot">=></span><span class="dt">Functor</span> (<span class="dt">Compose</span> f g) <span class="kw">where</span></span>
|
||||
<span id="cb30-2"><a href="#cb30-2"></a> <span class="fu">fmap</span> f (<span class="dt">Compose</span> x) <span class="ot">=</span> <span class="dt">Compose</span> (<span class="fu">fmap</span> (<span class="fu">fmap</span> f) x)</span></code></pre></div>
|
||||
<p><span class="fragment">🎉</span></p>
|
||||
<p><span class="fragment">(<code>Applicative</code> тоже можно – можете сами попробовать)</span></p>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<p>К монадам!</p>
|
||||
<div class="sourceCode" id="cb31"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb31-1"><a href="#cb31-1"></a><span class="kw">instance</span> (<span class="dt">Monad</span> f, <span class="dt">Monad</span> g) <span class="ot">=></span> <span class="dt">Monad</span> (<span class="dt">Compose</span> f g) <span class="kw">where</span></span>
|
||||
<span id="cb31-2"><a href="#cb31-2"></a> <span class="fu">return</span> x <span class="ot">=</span> <span class="dt">Compose</span> (<span class="fu">return</span> (<span class="fu">return</span> x))</span>
|
||||
<span id="cb31-3"><a href="#cb31-3"></a> (<span class="dt">Compose</span> x) <span class="op">>>=</span> f <span class="ot">=</span> <span class="op">???</span></span></code></pre></div>
|
||||
<p><span class="fragment">😢</span></p>
|
||||
<p><span class="fragment">Все равно хочется!</span></p>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<div class="sourceCode" id="cb32"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb32-1"><a href="#cb32-1"></a><span class="kw">data</span> <span class="dt">Compose</span> f g a <span class="ot">=</span> <span class="dt">Compose</span> {<span class="ot"> getCompose ::</span> f (g a) }</span></code></pre></div>
|
||||
<p><span class="fragment">Для такого не можем</span></p>
|
||||
<div class="sourceCode" id="cb33"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb33-1"><a href="#cb33-1"></a><span class="kw">data</span> <span class="dt">ErrorState</span> e s a <span class="ot">=</span></span>
|
||||
<span id="cb33-2"><a href="#cb33-2"></a> <span class="dt">ErrorState</span> {<span class="ot"> runErrorState ::</span> <span class="dt">State</span> s (<span class="dt">Either</span> e a) }</span></code></pre></div>
|
||||
<p><span class="fragment">А для такого можем!</span></p>
|
||||
<div class="sourceCode" id="cb34"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb34-1"><a href="#cb34-1"></a>(<span class="dt">State</span> s (<span class="dt">Either</span> e a)) <span class="op">~</span> (s <span class="ot">-></span> <span class="dt">Either</span> e (a, s))</span></code></pre></div>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<div class="sourceCode" id="cb35"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb35-1"><a href="#cb35-1"></a><span class="kw">data</span> <span class="dt">ErrorState</span> e s a <span class="ot">=</span></span>
|
||||
<span id="cb35-2"><a href="#cb35-2"></a> <span class="dt">ErrorState</span> {<span class="ot"> runErrorState ::</span> <span class="dt">State</span> s (<span class="dt">Either</span> e a) }</span></code></pre></div>
|
||||
<p><span class="fragment">Хочется чтобы можно было не только <code>Either</code>!</span></p>
|
||||
<div class="sourceCode" id="cb36"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb36-1"><a href="#cb36-1"></a><span class="kw">data</span> <span class="dt">State</span> s a <span class="ot">=</span> <span class="dt">State</span> {<span class="ot"> runState ::</span> s <span class="ot">-></span> (a, s) }</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb37"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb37-1"><a href="#cb37-1"></a><span class="kw">data</span> <span class="dt">StateT</span> s m a <span class="ot">=</span> <span class="dt">StateT</span> {<span class="ot"> runStateT ::</span> s <span class="ot">-></span> m (a, s) }</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb38"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb38-1"><a href="#cb38-1"></a><span class="kw">instance</span> (<span class="dt">Monad</span> m) <span class="ot">=></span> <span class="dt">Monad</span> (<span class="dt">StateT</span> s m) <span class="kw">where</span></span>
|
||||
<span id="cb38-2"><a href="#cb38-2"></a> <span class="fu">return</span> a <span class="ot">=</span> <span class="dt">StateT</span> <span class="op">$</span> \ s <span class="ot">-></span> <span class="fu">return</span> (a, s)</span>
|
||||
<span id="cb38-3"><a href="#cb38-3"></a></span>
|
||||
<span id="cb38-4"><a href="#cb38-4"></a> m <span class="op">>>=</span> f <span class="ot">=</span> <span class="dt">StateT</span> (\s <span class="ot">-></span> <span class="kw">do</span></span>
|
||||
<span id="cb38-5"><a href="#cb38-5"></a> (a, s') <span class="ot"><-</span> runStateT m s</span>
|
||||
<span id="cb38-6"><a href="#cb38-6"></a> runStateT (f a) s'</span>
|
||||
<span id="cb38-7"><a href="#cb38-7"></a> )</span></code></pre></div>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<h2 id="transformers">Transformers!</h2>
|
||||
<p><img data-src="images/transformer.gif" height="400" /></p>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<h2 id="вернем-операции">Вернем операции!</h2>
|
||||
<div class="sourceCode" id="cb39"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb39-1"><a href="#cb39-1"></a><span class="kw">class</span> <span class="dt">Monad</span> m <span class="ot">=></span> <span class="dt">MonadState</span> s m <span class="kw">where</span></span>
|
||||
<span id="cb39-2"><a href="#cb39-2"></a><span class="ot"> get ::</span> m s</span>
|
||||
<span id="cb39-3"><a href="#cb39-3"></a><span class="ot"> put ::</span> s <span class="ot">-></span> m ()</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb40"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb40-1"><a href="#cb40-1"></a><span class="kw">data</span> <span class="dt">StateT</span> s n a <span class="ot">=</span> <span class="dt">StateT</span> {<span class="ot"> runStateT ::</span> s <span class="ot">-></span> n (a, s) }</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb41"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb41-1"><a href="#cb41-1"></a><span class="kw">instance</span> <span class="dt">Monad</span> n <span class="ot">=></span> <span class="dt">MonadState</span> s (<span class="dt">StateT</span> s n) <span class="kw">where</span></span>
|
||||
<span id="cb41-2"><a href="#cb41-2"></a> get <span class="ot">=</span> <span class="dt">StateT</span> (\s <span class="ot">-></span> <span class="fu">return</span> (s, s))</span>
|
||||
<span id="cb41-3"><a href="#cb41-3"></a> put s <span class="ot">=</span> <span class="dt">StateT</span> (\_ <span class="ot">-></span> <span class="fu">return</span> ((), s)</span></code></pre></div>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<h2 id="error">Error</h2>
|
||||
<div class="sourceCode" id="cb42"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb42-1"><a href="#cb42-1"></a><span class="kw">data</span> <span class="dt">ExceptT</span> e n a <span class="ot">=</span> <span class="dt">ExceptT</span> {<span class="ot"> runExceptT ::</span> n (<span class="dt">Either</span> e a) }</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb43"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb43-1"><a href="#cb43-1"></a><span class="kw">class</span> <span class="dt">Monad</span> m <span class="ot">=></span> <span class="dt">MonadError</span> e m <span class="kw">where</span></span>
|
||||
<span id="cb43-2"><a href="#cb43-2"></a><span class="ot"> throwError ::</span> e <span class="ot">-></span> m a</span>
|
||||
<span id="cb43-3"><a href="#cb43-3"></a><span class="ot"> catchError ::</span> m a <span class="ot">-></span> (e <span class="ot">-></span> m a) <span class="ot">-></span> m a</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb44"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb44-1"><a href="#cb44-1"></a><span class="kw">instance</span> <span class="dt">Monad</span> n <span class="ot">=></span> <span class="dt">MonadError</span> e (<span class="dt">ExceptT</span> e n) <span class="kw">where</span></span>
|
||||
<span id="cb44-2"><a href="#cb44-2"></a> throwError e <span class="ot">=</span> <span class="dt">ExceptT</span> (<span class="fu">return</span> (<span class="dt">Left</span> e))</span>
|
||||
<span id="cb44-3"><a href="#cb44-3"></a></span>
|
||||
<span id="cb44-4"><a href="#cb44-4"></a> catchError (<span class="dt">ExceptT</span> n) f <span class="ot">=</span> <span class="dt">ExceptT</span> <span class="op">$</span> <span class="kw">do</span></span>
|
||||
<span id="cb44-5"><a href="#cb44-5"></a> x <span class="ot"><-</span> n</span>
|
||||
<span id="cb44-6"><a href="#cb44-6"></a> <span class="kw">case</span> x <span class="kw">of</span></span>
|
||||
<span id="cb44-7"><a href="#cb44-7"></a> <span class="dt">Left</span> e <span class="ot">-></span> runExceptT (f e)</span>
|
||||
<span id="cb44-8"><a href="#cb44-8"></a> <span class="dt">Right</span> a <span class="ot">-></span> <span class="fu">return</span> (<span class="dt">Right</span> a)</span></code></pre></div>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<div class="sourceCode" id="cb45"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb45-1"><a href="#cb45-1"></a><span class="kw">class</span> <span class="dt">Monad</span> m <span class="ot">=></span> <span class="dt">MonadError</span> e m <span class="kw">where</span></span>
|
||||
<span id="cb45-2"><a href="#cb45-2"></a><span class="ot"> throwError ::</span> e <span class="ot">-></span> m a</span>
|
||||
<span id="cb45-3"><a href="#cb45-3"></a><span class="ot"> catchError ::</span> m a <span class="ot">-></span> (e <span class="ot">-></span> m a) <span class="ot">-></span> m a</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb46"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb46-1"><a href="#cb46-1"></a><span class="kw">data</span> <span class="dt">PaymentError</span> <span class="ot">=</span> <span class="dt">InsufficientBalance</span></span>
|
||||
<span id="cb46-2"><a href="#cb46-2"></a></span>
|
||||
<span id="cb46-3"><a href="#cb46-3"></a><span class="ot">pay ::</span> <span class="dt">MonadError</span> e m <span class="ot">=></span> <span class="dt">Balance</span> <span class="ot">-></span> <span class="dt">Price</span> <span class="ot">-></span> m <span class="dt">Balance</span></span>
|
||||
<span id="cb46-4"><a href="#cb46-4"></a>pay balance price <span class="ot">=</span> <span class="kw">do</span></span>
|
||||
<span id="cb46-5"><a href="#cb46-5"></a> <span class="kw">let</span> newBalance <span class="ot">=</span> balance <span class="op">-</span> price</span>
|
||||
<span id="cb46-6"><a href="#cb46-6"></a> when (newBalance <span class="op"><</span> <span class="dv">0</span>) (throwError <span class="dt">InsufficientBalance</span>)</span>
|
||||
<span id="cb46-7"><a href="#cb46-7"></a> <span class="fu">return</span> newBalance</span></code></pre></div>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<h2 id="the-states">The states</h2>
|
||||
<div class="sourceCode" id="cb47"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb47-1"><a href="#cb47-1"></a><span class="kw">data</span> <span class="dt">State</span> s a <span class="ot">=</span> <span class="dt">State</span> {<span class="ot"> runState ::</span> s <span class="ot">-></span> (a, s) }</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb48"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb48-1"><a href="#cb48-1"></a><span class="kw">data</span> <span class="dt">StateT</span> s m a <span class="ot">=</span> <span class="dt">StateT</span> {<span class="ot"> runStateT ::</span> s <span class="ot">-></span> m (a, s) }</span></code></pre></div>
|
||||
<p><span class="fragment">🤔</span></p>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<p>Напоминалочка:</p>
|
||||
<div class="sourceCode" id="cb49"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb49-1"><a href="#cb49-1"></a><span class="kw">data</span> <span class="dt">Identity</span> a <span class="ot">=</span> <span class="dt">Identity</span> {<span class="ot"> runIdentity ::</span> a }</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb50"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb50-1"><a href="#cb50-1"></a>(<span class="dt">Identity</span> a) <span class="op">~</span> a</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb51"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb51-1"><a href="#cb51-1"></a><span class="kw">data</span> <span class="dt">State</span> s a <span class="ot">=</span> <span class="dt">State</span> {<span class="ot"> runState ::</span> s <span class="ot">-></span> (a, s) }</span>
|
||||
<span id="cb51-2"><a href="#cb51-2"></a></span>
|
||||
<span id="cb51-3"><a href="#cb51-3"></a><span class="kw">data</span> <span class="dt">StateT</span> s m a <span class="ot">=</span> <span class="dt">StateT</span> {<span class="ot"> runStateT ::</span> s <span class="ot">-></span> m (a, s) }</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb52"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb52-1"><a href="#cb52-1"></a>(<span class="dt">StateT</span> s <span class="dt">Identity</span> a) <span class="op">~</span> (s <span class="ot">-></span> <span class="dt">Identity</span> (a, s))</span></code></pre></div>
|
||||
<p><span class="fragment">🤔</span></p>
|
||||
<div class="sourceCode" id="cb53"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb53-1"><a href="#cb53-1"></a>(s <span class="ot">-></span> <span class="dt">Identity</span> (a, s)) <span class="op">~</span> (s <span class="ot">-></span> (a, s)) <span class="op">~</span> (<span class="dt">State</span> s a)</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb54"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb54-1"><a href="#cb54-1"></a><span class="kw">type</span> <span class="dt">State</span> s a <span class="ot">=</span> <span class="dt">StateT</span> s <span class="dt">Identity</span> a</span></code></pre></div>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<div class="sourceCode" id="cb55"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb55-1"><a href="#cb55-1"></a><span class="kw">data</span> <span class="dt">ExceptT</span> e n a <span class="ot">=</span> <span class="dt">ExceptT</span> {<span class="ot"> runExceptT ::</span> n (<span class="dt">Either</span> e a) }</span>
|
||||
<span id="cb55-2"><a href="#cb55-2"></a></span>
|
||||
<span id="cb55-3"><a href="#cb55-3"></a><span class="kw">data</span> <span class="dt">StateT</span> s m a <span class="ot">=</span> <span class="dt">StateT</span> {<span class="ot"> runStateT ::</span> s <span class="ot">-></span> m (a, s) }</span>
|
||||
<span id="cb55-4"><a href="#cb55-4"></a></span>
|
||||
<span id="cb55-5"><a href="#cb55-5"></a><span class="kw">class</span> <span class="dt">Monad</span> m <span class="ot">=></span> <span class="dt">MonadError</span> e m <span class="kw">where</span></span>
|
||||
<span id="cb55-6"><a href="#cb55-6"></a><span class="ot"> throwError ::</span> e <span class="ot">-></span> m a</span>
|
||||
<span id="cb55-7"><a href="#cb55-7"></a><span class="ot"> catchError ::</span> m a <span class="ot">-></span> (e <span class="ot">-></span> m a) <span class="ot">-></span> m a</span>
|
||||
<span id="cb55-8"><a href="#cb55-8"></a></span>
|
||||
<span id="cb55-9"><a href="#cb55-9"></a><span class="kw">class</span> <span class="dt">Monad</span> m <span class="ot">=></span> <span class="dt">MonadState</span> s m <span class="kw">where</span></span>
|
||||
<span id="cb55-10"><a href="#cb55-10"></a><span class="ot"> get ::</span> m s</span>
|
||||
<span id="cb55-11"><a href="#cb55-11"></a><span class="ot"> put ::</span> s <span class="ot">-></span> m ()</span></code></pre></div>
|
||||
<p>Пришли к тому, с чего начинали, только еще сложнее</p>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<ol type="1">
|
||||
<li class="fragment"><del>Отделить эффекты от конкретной монады</del></li>
|
||||
<li class="fragment">Получить возможность комбинировать разные эффекты</li>
|
||||
</ol>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<div class="sourceCode" id="cb56"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb56-1"><a href="#cb56-1"></a><span class="ot">foo ::</span> <span class="dt">ExceptT</span> <span class="dt">Bool</span> (<span class="dt">State</span> <span class="dt">Char</span>) <span class="dt">Int</span></span>
|
||||
<span id="cb56-2"><a href="#cb56-2"></a>foo <span class="ot">=</span> <span class="kw">do</span></span>
|
||||
<span id="cb56-3"><a href="#cb56-3"></a> c <span class="ot"><-</span> get <span class="co">-- :: State Char Char</span></span>
|
||||
<span id="cb56-4"><a href="#cb56-4"></a> throwError <span class="dt">True</span> <span class="co">-- :: ExceptT Bool (State Char) Int</span></span></code></pre></div>
|
||||
<div class="sourceCode" id="cb57"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb57-1"><a href="#cb57-1"></a><span class="ot">(>=>) ::</span> <span class="dt">Monad</span> m <span class="ot">=></span> (a <span class="ot">-></span> m b) <span class="ot">-></span> (b <span class="ot">-></span> m c) <span class="ot">-></span> (a <span class="ot">-></span> m c)</span>
|
||||
<span id="cb57-2"><a href="#cb57-2"></a></span>
|
||||
<span id="cb57-3"><a href="#cb57-3"></a><span class="co">-- Такого нет</span></span>
|
||||
<span id="cb57-4"><a href="#cb57-4"></a>(<span class="op">>=></span>)</span>
|
||||
<span id="cb57-5"><a href="#cb57-5"></a><span class="ot"> ::</span> (<span class="dt">Monad</span> m, <span class="dt">Monad</span> n)</span>
|
||||
<span id="cb57-6"><a href="#cb57-6"></a> <span class="ot">=></span> (a <span class="ot">-></span> m b) <span class="ot">-></span> (b <span class="ot">-></span> n c) <span class="ot">-></span> (a <span class="ot">-></span> <span class="op">?</span> c)</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb58"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb58-1"><a href="#cb58-1"></a><span class="ot">lift ::</span> <span class="dt">State</span> <span class="dt">Char</span> a <span class="ot">-></span> <span class="dt">ExceptT</span> <span class="dt">Bool</span> (<span class="dt">State</span> <span class="dt">Char</span>) a</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb59"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb59-1"><a href="#cb59-1"></a><span class="ot">foo ::</span> <span class="dt">ExceptT</span> <span class="dt">Bool</span> (<span class="dt">State</span> <span class="dt">Char</span>) <span class="dt">Int</span></span>
|
||||
<span id="cb59-2"><a href="#cb59-2"></a>foo <span class="ot">=</span> <span class="kw">do</span></span>
|
||||
<span id="cb59-3"><a href="#cb59-3"></a> c <span class="ot"><-</span> lift get <span class="co">-- :: ExceptT Bool (State Char) Int</span></span>
|
||||
<span id="cb59-4"><a href="#cb59-4"></a> throwError <span class="dt">True</span> <span class="co">-- :: ExceptT Bool (State Char) Int</span></span></code></pre></div>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<div class="sourceCode" id="cb60"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb60-1"><a href="#cb60-1"></a><span class="kw">class</span> <span class="dt">MonadTrans</span> t <span class="kw">where</span></span>
|
||||
<span id="cb60-2"><a href="#cb60-2"></a><span class="ot"> lift ::</span> (<span class="dt">Monad</span> m) <span class="ot">=></span> m a <span class="ot">-></span> t m a</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb61"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb61-1"><a href="#cb61-1"></a><span class="ot">lift ::</span> <span class="dt">State</span> <span class="dt">Char</span> a <span class="ot">-></span> <span class="dt">ExceptT</span> <span class="dt">Bool</span> (<span class="dt">State</span> <span class="dt">Char</span>) a</span>
|
||||
<span id="cb61-2"><a href="#cb61-2"></a><span class="co">-- (m ) a -> (t ) (m ) a</span></span></code></pre></div>
|
||||
<div class="sourceCode" id="cb62"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb62-1"><a href="#cb62-1"></a><span class="kw">instance</span> <span class="dt">MonadTrans</span> (<span class="dt">StateT</span> s) <span class="kw">where</span></span>
|
||||
<span id="cb62-2"><a href="#cb62-2"></a> lift m <span class="ot">=</span> <span class="dt">StateT</span> <span class="op">$</span> \ s <span class="ot">-></span> <span class="kw">do</span></span>
|
||||
<span id="cb62-3"><a href="#cb62-3"></a> a <span class="ot"><-</span> m</span>
|
||||
<span id="cb62-4"><a href="#cb62-4"></a> <span class="fu">return</span> (a, s)</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb63"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb63-1"><a href="#cb63-1"></a><span class="kw">instance</span> <span class="dt">MonadTrans</span> (<span class="dt">ExceptT</span> e) <span class="kw">where</span></span>
|
||||
<span id="cb63-2"><a href="#cb63-2"></a> lift m <span class="ot">=</span> <span class="dt">ExceptT</span> (<span class="fu">fmap</span> <span class="dt">Right</span> m)</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb64"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb64-1"><a href="#cb64-1"></a><span class="ot">foo ::</span> <span class="dt">ExceptT</span> <span class="dt">Bool</span> (<span class="dt">State</span> <span class="dt">Char</span>) <span class="dt">Int</span></span>
|
||||
<span id="cb64-2"><a href="#cb64-2"></a>foo <span class="ot">=</span> <span class="kw">do</span></span>
|
||||
<span id="cb64-3"><a href="#cb64-3"></a> c <span class="ot"><-</span> lift get</span>
|
||||
<span id="cb64-4"><a href="#cb64-4"></a> throwError <span class="dt">True</span></span></code></pre></div>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<p>Но хочется чтобы без <code>lift</code> 🙂</p>
|
||||
<div class="sourceCode" id="cb65"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb65-1"><a href="#cb65-1"></a><span class="ot">foo ::</span> <span class="dt">ExceptT</span> <span class="dt">Bool</span> (<span class="dt">State</span> <span class="dt">Char</span>) <span class="dt">Int</span></span>
|
||||
<span id="cb65-2"><a href="#cb65-2"></a>foo <span class="ot">=</span> <span class="kw">do</span></span>
|
||||
<span id="cb65-3"><a href="#cb65-3"></a> c <span class="ot"><-</span> get</span>
|
||||
<span id="cb65-4"><a href="#cb65-4"></a> throwError <span class="dt">True</span></span></code></pre></div>
|
||||
<p><span class="fragment">Нужен инстанс <code>MonadState</code> для <code>ExceptT</code></span></p>
|
||||
<div class="sourceCode" id="cb66"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb66-1"><a href="#cb66-1"></a><span class="kw">instance</span> <span class="dt">MonadState</span> s n <span class="ot">=></span> <span class="dt">MonadState</span> s (<span class="dt">ExceptT</span> e n) <span class="kw">where</span></span>
|
||||
<span id="cb66-2"><a href="#cb66-2"></a> get <span class="ot">=</span> lift get</span>
|
||||
<span id="cb66-3"><a href="#cb66-3"></a> put s <span class="ot">=</span> lift (put s)</span></code></pre></div>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<div class="sourceCode" id="cb67"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb67-1"><a href="#cb67-1"></a>registerPayment</span>
|
||||
<span id="cb67-2"><a href="#cb67-2"></a><span class="ot"> ::</span> <span class="dt">State</span> <span class="dt">TransactionId</span> m</span>
|
||||
<span id="cb67-3"><a href="#cb67-3"></a> <span class="ot">=></span> <span class="dt">Price</span> <span class="ot">-></span> m <span class="dt">Transaction</span></span>
|
||||
<span id="cb67-4"><a href="#cb67-4"></a>pay</span>
|
||||
<span id="cb67-5"><a href="#cb67-5"></a><span class="ot"> ::</span> <span class="dt">MonadError</span> <span class="dt">PaymentError</span> m</span>
|
||||
<span id="cb67-6"><a href="#cb67-6"></a> <span class="ot">=></span> <span class="dt">Balance</span> <span class="ot">-></span> <span class="dt">Price</span> <span class="ot">-></span> m <span class="dt">Balance</span></span></code></pre></div>
|
||||
<div class="sourceCode" id="cb68"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb68-1"><a href="#cb68-1"></a>payAndRegister</span>
|
||||
<span id="cb68-2"><a href="#cb68-2"></a><span class="ot"> ::</span> (<span class="dt">State</span> <span class="dt">TransactionId</span> m, <span class="dt">MonadError</span> <span class="dt">PaymentError</span> m)</span>
|
||||
<span id="cb68-3"><a href="#cb68-3"></a> <span class="ot">=></span> <span class="dt">Balance</span> <span class="ot">-></span> <span class="dt">Price</span> <span class="ot">-></span> m (<span class="dt">Balance</span>, <span class="dt">Transaction</span>)</span>
|
||||
<span id="cb68-4"><a href="#cb68-4"></a>payAndRegister balance price <span class="ot">=</span> <span class="kw">do</span></span>
|
||||
<span id="cb68-5"><a href="#cb68-5"></a> newBalance <span class="ot"><-</span> pay balance price</span>
|
||||
<span id="cb68-6"><a href="#cb68-6"></a> transacation <span class="ot"><-</span> registerPayment price</span>
|
||||
<span id="cb68-7"><a href="#cb68-7"></a> <span class="fu">return</span> (newBalance, transaction)</span></code></pre></div>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<p>Как это “запускать”?</p>
|
||||
<div class="sourceCode" id="cb69"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb69-1"><a href="#cb69-1"></a>payAndRegister</span>
|
||||
<span id="cb69-2"><a href="#cb69-2"></a><span class="ot"> ::</span> (<span class="dt">State</span> <span class="dt">TransactionId</span> m, <span class="dt">MonadError</span> <span class="dt">PaymentError</span> m)</span>
|
||||
<span id="cb69-3"><a href="#cb69-3"></a> <span class="ot">=></span> <span class="dt">Balance</span> <span class="ot">-></span> <span class="dt">Price</span> <span class="ot">-></span> m (<span class="dt">Balance</span>, <span class="dt">Transaction</span>)</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb70"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb70-1"><a href="#cb70-1"></a><span class="ot">myBalance ::</span> <span class="dt">Balance</span></span>
|
||||
<span id="cb70-2"><a href="#cb70-2"></a><span class="ot">myPrice ::</span> <span class="dt">Price</span></span>
|
||||
<span id="cb70-3"><a href="#cb70-3"></a></span>
|
||||
<span id="cb70-4"><a href="#cb70-4"></a>balanceAndTransaction</span>
|
||||
<span id="cb70-5"><a href="#cb70-5"></a><span class="ot"> ::</span> <span class="dt">Either</span> <span class="dt">PaymentError</span> (<span class="dt">Balance</span>, <span class="dt">Transaction</span>)</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb71"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb71-1"><a href="#cb71-1"></a><span class="kw">data</span> <span class="dt">ExceptT</span> e n a <span class="ot">=</span> <span class="dt">ExceptT</span> {<span class="ot"> runExceptT ::</span> n (<span class="dt">Either</span> e a) }</span>
|
||||
<span id="cb71-2"><a href="#cb71-2"></a><span class="kw">data</span> <span class="dt">StateT</span> s n a <span class="ot">=</span> <span class="dt">StateT</span> {<span class="ot"> runStateT ::</span> s <span class="ot">-></span> n (a, s) }</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb72"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb72-1"><a href="#cb72-1"></a>balanceAndTransaction <span class="ot">=</span></span>
|
||||
<span id="cb72-2"><a href="#cb72-2"></a> runStateT <span class="dv">0</span> (payAndRegister myBalance myPrice)</span></code></pre></div>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<div class="sourceCode" id="cb73"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb73-1"><a href="#cb73-1"></a>payAndRegister</span>
|
||||
<span id="cb73-2"><a href="#cb73-2"></a><span class="ot"> ::</span> (<span class="dt">State</span> <span class="dt">TransactionId</span> m, <span class="dt">MonadError</span> <span class="dt">PaymentError</span> m)</span>
|
||||
<span id="cb73-3"><a href="#cb73-3"></a> <span class="ot">=></span> <span class="dt">Balance</span> <span class="ot">-></span> <span class="dt">Price</span> <span class="ot">-></span> m (<span class="dt">Balance</span>, <span class="dt">Transaction</span>)</span>
|
||||
<span id="cb73-4"><a href="#cb73-4"></a></span>
|
||||
<span id="cb73-5"><a href="#cb73-5"></a><span class="kw">data</span> <span class="dt">StateT</span> s n a <span class="ot">=</span> <span class="dt">StateT</span> {<span class="ot"> runStateT ::</span> s <span class="ot">-></span> n (a, s) }</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb74"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb74-1"><a href="#cb74-1"></a>balanceAndTransaction</span>
|
||||
<span id="cb74-2"><a href="#cb74-2"></a><span class="ot"> ::</span> <span class="dt">Either</span> <span class="dt">PaymentError</span> (<span class="dt">Balance</span>, <span class="dt">Transaction</span>)</span>
|
||||
<span id="cb74-3"><a href="#cb74-3"></a>balanceAndTransaction <span class="ot">=</span></span>
|
||||
<span id="cb74-4"><a href="#cb74-4"></a> <span class="fu">flip</span> runStateT <span class="dv">0</span> (payAndRegister myBalance myPrice)</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb75"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb75-1"><a href="#cb75-1"></a> <span class="fu">flip</span> runStateT <span class="dv">0</span> (payAndRegister myBalance myPrice)</span>
|
||||
<span id="cb75-2"><a href="#cb75-2"></a><span class="co">-- ^-------------^</span></span>
|
||||
<span id="cb75-3"><a href="#cb75-3"></a><span class="co">-- StateT TransactionId n a -> n a</span></span></code></pre></div>
|
||||
<div class="sourceCode" id="cb76"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb76-1"><a href="#cb76-1"></a><span class="co">-- (n a) ~ (Either PaymentError (Balance, Transaction))</span></span></code></pre></div>
|
||||
<div class="sourceCode" id="cb77"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb77-1"><a href="#cb77-1"></a><span class="fu">flip</span> runStateT <span class="dv">0</span> (payAndRegister myBalance myPrice)</span>
|
||||
<span id="cb77-2"><a href="#cb77-2"></a><span class="co">-- ^--------------------------------^</span></span>
|
||||
<span id="cb77-3"><a href="#cb77-3"></a><span class="co">-- StateT TransactionId</span></span>
|
||||
<span id="cb77-4"><a href="#cb77-4"></a><span class="co">-- (Either PaymentError)</span></span>
|
||||
<span id="cb77-5"><a href="#cb77-5"></a><span class="co">-- (Balance, Transaction)</span></span></code></pre></div>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<div class="sourceCode" id="cb78"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb78-1"><a href="#cb78-1"></a>payAndRegister</span>
|
||||
<span id="cb78-2"><a href="#cb78-2"></a><span class="ot"> ::</span> (<span class="dt">State</span> <span class="dt">TransactionId</span> m, <span class="dt">MonadError</span> <span class="dt">PaymentError</span> m)</span>
|
||||
<span id="cb78-3"><a href="#cb78-3"></a> <span class="ot">=></span> <span class="dt">Balance</span> <span class="ot">-></span> <span class="dt">Price</span> <span class="ot">-></span> m (<span class="dt">Balance</span>, <span class="dt">Transaction</span>)</span>
|
||||
<span id="cb78-4"><a href="#cb78-4"></a></span>
|
||||
<span id="cb78-5"><a href="#cb78-5"></a><span class="kw">data</span> <span class="dt">ExceptT</span> e n a <span class="ot">=</span> <span class="dt">ExceptT</span> {<span class="ot"> runExceptT ::</span> n (<span class="dt">Either</span> e a) }</span>
|
||||
<span id="cb78-6"><a href="#cb78-6"></a><span class="kw">data</span> <span class="dt">StateT</span> s n a <span class="ot">=</span> <span class="dt">StateT</span> {<span class="ot"> runStateT ::</span> s <span class="ot">-></span> n (a, s) }</span>
|
||||
<span id="cb78-7"><a href="#cb78-7"></a><span class="kw">data</span> <span class="dt">Identity</span> a <span class="ot">=</span> <span class="dt">Identity</span> {<span class="ot"> runIdentity ::</span> a }</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb79"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb79-1"><a href="#cb79-1"></a>balanceAndTransaction</span>
|
||||
<span id="cb79-2"><a href="#cb79-2"></a><span class="ot"> ::</span> <span class="dt">Either</span> <span class="dt">PaymentError</span> (<span class="dt">Balance</span>, <span class="dt">Transaction</span>)</span>
|
||||
<span id="cb79-3"><a href="#cb79-3"></a>balanceAndTransaction <span class="ot">=</span></span>
|
||||
<span id="cb79-4"><a href="#cb79-4"></a> (runIdentity <span class="op">.</span> runExceptT <span class="op">.</span> <span class="fu">flip</span> runStateT <span class="dv">0</span>)</span>
|
||||
<span id="cb79-5"><a href="#cb79-5"></a> (payAndRegister myBalance myPrice)</span></code></pre></div>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<p>А теперь мы пойдем в совершенно другом направлении!</p>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<h2 id="напоминалочка-1">Напоминалочка</h2>
|
||||
<div class="sourceCode" id="cb80"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb80-1"><a href="#cb80-1"></a><span class="kw">class</span> <span class="dt">Show</span> a <span class="kw">where</span></span>
|
||||
<span id="cb80-2"><a href="#cb80-2"></a><span class="ot"> show ::</span> a <span class="ot">-></span> <span class="dt">String</span></span></code></pre></div>
|
||||
<div class="sourceCode" id="cb81"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb81-1"><a href="#cb81-1"></a><span class="kw">data</span> <span class="dt">Foo</span> <span class="ot">=</span> <span class="dt">Bar</span> {<span class="ot"> barInt ::</span> <span class="dt">Int</span> }</span>
|
||||
<span id="cb81-2"><a href="#cb81-2"></a> <span class="kw">deriving</span> <span class="dt">Show</span></span></code></pre></div>
|
||||
<div class="sourceCode" id="cb82"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb82-1"><a href="#cb82-1"></a><span class="fu">show</span> (<span class="dt">Bar</span> <span class="dv">8</span>)</span>
|
||||
<span id="cb82-2"><a href="#cb82-2"></a><span class="co">-- Bar {barInt = 8}</span></span></code></pre></div>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<h2 id="read">Read</h2>
|
||||
<p>Обратная операция к <code>show</code></p>
|
||||
<div class="sourceCode" id="cb83"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb83-1"><a href="#cb83-1"></a><span class="fu">read</span><span class="ot"> ::</span> <span class="dt">Read</span> a <span class="ot">=></span> <span class="dt">String</span> <span class="ot">-></span> a</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb84"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb84-1"><a href="#cb84-1"></a><span class="kw">class</span> <span class="dt">Read</span> a <span class="kw">where</span></span>
|
||||
<span id="cb84-2"><a href="#cb84-2"></a> <span class="fu">readsPrec</span></span>
|
||||
<span id="cb84-3"><a href="#cb84-3"></a><span class="ot"> ::</span> <span class="co">-- | Приоритет контекста выражения</span></span>
|
||||
<span id="cb84-4"><a href="#cb84-4"></a> <span class="dt">Int</span></span>
|
||||
<span id="cb84-5"><a href="#cb84-5"></a> <span class="ot">-></span> <span class="dt">String</span></span>
|
||||
<span id="cb84-6"><a href="#cb84-6"></a> <span class="ot">-></span> [(a, <span class="dt">String</span>)]</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb85"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb85-1"><a href="#cb85-1"></a><span class="kw">data</span> <span class="dt">Foo</span> <span class="ot">=</span> <span class="dt">Bar</span> {<span class="ot"> barInt ::</span> <span class="dt">Int</span> }</span>
|
||||
<span id="cb85-2"><a href="#cb85-2"></a> <span class="kw">deriving</span> (<span class="dt">Show</span>, <span class="dt">Read</span>)</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb86"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb86-1"><a href="#cb86-1"></a><span class="fu">read</span> <span class="st">"Bar {barInt = 8}"</span></span>
|
||||
<span id="cb86-2"><a href="#cb86-2"></a><span class="co">-- Bar { barInt = 8 }</span></span></code></pre></div>
|
||||
</section>
|
||||
<section id="монады" class="slide level1">
|
||||
<h1>Монады</h1>
|
||||
<ol type="1">
|
||||
<li class="fragment">Базовые операции монады (эффекты)</li>
|
||||
</ol>
|
||||
<p><span class="fragment"><code>MonadState</code>, <code>MonadErrror</code></span></p>
|
||||
<ol start="2" type="1">
|
||||
<li class="fragment">Конкретные монады (переносчик) (и способы их “разворачивать”)</li>
|
||||
</ol>
|
||||
<p><span class="fragment"><code>StateT</code> (<code>runStateT</code>), <code>Either</code>, <code>ExceptT</code> (<code>runExceptT</code>)</span></p>
|
||||
<p><span class="fragment">Переводят эффекты в pure код</span></p>
|
||||
</section>
|
||||
<section id="монада-io" class="slide level1">
|
||||
<h1>Монада <code>IO</code></h1>
|
||||
<p><span class="fragment">Обладает только базовыми операциями</span> <span class="fragment">(Невозможно перевести в pure код)</span></p>
|
||||
<div class="sourceCode" id="cb87"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb87-1"><a href="#cb87-1"></a><span class="fu">getLine</span><span class="ot"> ::</span> <span class="dt">IO</span> <span class="dt">String</span></span>
|
||||
<span id="cb87-2"><a href="#cb87-2"></a></span>
|
||||
<span id="cb87-3"><a href="#cb87-3"></a><span class="fu">putStrLn</span><span class="ot"> ::</span> <span class="dt">String</span> <span class="ot">-></span> <span class="dt">IO</span> ()</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb88"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb88-1"><a href="#cb88-1"></a><span class="kw">type</span> <span class="dt">FilePath</span> <span class="ot">=</span> <span class="dt">String</span></span>
|
||||
<span id="cb88-2"><a href="#cb88-2"></a></span>
|
||||
<span id="cb88-3"><a href="#cb88-3"></a><span class="fu">readFile</span><span class="ot"> ::</span> <span class="dt">FilePath</span> <span class="ot">-></span> <span class="dt">IO</span> <span class="dt">String</span></span>
|
||||
<span id="cb88-4"><a href="#cb88-4"></a></span>
|
||||
<span id="cb88-5"><a href="#cb88-5"></a><span class="fu">writeFile</span><span class="ot"> ::</span> <span class="dt">FilePath</span> <span class="ot">-></span> <span class="dt">String</span> <span class="ot">-></span> <span class="dt">IO</span> ()</span></code></pre></div>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<div class="sourceCode" id="cb89"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb89-1"><a href="#cb89-1"></a><span class="ot">add10FromConsole ::</span> <span class="dt">IO</span> ()</span>
|
||||
<span id="cb89-2"><a href="#cb89-2"></a>add10FromConsole <span class="ot">=</span> <span class="kw">do</span></span>
|
||||
<span id="cb89-3"><a href="#cb89-3"></a> x <span class="ot"><-</span> <span class="fu">getLine</span></span>
|
||||
<span id="cb89-4"><a href="#cb89-4"></a> <span class="kw">let</span></span>
|
||||
<span id="cb89-5"><a href="#cb89-5"></a><span class="ot"> n ::</span> <span class="dt">Int</span></span>
|
||||
<span id="cb89-6"><a href="#cb89-6"></a> n <span class="ot">=</span> <span class="fu">read</span> x</span>
|
||||
<span id="cb89-7"><a href="#cb89-7"></a> <span class="fu">putStrLn</span> (<span class="fu">show</span> (n <span class="op">+</span> <span class="dv">10</span>))</span></code></pre></div>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<p>“Разворачивать” <code>IO</code> умеет только рантайм.</p>
|
||||
<div class="sourceCode" id="cb90"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb90-1"><a href="#cb90-1"></a><span class="ot">main ::</span> <span class="dt">IO</span> ()</span>
|
||||
<span id="cb90-2"><a href="#cb90-2"></a>main <span class="ot">=</span> add10FromConsole</span></code></pre></div>
|
||||
<pre class="fragment"><code>> 10
|
||||
20</code></pre>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<div class="sourceCode" id="cb92"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb92-1"><a href="#cb92-1"></a><span class="co">-- Как 'read', но не взрывается в рантайме</span></span>
|
||||
<span id="cb92-2"><a href="#cb92-2"></a><span class="ot">readMaybe ::</span> <span class="dt">Read</span> a <span class="ot">=></span> <span class="dt">String</span> <span class="ot">-></span> <span class="dt">Maybe</span> a</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb93"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb93-1"><a href="#cb93-1"></a><span class="ot">accumulateNums ::</span> <span class="dt">StateT</span> <span class="dt">Int</span> <span class="dt">IO</span> ()</span>
|
||||
<span id="cb93-2"><a href="#cb93-2"></a>accumulateNums <span class="ot">=</span> <span class="kw">do</span></span>
|
||||
<span id="cb93-3"><a href="#cb93-3"></a> x <span class="ot"><-</span> lift <span class="fu">getLine</span></span>
|
||||
<span id="cb93-4"><a href="#cb93-4"></a> <span class="kw">case</span> (readMaybe<span class="ot"> x ::</span> <span class="dt">Int</span>) <span class="kw">of</span></span>
|
||||
<span id="cb93-5"><a href="#cb93-5"></a> <span class="dt">Nothing</span> <span class="ot">-></span> <span class="kw">do</span></span>
|
||||
<span id="cb93-6"><a href="#cb93-6"></a> s <span class="ot"><-</span> get</span>
|
||||
<span id="cb93-7"><a href="#cb93-7"></a> lift (<span class="fu">putStrLn</span> (<span class="fu">show</span> s))</span>
|
||||
<span id="cb93-8"><a href="#cb93-8"></a> <span class="dt">Just</span> x' <span class="ot">-></span> <span class="kw">do</span></span>
|
||||
<span id="cb93-9"><a href="#cb93-9"></a> modify (<span class="op">+</span> x')</span>
|
||||
<span id="cb93-10"><a href="#cb93-10"></a> accumulateNums</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb94"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb94-1"><a href="#cb94-1"></a><span class="ot">main ::</span> <span class="dt">IO</span> ()</span>
|
||||
<span id="cb94-2"><a href="#cb94-2"></a>main <span class="ot">=</span> <span class="fu">flip</span> runStateT <span class="dv">0</span> accumulateNums</span></code></pre></div>
|
||||
<pre class="fragment"><code>> 8
|
||||
> 3
|
||||
> a
|
||||
11</code></pre>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<div class="sourceCode" id="cb96"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb96-1"><a href="#cb96-1"></a><span class="ot">accumulateNums ::</span> <span class="dt">StateT</span> <span class="dt">Int</span> <span class="dt">IO</span> ()</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb97"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb97-1"><a href="#cb97-1"></a><span class="co">-- Хочется так, но где тут взять 'IO'?</span></span>
|
||||
<span id="cb97-2"><a href="#cb97-2"></a><span class="ot">accumulateNums ::</span> <span class="dt">MonadState</span> <span class="dt">Int</span> m <span class="ot">=></span> m ()</span></code></pre></div>
|
||||
<p><span class="fragment">Трансформера <code>IO</code> нет.</span></p>
|
||||
<p><span class="fragment">Тогда <code>lift</code>!</span></p>
|
||||
<p><span class="fragment">Но <code>lift</code> поднимает строго на один уровень.</span></p>
|
||||
</section>
|
||||
<section class="slide level1">
|
||||
|
||||
<div class="sourceCode" id="cb98"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb98-1"><a href="#cb98-1"></a><span class="kw">class</span> <span class="dt">MonadIO</span> m <span class="kw">where</span></span>
|
||||
<span id="cb98-2"><a href="#cb98-2"></a><span class="ot"> liftIO ::</span> <span class="dt">IO</span> a <span class="ot">-></span> m a</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb99"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb99-1"><a href="#cb99-1"></a><span class="ot">accumulateNums ::</span> (<span class="dt">MonadState</span> <span class="dt">Int</span> m, <span class="dt">MonadIO</span> m) <span class="ot">=></span> m ()</span>
|
||||
<span id="cb99-2"><a href="#cb99-2"></a>accumulateNums <span class="ot">=</span> <span class="kw">do</span></span>
|
||||
<span id="cb99-3"><a href="#cb99-3"></a> x <span class="ot"><-</span> liftIO <span class="fu">getLine</span></span>
|
||||
<span id="cb99-4"><a href="#cb99-4"></a> <span class="kw">case</span> (readMaybe<span class="ot"> x ::</span> <span class="dt">Int</span>) <span class="kw">of</span></span>
|
||||
<span id="cb99-5"><a href="#cb99-5"></a> <span class="dt">Nothing</span> <span class="ot">-></span> <span class="kw">do</span></span>
|
||||
<span id="cb99-6"><a href="#cb99-6"></a> s <span class="ot"><-</span> get</span>
|
||||
<span id="cb99-7"><a href="#cb99-7"></a> liftIO (<span class="fu">putStrLn</span> (<span class="fu">show</span> s))</span>
|
||||
<span id="cb99-8"><a href="#cb99-8"></a> <span class="dt">Just</span> x' <span class="ot">-></span> <span class="kw">do</span></span>
|
||||
<span id="cb99-9"><a href="#cb99-9"></a> modify (<span class="op">+</span> x')</span>
|
||||
<span id="cb99-10"><a href="#cb99-10"></a> accumulateNums</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb100"><pre class="sourceCode haskell fragment"><code class="sourceCode haskell"><span id="cb100-1"><a href="#cb100-1"></a><span class="ot">main ::</span> <span class="dt">IO</span> ()</span>
|
||||
<span id="cb100-2"><a href="#cb100-2"></a>main <span class="ot">=</span> <span class="fu">flip</span> runStateT <span class="dv">0</span> accumulateNums</span></code></pre></div>
|
||||
<pre class="fragment"><code>> 8
|
||||
> 3
|
||||
> a
|
||||
11</code></pre>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="reveal.js/js/reveal.js"></script>
|
||||
|
||||
<script>
|
||||
|
||||
// Full list of configuration options available at:
|
||||
// https://github.com/hakimel/reveal.js#configuration
|
||||
Reveal.initialize({
|
||||
// Push each slide change to the browser history
|
||||
history: true,
|
||||
|
||||
// Optional reveal.js plugins
|
||||
dependencies: [
|
||||
{ src: 'reveal.js/lib/js/classList.js', condition: function() { return !document.body.classList; } },
|
||||
{ src: 'reveal.js/plugin/zoom-js/zoom.js', async: true },
|
||||
{ src: 'reveal.js/plugin/notes/notes.js', async: true }
|
||||
]
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
857
4.md
Normal file
857
4.md
Normal file
@ -0,0 +1,857 @@
|
||||
---
|
||||
theme: superblack
|
||||
author: Ilya Kostyuchenko
|
||||
---
|
||||
|
||||
# Функциональное программирование
|
||||
|
||||
---
|
||||
|
||||
## Напоминалочка
|
||||
|
||||
```{ .haskell }
|
||||
class Functor f where
|
||||
fmap :: (a -> b) -> f a -> f b
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
class Functor f => Applicative f where
|
||||
(<*>) :: f (a -> b) -> f a -> f b
|
||||
pure :: a -> f a
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
class Applicative f => Monad f where
|
||||
(>>=) :: f a -> (a -> f b) -> f b
|
||||
return :: a -> f a
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Either
|
||||
|
||||
```{ .haskell }
|
||||
data Either a b = Left a | Right b
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
throwError :: e -> Either e a
|
||||
throwError = Left
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
data PaymentError = InsufficientBalance
|
||||
|
||||
pay :: Balance -> Price -> Either PaymentError Balance
|
||||
pay balance price = do
|
||||
let newBalance = balance - price
|
||||
when (newBalance < 0) (throwError InsufficientBalance)
|
||||
return newBalance
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
when :: (Applicative f) => Bool -> f () -> f ()
|
||||
when p s = if p then s else pure ()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# State
|
||||
|
||||
```{ .haskell }
|
||||
data State s a = State { runState :: s -> (a, s) }
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
get :: State s s
|
||||
get = State (\s -> (s, s))
|
||||
|
||||
put :: s -> State s ()
|
||||
put s = State (\_ -> (_, s))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
```{ .haskell }
|
||||
getNewId :: State TransactionId TransactionId
|
||||
getNewId = do
|
||||
newId <- get
|
||||
put (succ newId)
|
||||
return newId
|
||||
|
||||
data Transaction = Transaction
|
||||
{ transactionId :: TransactionId,
|
||||
transactionAmount :: Price
|
||||
}
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
registerPayment :: Price -> State TransactionId Transaction
|
||||
registerPayment price = do
|
||||
newId <- getNewId
|
||||
return Transaction
|
||||
{ transactionId = newId
|
||||
transactionAmount = price
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
```{ .haskell }
|
||||
registerPayment :: Price -> State TransactionId Transaction
|
||||
pay :: Balance -> Price -> Either PaymentError Balance
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
payAndRegister balance price = do
|
||||
newBalance <- pay balance price
|
||||
transacation <- registerPayment price
|
||||
return (newBalance, transaction)
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
-- Такого нет
|
||||
(>=>)
|
||||
:: (Monad m, Monad n)
|
||||
=> (a -> m b) -> (b -> n c) -> (a -> ? c)
|
||||
```
|
||||
|
||||
[На каждый чих нужно создавать новый тип?]{.fragment}
|
||||
|
||||
---
|
||||
|
||||
Мы хотим:
|
||||
|
||||
1. Отделить эффекты от конкретной монады
|
||||
2. Получить возможность комбинировать разные эффекты
|
||||
|
||||
[(И чтобы не надо было на каждый случай новый тип создавать)]{.fragment}
|
||||
|
||||
---
|
||||
|
||||
```{ .haskell }
|
||||
registerPayment :: Price -> State TransactionId Transaction
|
||||
pay :: Balance -> Price -> Either PaymentError Balance
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
registerPayment
|
||||
:: MonadState TransactionId m
|
||||
=> Price -> m Transaction
|
||||
pay
|
||||
:: MonadError PaymentError m
|
||||
=> Balance -> Price -> m Balance
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
payAndRegister
|
||||
:: (MonadState TransactionId m, MonadError PaymentError m)
|
||||
=> Balance -> Price -> m (Balance, Transaction)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
На самом деле мы хотим чтобы понятие "state" не было привязано к конкретной монаде.
|
||||
|
||||
[Мы хотим чтобы "базовые операции" не были привязаны к конкретной монаде.]{.fragment}
|
||||
|
||||
```{ .haskell .fragment }
|
||||
-- Было
|
||||
get :: State s s
|
||||
put :: s -> State s ()
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
-- Стало
|
||||
get :: MonadState s m => m s
|
||||
put :: MonadState s m => s -> m ()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## MonadState
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
```{ .haskell .fragment }
|
||||
class MonadState s m | m -> s where
|
||||
get :: m s
|
||||
put :: s -> m ()
|
||||
```
|
||||
|
||||
[`m -> s` означает что для каждого `m` может быть строго один `s`.]{ .fragment }
|
||||
|
||||
[(Это нужно чтобы когда вы делаете `get` было понятно что вам возвращать.)]{ .fragment } -->
|
||||
```{ .haskell }
|
||||
class Monad m => MonadState s m where
|
||||
get :: m s
|
||||
put :: s -> m ()
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
data State s a = State { runState :: s -> (a, s) }
|
||||
|
||||
instance MonadState s (State s) where
|
||||
get = State (\s -> (s, s))
|
||||
put s = State (\_ -> ((), s))
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
getNewId :: MonadState TransactionId m => m TransactionId
|
||||
getNewId = do
|
||||
newId <- get
|
||||
put (succ newId)
|
||||
return newId
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
registerPayment :: Price -> State TransactionId Transaction
|
||||
registerPayment price = do
|
||||
newId <- getNewId
|
||||
return Transaction
|
||||
{ transactionId = newId
|
||||
transactionAmount = price
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## MonadError
|
||||
|
||||
```{ .haskell .fragment }
|
||||
class Monad m => MonadError e m where
|
||||
throwError :: e -> m a
|
||||
catchError :: m a -> (e -> m a) -> m a
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
data Either e a = Left e | Right a
|
||||
|
||||
instance MonadError e (Either e) where
|
||||
throwError e = Left e
|
||||
|
||||
catchError (Left e) f = f e
|
||||
catchError (Right a) _ = Right a
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Мы хотим:
|
||||
|
||||
1. ~~Отделить эффекты от конкретной монады~~
|
||||
2. Получить возможность комбинировать разные эффекты
|
||||
|
||||
---
|
||||
|
||||
Начнем с функторов
|
||||
|
||||
```{ .haskell .fragment }
|
||||
data Compose f g a = Compose { getCompose :: f (g a) }
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
type ErrorStateFunctor a =
|
||||
Compose (Either PaymentError) (State TransactionId) a
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
-- Compose
|
||||
-- { getCompose ::
|
||||
-- Either PaymentError (State TransactionId a)
|
||||
-- }
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
instance (Functor f, Functor g) =>Functor (Compose f g) where
|
||||
fmap f (Compose x) = Compose (fmap (fmap f) x)
|
||||
```
|
||||
|
||||
[🎉]{ .fragment }
|
||||
|
||||
[(`Applicative` тоже можно -- можете сами попробовать)]{ .fragment }
|
||||
|
||||
---
|
||||
|
||||
К монадам!
|
||||
|
||||
```{ .haskell .fragment }
|
||||
instance (Monad f, Monad g) => Monad (Compose f g) where
|
||||
return x = Compose (return (return x))
|
||||
(Compose x) >>= f = ???
|
||||
```
|
||||
|
||||
[😢]{ .fragment }
|
||||
|
||||
[Все равно хочется!]{ .fragment }
|
||||
|
||||
---
|
||||
|
||||
|
||||
```{ .haskell }
|
||||
data Compose f g a = Compose { getCompose :: f (g a) }
|
||||
```
|
||||
|
||||
[Для такого не можем]{ .fragment }
|
||||
|
||||
|
||||
```{ .haskell .fragment }
|
||||
data ErrorState e s a =
|
||||
ErrorState { runErrorState :: State s (Either e a) }
|
||||
```
|
||||
|
||||
[А для такого можем!]{ .fragment }
|
||||
|
||||
```{ .haskell .fragment }
|
||||
(State s (Either e a)) ~ (s -> Either e (a, s))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
```{ .haskell }
|
||||
data ErrorState e s a =
|
||||
ErrorState { runErrorState :: State s (Either e a) }
|
||||
```
|
||||
|
||||
[Хочется чтобы можно было не только `Either`!]{ .fragment }
|
||||
|
||||
```{ .haskell .fragment }
|
||||
data State s a = State { runState :: s -> (a, s) }
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
data StateT s m a = StateT { runStateT :: s -> m (a, s) }
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
instance (Monad m) => Monad (StateT s m) where
|
||||
return a = StateT $ \ s -> return (a, s)
|
||||
|
||||
m >>= f = StateT (\s -> do
|
||||
(a, s') <- runStateT m s
|
||||
runStateT (f a) s'
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Transformers!
|
||||
|
||||
![](images/transformer.gif){height=400px}
|
||||
|
||||
---
|
||||
|
||||
## Вернем операции!
|
||||
|
||||
```{ .haskell }
|
||||
class Monad m => MonadState s m where
|
||||
get :: m s
|
||||
put :: s -> m ()
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
data StateT s n a = StateT { runStateT :: s -> n (a, s) }
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
instance Monad n => MonadState s (StateT s n) where
|
||||
get = StateT (\s -> return (s, s))
|
||||
put s = StateT (\_ -> return ((), s)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error
|
||||
|
||||
```{ .haskell .fragment }
|
||||
data ExceptT e n a = ExceptT { runExceptT :: n (Either e a) }
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
class Monad m => MonadError e m where
|
||||
throwError :: e -> m a
|
||||
catchError :: m a -> (e -> m a) -> m a
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
instance Monad n => MonadError e (ExceptT e n) where
|
||||
throwError e = ExceptT (return (Left e))
|
||||
|
||||
catchError (ExceptT n) f = ExceptT $ do
|
||||
x <- n
|
||||
case x of
|
||||
Left e -> runExceptT (f e)
|
||||
Right a -> return (Right a)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
```{ .haskell }
|
||||
class Monad m => MonadError e m where
|
||||
throwError :: e -> m a
|
||||
catchError :: m a -> (e -> m a) -> m a
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
data PaymentError = InsufficientBalance
|
||||
|
||||
pay :: MonadError e m => Balance -> Price -> m Balance
|
||||
pay balance price = do
|
||||
let newBalance = balance - price
|
||||
when (newBalance < 0) (throwError InsufficientBalance)
|
||||
return newBalance
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## The states
|
||||
|
||||
|
||||
```{ .haskell .fragment }
|
||||
data State s a = State { runState :: s -> (a, s) }
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
data StateT s m a = StateT { runStateT :: s -> m (a, s) }
|
||||
```
|
||||
|
||||
[🤔]{.fragment}
|
||||
|
||||
---
|
||||
|
||||
Напоминалочка:
|
||||
|
||||
```{ .haskell .fragment }
|
||||
data Identity a = Identity { runIdentity :: a }
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
(Identity a) ~ a
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
data State s a = State { runState :: s -> (a, s) }
|
||||
|
||||
data StateT s m a = StateT { runStateT :: s -> m (a, s) }
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
(StateT s Identity a) ~ (s -> Identity (a, s))
|
||||
```
|
||||
|
||||
[🤔]{.fragment}
|
||||
|
||||
```{ .haskell .fragment }
|
||||
(s -> Identity (a, s)) ~ (s -> (a, s)) ~ (State s a)
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
type State s a = StateT s Identity a
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
```{ .haskell }
|
||||
data ExceptT e n a = ExceptT { runExceptT :: n (Either e a) }
|
||||
|
||||
data StateT s m a = StateT { runStateT :: s -> m (a, s) }
|
||||
|
||||
class Monad m => MonadError e m where
|
||||
throwError :: e -> m a
|
||||
catchError :: m a -> (e -> m a) -> m a
|
||||
|
||||
class Monad m => MonadState s m where
|
||||
get :: m s
|
||||
put :: s -> m ()
|
||||
```
|
||||
|
||||
Пришли к тому, с чего начинали, только еще сложнее
|
||||
|
||||
---
|
||||
|
||||
1. ~~Отделить эффекты от конкретной монады~~
|
||||
2. Получить возможность комбинировать разные эффекты
|
||||
|
||||
---
|
||||
|
||||
```{ .haskell }
|
||||
foo :: ExceptT Bool (State Char) Int
|
||||
foo = do
|
||||
c <- get -- :: State Char Char
|
||||
throwError True -- :: ExceptT Bool (State Char) Int
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)
|
||||
|
||||
-- Такого нет
|
||||
(>=>)
|
||||
:: (Monad m, Monad n)
|
||||
=> (a -> m b) -> (b -> n c) -> (a -> ? c)
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
lift :: State Char a -> ExceptT Bool (State Char) a
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
foo :: ExceptT Bool (State Char) Int
|
||||
foo = do
|
||||
c <- lift get -- :: ExceptT Bool (State Char) Int
|
||||
throwError True -- :: ExceptT Bool (State Char) Int
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
```{ .haskell }
|
||||
class MonadTrans t where
|
||||
lift :: (Monad m) => m a -> t m a
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
lift :: State Char a -> ExceptT Bool (State Char) a
|
||||
-- (m ) a -> (t ) (m ) a
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
instance MonadTrans (StateT s) where
|
||||
lift m = StateT $ \ s -> do
|
||||
a <- m
|
||||
return (a, s)
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
instance MonadTrans (ExceptT e) where
|
||||
lift m = ExceptT (fmap Right m)
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
foo :: ExceptT Bool (State Char) Int
|
||||
foo = do
|
||||
c <- lift get
|
||||
throwError True
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Но хочется чтобы без `lift` 🙂
|
||||
|
||||
```{ .haskell .fragment }
|
||||
foo :: ExceptT Bool (State Char) Int
|
||||
foo = do
|
||||
c <- get
|
||||
throwError True
|
||||
```
|
||||
|
||||
[Нужен инстанс `MonadState` для `ExceptT`]{ .fragment }
|
||||
|
||||
```{ .haskell .fragment }
|
||||
instance MonadState s n => MonadState s (ExceptT e n) where
|
||||
get = lift get
|
||||
put s = lift (put s)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
```{ .haskell }
|
||||
registerPayment
|
||||
:: State TransactionId m
|
||||
=> Price -> m Transaction
|
||||
pay
|
||||
:: MonadError PaymentError m
|
||||
=> Balance -> Price -> m Balance
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
payAndRegister
|
||||
:: (State TransactionId m, MonadError PaymentError m)
|
||||
=> Balance -> Price -> m (Balance, Transaction)
|
||||
payAndRegister balance price = do
|
||||
newBalance <- pay balance price
|
||||
transacation <- registerPayment price
|
||||
return (newBalance, transaction)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Как это "запускать"?
|
||||
|
||||
```{ .haskell }
|
||||
payAndRegister
|
||||
:: (State TransactionId m, MonadError PaymentError m)
|
||||
=> Balance -> Price -> m (Balance, Transaction)
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
myBalance :: Balance
|
||||
myPrice :: Price
|
||||
|
||||
balanceAndTransaction
|
||||
:: Either PaymentError (Balance, Transaction)
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
data ExceptT e n a = ExceptT { runExceptT :: n (Either e a) }
|
||||
data StateT s n a = StateT { runStateT :: s -> n (a, s) }
|
||||
```
|
||||
|
||||
|
||||
```{ .haskell .fragment }
|
||||
balanceAndTransaction =
|
||||
runStateT 0 (payAndRegister myBalance myPrice)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
```{ .haskell }
|
||||
payAndRegister
|
||||
:: (State TransactionId m, MonadError PaymentError m)
|
||||
=> Balance -> Price -> m (Balance, Transaction)
|
||||
|
||||
data StateT s n a = StateT { runStateT :: s -> n (a, s) }
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
balanceAndTransaction
|
||||
:: Either PaymentError (Balance, Transaction)
|
||||
balanceAndTransaction =
|
||||
flip runStateT 0 (payAndRegister myBalance myPrice)
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
flip runStateT 0 (payAndRegister myBalance myPrice)
|
||||
-- ^-------------^
|
||||
-- StateT TransactionId n a -> n a
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
-- (n a) ~ (Either PaymentError (Balance, Transaction))
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
flip runStateT 0 (payAndRegister myBalance myPrice)
|
||||
-- ^--------------------------------^
|
||||
-- StateT TransactionId
|
||||
-- (Either PaymentError)
|
||||
-- (Balance, Transaction)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
```{ .haskell }
|
||||
payAndRegister
|
||||
:: (State TransactionId m, MonadError PaymentError m)
|
||||
=> Balance -> Price -> m (Balance, Transaction)
|
||||
|
||||
data ExceptT e n a = ExceptT { runExceptT :: n (Either e a) }
|
||||
data StateT s n a = StateT { runStateT :: s -> n (a, s) }
|
||||
data Identity a = Identity { runIdentity :: a }
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
balanceAndTransaction
|
||||
:: Either PaymentError (Balance, Transaction)
|
||||
balanceAndTransaction =
|
||||
(runIdentity . runExceptT . flip runStateT 0)
|
||||
(payAndRegister myBalance myPrice)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
А теперь мы пойдем в совершенно другом направлении!
|
||||
|
||||
---
|
||||
|
||||
## Напоминалочка
|
||||
|
||||
```{ .haskell .fragment }
|
||||
class Show a where
|
||||
show :: a -> String
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
data Foo = Bar { barInt :: Int }
|
||||
deriving Show
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
show (Bar 8)
|
||||
-- Bar {barInt = 8}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Read
|
||||
|
||||
Обратная операция к `show`
|
||||
|
||||
```{ .haskell }
|
||||
read :: Read a => String -> a
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
class Read a where
|
||||
readsPrec
|
||||
:: -- | Приоритет контекста выражения
|
||||
Int
|
||||
-> String
|
||||
-> [(a, String)]
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
data Foo = Bar { barInt :: Int }
|
||||
deriving (Show, Read)
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
read "Bar {barInt = 8}"
|
||||
-- Bar { barInt = 8 }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Монады
|
||||
|
||||
1. Базовые операции монады (эффекты)
|
||||
|
||||
[`MonadState`, `MonadErrror`]{ .fragment }
|
||||
|
||||
2. Конкретные монады (переносчик) (и способы их "разворачивать")
|
||||
|
||||
[`StateT` (`runStateT`), `Either`, `ExceptT` (`runExceptT`)]{ .fragment }
|
||||
|
||||
[Переводят эффекты в pure код]{ .fragment }
|
||||
|
||||
---
|
||||
|
||||
# Монада `IO`
|
||||
|
||||
[Обладает только базовыми операциями]{ .fragment }
|
||||
[(Невозможно перевести в pure код)]{ .fragment }
|
||||
|
||||
```{ .haskell .fragment }
|
||||
getLine :: IO String
|
||||
|
||||
putStrLn :: String -> IO ()
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
type FilePath = String
|
||||
|
||||
readFile :: FilePath -> IO String
|
||||
|
||||
writeFile :: FilePath -> String -> IO ()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
```{ .haskell }
|
||||
add10FromConsole :: IO ()
|
||||
add10FromConsole = do
|
||||
x <- getLine
|
||||
let
|
||||
n :: Int
|
||||
n = read x
|
||||
putStrLn (show (n + 10))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
"Разворачивать" `IO` умеет только рантайм.
|
||||
|
||||
```{ .haskell .fragment }
|
||||
main :: IO ()
|
||||
main = add10FromConsole
|
||||
```
|
||||
|
||||
```{ .fragment }
|
||||
> 10
|
||||
20
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
```{ .haskell }
|
||||
-- Как 'read', но не взрывается в рантайме
|
||||
readMaybe :: Read a => String -> Maybe a
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
accumulateNums :: StateT Int IO ()
|
||||
accumulateNums = do
|
||||
x <- lift getLine
|
||||
case (readMaybe x :: Int) of
|
||||
Nothing -> do
|
||||
s <- get
|
||||
lift (putStrLn (show s))
|
||||
Just x' -> do
|
||||
modify (+ x')
|
||||
accumulateNums
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
main :: IO ()
|
||||
main = flip runStateT 0 accumulateNums
|
||||
```
|
||||
|
||||
```{ .fragment }
|
||||
> 8
|
||||
> 3
|
||||
> a
|
||||
11
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
```{ .haskell }
|
||||
accumulateNums :: StateT Int IO ()
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
-- Хочется так, но где тут взять 'IO'?
|
||||
accumulateNums :: MonadState Int m => m ()
|
||||
```
|
||||
|
||||
[Трансформера `IO` нет.]{ .fragment }
|
||||
|
||||
[Тогда `lift`!]{ .fragment }
|
||||
|
||||
[Но `lift` поднимает строго на один уровень.]{ .fragment }
|
||||
|
||||
---
|
||||
|
||||
```{ .haskell }
|
||||
class MonadIO m where
|
||||
liftIO :: IO a -> m a
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
accumulateNums :: (MonadState Int m, MonadIO m) => m ()
|
||||
accumulateNums = do
|
||||
x <- liftIO getLine
|
||||
case (readMaybe x :: Int) of
|
||||
Nothing -> do
|
||||
s <- get
|
||||
liftIO (putStrLn (show s))
|
||||
Just x' -> do
|
||||
modify (+ x')
|
||||
accumulateNums
|
||||
```
|
||||
|
||||
```{ .haskell .fragment }
|
||||
main :: IO ()
|
||||
main = flip runStateT 0 accumulateNums
|
||||
```
|
||||
|
||||
```{ .fragment }
|
||||
> 8
|
||||
> 3
|
||||
> a
|
||||
11
|
||||
```
|
11
README.md
11
README.md
@ -34,3 +34,14 @@
|
||||
- [`mapM`](https://hackage.haskell.org/package/base-4.12.0.0/docs/Control-Monad.html#v:mapM)
|
||||
- [`State`](https://wiki.haskell.org/State_Monad)
|
||||
- [`Applicative`](http://learnyouahaskell.com/functors-applicative-functors-and-monoids#applicative-functors)
|
||||
- Lecture 4 [[md](https://github.com/ikoHSE/sc-lectures/blob/master/4.md)] [[web](https://ikohse.github.io/sc-lectures/4.html)]
|
||||
- [Transformers](https://mmhaskell.com/monads/transformers)
|
||||
- [`MonadState`](https://hackage.haskell.org/package/mtl-2.2.2/docs/Control-Monad-State-Class.html#t:MonadState)
|
||||
- [`MonadError`](https://hackage.haskell.org/package/mtl-2.2.2/docs/Control-Monad-Except.html#t:MonadError)
|
||||
- [`Compose`](https://hackage.haskell.org/package/base-4.14.0.0/docs/Data-Functor-Compose.html#t:Compose)
|
||||
- [`StateT`](https://hackage.haskell.org/package/transformers-0.5.6.2/docs/Control-Monad-Trans-State-Lazy.html#t:StateT)
|
||||
- [`ExceptT`](https://hackage.haskell.org/package/transformers-0.5.6.2/docs/Control-Monad-Trans-Except.html#t:ExceptT)
|
||||
- [`MonadTrans`](https://hackage.haskell.org/package/transformers-0.5.6.2/docs/Control-Monad-Trans-Class.html#t:MonadTrans)
|
||||
- [`Read`](https://hackage.haskell.org/package/base-4.14.0.0/docs/Prelude.html#t:Read)
|
||||
- [`IO`](https://www.haskell.org/tutorial/io.html)
|
||||
- [`MonadIO`](https://hackage.haskell.org/package/base-4.14.0.0/docs/Control-Monad-IO-Class.html#t:MonadIO)
|
||||
|
BIN
images/transformer.gif
Normal file
BIN
images/transformer.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 MiB |
Loading…
Reference in New Issue
Block a user