Files
olla/content/talks/afup-tours-programmation-fonctionnelle/index.html
2026-02-18 17:23:24 +01:00

443 lines
22 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>reveal.js</title>
<link rel="stylesheet" href="css/reset.css">
<link rel="stylesheet" href="css/reveal.css">
<link rel="stylesheet" href="css/theme/solarized.css">
<!-- Theme used for syntax highlighting of code -->
<link rel="stylesheet" href="lib/css/zenburn.css">
<!-- 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 ) ? 'css/print/pdf.css' : 'css/print/paper.css';
document.getElementsByTagName( 'head' )[0].appendChild( link );
</script>
<style media="screen">
.fragment.current-visible.visible:not(.current-fragment) {
display: none;
height:0px;
line-height: 0px;
font-size: 0px;
}
</style>
</head>
<body>
<div class="reveal">
<div class="slides">
<section>
<h3>La programmation fonctionnelle</h3>
<h4>pour les développeuses·eurs web</h4>
<small><a href="https://twitter.com/ThibaudDauce">@ThibaudDauce</a> | <a href="https://thibaud.dauce.fr">https://thibaud.dauce.fr</a></small>
</section>
<section>
<img src="images/quantic-telecom.png" alt="Quantic Telecom" />
<h4>Co-fondateur de Quantic Telecom</h4>
<small><a href="https://www.quantic-telecom.net">https://www.quantic-telecom.net</a></small>
<hr>
<img src="images/laravel.png" alt="Apprendre Laravel" />
<h4>Thibaud Dauce sur YouTube</h4>
<small><a href="https://www.apprendre-laravel.fr">https://www.apprendre-laravel.fr</a></small>
</section>
<section>
<h3>La programmation fonctionnelle</h3>
<h4>pour les développeuses·eurs web</h4>
<small><a href="https://twitter.com/ThibaudDauce">@ThibaudDauce</a> | <a href="https://thibaud.dauce.fr">https://thibaud.dauce.fr</a></small>
</section>
<section>
<h3>Il y a 3 ans…</h3>
<img src="images/haskell-book.png" alt="Haskell Book" class="stretch plain">
</section>
<section>
<h3>Haskell c'est quoi ?</h3>
<ul>
<li>pas de conditions</li>
<li>pas de boucles</li>
<li>pas de variables</li>
</ul>
</section>
<section>
<pre><code data-line-numbers data-trim data-noescape class="php">
function getStudents() {
$customers = Customer::all();
$students = [];
foreach($customers as $customer) {
if ($customer->isStudent()) {
$students[] = $customer;
}
}
return $students;
}
</pre></code>
</section>
<section>
<pre><code data-line-numbers data-trim data-noescape class="php">
function getStudents() {
$customers = Customer::all();
return array_filter($customers, function ($customer) {
return $customer->isStudent();
});
}
</pre></code>
</section>
<section>
<pre><code data-line-numbers data-trim data-noescape class="php">
function getStudents() {
return Customer::all()
->filter(function ($customer) {
return $customer->isStudent();
});
}
</pre></code>
</section>
<section>
<pre style="width: 100%"><code data-line-numbers data-trim data-noescape class="php">
function getStudents() {
$customers = Customer::all();
return array_filter(array_filter($customers, function ($customer) {
return $customer->isStudent();
}), function ($student) {
return $student->isValid();
});
}
</pre></code>
</section>
<section>
<pre><code data-line-numbers data-trim data-noescape class="php">
function getStudents() {
return Customer::all()
->filter(function ($customer) {
return $customer->isStudent();
});
->filter(function ($student) {
return $customer->isValid();
});
}
</pre></code>
</section>
<section>
<pre class="stretch"><code data-line-numbers data-trim data-noescape class="php">
function getMonthlyMeanRevenueForStudents() {
$customers = Customer::all();
$totalRevenue = [];
$counts = [];
foreach($customers as $customer) {
if ($customer->isStudent()) {
foreach($customer->invoices() as $invoice) {
$totalRevenue[$invoice->month()] +=
$invoice->total();
$counts[$invoice->month()]++;
}
}
}
$meanRevenue = [];
foreach ($totalRevenue as $month => $total) {
$meanRevenue[$month] = $total / $counts[$month];
}
return $meanRevenue;
}
</pre></code>
</section>
<section>
<pre class="stretch"><code data-trim data-noescape class="php">
function getMonthlyMeanRevenueForStudents() {
<span class="fragment" data-fragment-index="0">return Customer::all()</span><span class="fragment current-visible" data-fragment-index="0">
// [
// 'Étudiant John',
// 'Étudiante Jane',
// 'Entreprise Google',
// ]</span>
<span class="fragment" data-fragment-index="1">->filter(function ($customer) {
return $customer->isStudent();
})</span><span class="fragment current-visible" data-fragment-index="1">
// [
// 'Étudiant John',
// 'Étudiante Jane',
// ]</span>
<span class="fragment" data-fragment-index="2">->map(function ($student) {
return $student->invoices();
})<span class="fragment current-visible" data-fragment-index="2">
// [
// ['2019-03-001', '2019-04-001'],
// ['2019-03-002'],
// ]</span>
<span class="fragment" data-fragment-index="3">->flatten()<span class="fragment current-visible" data-fragment-index="3">
// [ '2019-03-001', '2019-04-001', '2019-03-002']</span>
<span class="fragment" data-fragment-index="4">->groupBy(function ($invoice) {
return $invoice->month();
})<span class="fragment current-visible" data-fragment-index="4">
// [
// '2019-03' => ['2019-03-001', '2019-03-002'],
// '2019-04' => ['2019-04-001'],
// ]</span>
<span class="fragment" data-fragment-index="5">->map(function ($invoices) {</span>
<span class="fragment current-visible" data-fragment-index="5">// = ['2019-03-001', '2019-03-002']</span><span class="fragment current-visible" data-fragment-index="6">// = [80, 100]</span><span class="fragment current-visible" data-fragment-index="7">// = 180</span><span class="fragment current-visible" data-fragment-index="8">// = 90</span>
<span class="fragment" data-fragment-index="6">return $invoices->map(function ($invoice) {
return $invoice->total();
})</span><span class="fragment" data-fragment-index="7">->sum()</span><span class="fragment" data-fragment-index="8"> / $invoices->count();</span>
<span class="fragment" data-fragment-index="5">});</span>
<span class="fragment current-visible" data-fragment-index="9">// [
// '2019-03' => 90,
// '2019-04' => 150,
// ]</span>
}
</pre></code>
</section>
<section>
<pre class="stretch"><code data-line-numbers data-trim data-noescape class="php">
function getMonthlyMeanRevenueForStudents() {
$customers = Customer::all();
$totalRevenue = [];
$counts = [];
foreach($customers as $customer) {
if ($customer->isStudent()) {
foreach($customer->invoices() as $invoice) {
$totalRevenue[$invoice->month()] +=
$invoice->total();
$counts[$invoice->month()]++;
}
}
}
$meanRevenue = [];
foreach ($totalRevenue as $month => $total) {
$meanRevenue[$month] = $total / $counts[$month];
}
return $meanRevenue;
}
</pre></code>
</section>
<section>
<h3>PHP RFC: Arrow Functions 2.0</h3>
<pre style="width: 100%"><code data-line-numbers data-trim data-noescape class="php">
function getMonthlyMeanRevenueForStudents() {
return Customer::all()
->filter(fn($customer) => $customer->isStudent())
->flatMap(fn($student) => $student->invoice())
->groupBy(fn($invoice) => $invoice->month())
->map(function ($invoices) {
$total = $invoices
->map(fn($invoice) => $invoice->total())
->sum();
return $total / $invoices->count();
});
}
</pre></code>
</section>
<section>
<h3>Middlewares</h3>
<h4>Gestion d'une Chaîne de traitement</h4>
</section>
<section>
<h3>Requête HTTP &#8594; Réponse HTTP</h3>
</section>
<section>
<h3>3 problèmes</h3>
<ul>
<li class="fragment">Vérifiez que la personne est connectée</li>
<li class="fragment">Ajouter un header HTTP à toutes les réponses<br>(<code>Content-Language: fr</code>)</li>
<li class="fragment">Enregistrer le temps de traitement d'une requête</li>
</ul>
</section>
<section>
<pre><code data-trim data-noescape class="php">
function myMiddleware(Request $request, $next): Response
{
<span class="fragment">// $next = function (Request $request): Response {
// return new Response(200, 'All good!');
// };</span>
<span class="fragment">$response = $next($request);</span>
<span class="fragment">return $response;</span>
}
</pre></code>
</section>
<section>
<h3>Vérifiez que la personne est connectée</h3>
<pre class="fragment"><code data-trim data-noescape class="php">
function authMiddleware(Request $request, $next): Response
{
<span class="fragment">if (! $request->session()->has('user')) {
return new Response(302, '/login');
}</span>
<span class="fragment">$response = $next($request);</span>
<span class="fragment">return $response;</span>
}
</pre></code>
</section>
<section>
<h3>Ajouter un header HTTP à chaque réponse</h3>
<pre class="fragment" style="width: 100%"><code data-trim data-noescape class="php">
function contentLanguageMiddleware(Request $request, $next): Response
{
<span class="fragment">$response = $next($request);</span>
<span class="fragment">$response->headers()->add('Content-Language', 'fr');</span>
<span class="fragment">return $response;</span>
}
</pre></code>
</section>
<section>
<h3>Enregistrer le temps de traitement d'une requête</h3>
<pre class="fragment" style="width: 100%"><code data-trim data-noescape class="php">
function timeMiddleware(Request $request, $next): Response
{
<span class="fragment">$start = microtime(true);</span>
<span class="fragment">$response = $next($request);</span>
<span class="fragment">$time = microtime(true) - $start;
$text = "Request {$request->url()} took {$time}s \n";
file_put_content('timings.txt', $text, FILE_APPEND);</span>
<span class="fragment">return $response;</span>
}
</pre></code>
</section>
<section>
<h3>Exemples de middlewares avec Laravel</h3>
<pre><code data-trim data-noescape class="php">
$middlewares = [
CheckForMaintenanceMode::class,
EncryptCookies::class,
StartSession::class,
ThrottleRequests::class,
ConvertEmptyStringsToNull::class,
];
</pre></code>
</section>
<section>
<h3>L'ordre des middlewares</h3>
<pre><code data-trim data-noescape class="php">
$middlewares = [
'authMiddleware',
'contentLanguageMiddleware',
'timeMiddleware',
];
</pre></code>
<pre><code data-trim data-noescape class="php">
$middlewares = [
'timeMiddleware',
'authMiddleware',
'contentLanguageMiddleware',
];
</pre></code>
</section>
<section>
<pre><code data-trim data-noescape class="php">
function otherMiddleware(ClasseA $classeA, $next): ClasseB
{
// $next = function (ClasseA $classeA): ClasseB {
// return new ClasseB;
// };
$classeB = $next($classeA);
return $classeB;
}
</pre></code>
</section>
<section>
<h3>Un peu de Front-End</h3>
<h4 class="fragment">Elm</h4>
</section>
<section>
<h3>MVU</h3>
<h4 class="fragment"><strong>M</strong>odel <strong>V</strong>iew <strong>U</strong>pdate</h4>
</section>
<section>
<h3>Model</h3>
<pre><code data-line-numbers data-trim data-noescape class="elm">
type alias Model = String
</pre></code>
<pre class="fragment"><code data-line-numbers data-trim data-noescape class="elm">
init : Model
init = "Thibaud"
</pre></code>
</section>
<section>
<h3>Update</h3>
<pre><code data-line-numbers data-trim data-noescape class="elm">
type Event = NewName String | Reverse
</pre></code>
<pre class="fragment"><code data-line-numbers data-trim data-noescape class="elm">
update : Event -> Model -> Model
update event oldName = case event of
NewName newName -> newName
Reverse -> String.reverse oldName
</pre></code>
</section>
<section>
<h3>View</h3>
<pre class="width: 100%"><code data-line-numbers data-trim data-noescape class="elm">
view : Model -> Html Event
view name =
div []
[ input [ placeholder "New Name", onInput NewName, value name ] []
, button [ onClick Reverse ] [ text "Reverse Name" ]
, div [] [ text name ]
]
</pre></code>
</section>
<section>
<h3><a href="http://elm-lang.org">elm-lang.org</a></h3>
</section>
<section>
<h3>D'autres ressources</h3>
<ul>
<li><a href="https://adamwathan.me/refactoring-to-collections/">Refactoring to Collection</a></li>
<li><a href="http://haskellbook.com/">Haskell Book</a></li>
<li><a href="http://www.purescript.org/">PureScript</a></li>
<li><a href="https://www.youtube.com/user/tdauce/">Ma chaîne YouTube « Thibaud Dauce »</a></li>
</ul>
</section>
<section>
<h3>Merci à tous</h3>
<small><a href="https://twitter.com/ThibaudDauce">@ThibaudDauce</a> | <a href="https://thibaud.dauce.fr">https://thibaud.dauce.fr</a></small>
</section>
</div>
</div>
<script src="js/reveal.js"></script>
<script>
// More info about config & dependencies:
// - https://github.com/hakimel/reveal.js#configuration
// - https://github.com/hakimel/reveal.js#dependencies
Reveal.initialize({
history: true,
dependencies: [
{ src: 'plugin/markdown/marked.js' },
{ src: 'plugin/markdown/markdown.js' },
{ src: 'plugin/notes/notes.js', async: true },
{ src: 'plugin/highlight/highlight.js', async: true }
]
});
</script>
</body>
</html>