443 lines
22 KiB
HTML
443 lines
22 KiB
HTML
<!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 → 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>
|