summaryrefslogtreecommitdiffhomepage
path: root/fr-fr/racket-fr.html.markdown
blob: 8b2420f89c57620ae4f4f952b2b41ca505ff8053 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
---
language: racket
filename: learnracket-fr.rkt
contributors:
  - ["th3rac25", "https://github.com/voila"]
  - ["Eli Barzilay", "https://github.com/elibarzilay"]
  - ["Gustavo Schmidt", "https://github.com/gustavoschmidt"]
translators:
  - ["Xavier Nayrac", "https://github.com/lkdjiin"]
lang: fr-fr
---

Racket est un langage de programmation généraliste, multi-paradigme,
descendant de Lisp/Scheme.

Les retours et commentaires sont appréciés ! Vous pouvez joindre l'auteur
original ici :
[@th3rac25](http://twitter.com/th3rac25) ou là : th3rac25 [at] [google's email
service]. Vous pouvez joindre le traducteur de ce document ici :
[@lkdjiin](http://twitter.com/lkdjiin).

```racket
#lang racket ; défini le dialecte à utiliser.

;;; Commentaires

;; Une ligne de commentaire commence par un point-virgule.

#| Un bloc de commentaires
   peut tenir sur plusieurs lignes…
    #|
       et on peut les imbriquer !
    |#
|#

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 1. Types de données et opérateurs primitifs
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; Nombres
9999999999999999999999 ; entier
#b111                  ; binaire => 7
#o111                  ; octal => 73
#x111                  ; hexadécimal => 273
3.14                   ; réel
6.02e+23
1/2                    ; rationnel
1+2i                   ; complexe

;; Un appel de fonction s'écrit (f x y z ...)
;; où f est une fonction et x, y, z, ... sont des arguments.
;; Si vous voulez créer une liste littérales, utilisez ' pour
;; empécher l'évaluation de la liste.
'(+ 1 2) ; => (+ 1 2)
;; Et maintenant, un peu d'arithmétique
(+ 1 1)  ; => 2
(- 8 1)  ; => 7
(* 10 2) ; => 20
(expt 2 3) ; => 8
(quotient 5 2) ; => 2
(remainder 5 2) ; => 1
(/ 35 5) ; => 7
(/ 1 3) ; => 1/3
(exact->inexact 1/3) ; => 0.3333333333333333
(+ 1+2i  2-3i) ; => 3-1i

;;; Booléens
#t ; pour vrai
#f ; pour faux -- Toute autre valeur que #f est vraie
(not #t) ; => #f
(and 0 #f (error "doesn't get here")) ; => #f
(or #f 0 (error "doesn't get here"))  ; => 0

;;; Caractères
#\A ; => #\A
#\λ ; => #\λ
#\u03BB ; => #\λ

;;; Une chaîne de caractères est un tableau de caractères de longueur
;;; fixe.
"Hello, world!"
"Benjamin \"Bugsy\" Siegel"   ; Le backslash est le caractère d'échappement
"Foo\tbar\41\x21\u0021\a\r\n" ; Sont inclus les échappements de type C
                              ; et unicode
"λx:(μα.α→α).xx"              ; une chaîne peut inclure de l'unicode

;; On peut ajouter une chaîne à une autre
(string-append "Hello " "world!") ; => "Hello world!"

;; Une chaîne peut être traitée comme une liste de caractères
(string-ref "Apple" 0) ; => #\A

;; format est utilisé pour formatter une chaîne
(format "~a can be ~a" "strings" "formatted")

;; L'affichage est tout simple
(printf "I'm Racket. Nice to meet you!\n")

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 2. Variables
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Vous pouvez créer une variable à l'aide de define
;; Une variable peut contenir n'importe quel caractères, à l'exception
;; de : ()[]{}",'`;#|\
(define some-var 5)
some-var ; => 5

;; Vous pouvez aussi utiliser des caractères unicode
(define ⊆ subset?)
(⊆ (set 3 2) (set 1 2 3)) ; => #t

;; Accéder à une variable non-initialisée provoque une exception
; x ; => x: indéfini ...

;; Déclaration locale : `me` est attaché à "Bob" seulement à l'intérieur
;; de (let ...)
(let ([me "Bob"])
  "Alice"
  me) ; => "Bob"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 3. Structures and Collections
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Structures
(struct dog (name breed age))
(define my-pet
  (dog "lassie" "collie" 5))
my-pet ; => #<dog>
(dog? my-pet) ; => #t
(dog-name my-pet) ; => "lassie"

;;; Paires (non mutable)
;; `cons` construit une paire, `car` et `cdr` extraient respectivement le
;; premier et le second élément.
(cons 1 2) ; => '(1 . 2)
(car (cons 1 2)) ; => 1
(cdr (cons 1 2)) ; => 2

;;; Listes

;; Les listes en Racket sont des structures de données de type *linked-list*,
;; produites avec des paires assemblées avec `cons` et terminée par `null`
;; (ou '()).
(cons 1 (cons 2 (cons 3 null))) ; => '(1 2 3)
;; `list` est un constructeur variadique plus commode à utiliser
(list 1 2 3) ; => '(1 2 3)
;; et un guillemet simple peut aussi être utilisé pour une liste littérale
'(1 2 3) ; => '(1 2 3)

;; On peut toujours utiliser `cons` pour ajouter un élément au début
;; d'une liste
(cons 4 '(1 2 3)) ; => '(4 1 2 3)

;; Utilisez `append` pour ajouter une liste à une autre
(append '(1 2) '(3 4)) ; => '(1 2 3 4)

;; Une liste est un type très basique, il y a donc *beaucoup* de
;; fonctionnalités qui leur sont dédiées, quelques exemples :
(map add1 '(1 2 3))          ; => '(2 3 4)
(map + '(1 2 3) '(10 20 30)) ; => '(11 22 33)
(filter even? '(1 2 3 4))    ; => '(2 4)
(count even? '(1 2 3 4))     ; => 2
(take '(1 2 3 4) 2)          ; => '(1 2)
(drop '(1 2 3 4) 2)          ; => '(3 4)

;;; Vecteurs

;; Un vecteur est un tableau de taille fixe
#(1 2 3) ; => '#(1 2 3)

;; Utilisez `vector-append` pour additionner des vecteurs entre eux
(vector-append #(1 2 3) #(4 5 6)) ; => #(1 2 3 4 5 6)

;;; Sets

;; Créez un set à partir d'une liste
(list->set '(1 2 3 1 2 3 3 2 1 3 2 1)) ; => (set 1 2 3)

;; Ajoutez un membre avec `set-add`
;; (Fonctionnel: renvoit le set étendu, plutôt que de muter le set en entrée)
(set-add (set 1 2 3) 4) ; => (set 1 2 3 4)

;; Retirez un membre avec `set-remove`
(set-remove (set 1 2 3) 1) ; => (set 2 3)

;; Testez l'existence d'un membre avec `set-member?`
(set-member? (set 1 2 3) 1) ; => #t
(set-member? (set 1 2 3) 4) ; => #f

;;; Tables de hashage

;; Créer une table de hashage non-mutable (un exemple mutable plus loin)
(define m (hash 'a 1 'b 2 'c 3))

;; Retrouver une valeur
(hash-ref m 'a) ; => 1

;; Chercher une valeur inexistante provoque une exceptions
; (hash-ref m 'd) => no value found

;; Vous pouvez fournir une valeur par défaut pour les clés manquantes
(hash-ref m 'd 0) ; => 0

;; Utilisez `hash-set` pour étendre une table de hashage non-mutable
;; (Renvoit la table étendu, plutôt que de la muter)
(define m2 (hash-set m 'd 4))
m2 ; => '#hash((b . 2) (a . 1) (d . 4) (c . 3))

;; Rappelez-vous, ces tables de hashage sont non-mutables !
m ; => '#hash((b . 2) (a . 1) (c . 3))  <-- no `d'

;; Utilisez `hash-remove` pour supprimer des clés (également fonctionnel)
(hash-remove m 'a) ; => '#hash((b . 2) (c . 3))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 3. Fonctions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Utilisez `lambda` pour créer des fonctions.
;; Une fonction renvoie toujours la valeur de sa dernière expression.
(lambda () "Hello World") ; => #<procedure>
;; On peut aussi utiliser le caractère unicode `λ'
(λ () "Hello World")     ; => même fonction

;; Utilisez des parenthèses pour appeler toutes les fonctions, ce qui
;; inclus aussi les expressions lambda
((lambda () "Hello World")) ; => "Hello World"
((λ () "Hello World"))      ; => "Hello World"

;; Assignez une fonction à une variable
(define hello-world (lambda () "Hello World"))
(hello-world) ; => "Hello World"

;; Vous pouvez raccourcir ceci en utilisant le sucre syntaxique pour la
;; définition de fonction :
(define (hello-world2) "Hello World")

;; Entre les () après lambda, vous déclarez la liste des arguments de la
;; fonction
(define hello
  (lambda (name)
    (string-append "Hello " name)))
(hello "Steve") ; => "Hello Steve"
;; … ou alors, en utilisant le sucre syntaxique, ce qui suit est équivalent
(define (hello2 name)
  (string-append "Hello " name))

;; Vous pouvez obtenir des fonctions variadique en utilisant `case-lambda`
(define hello3
  (case-lambda
    [() "Hello World"]
    [(name) (string-append "Hello " name)]))
(hello3 "Jake") ; => "Hello Jake"
(hello3) ; => "Hello World"
;; … ou spécifier des arguments optionnels avec une valeur par défaut
(define (hello4 [name "World"])
  (string-append "Hello " name))

;; Les fonctions peuvent rassembler des arguments supplémentaires dans une
;; liste
(define (count-args . args)
  (format "You passed ~a args: ~a" (length args) args))
(count-args 1 2 3) ; => "You passed 3 args: (1 2 3)"
;; … ou bien avec `lambda`, sans sucre syntaxique
(define count-args2
  (lambda args
    (format "You passed ~a args: ~a" (length args) args)))

;; Vous pouvez mixer arguments réguliers et supplémentaires
(define (hello-count name . args)
  (format "Hello ~a, you passed ~a extra args" name (length args)))
(hello-count "Finn" 1 2 3)
; => "Hello Finn, you passed 3 extra args"
;; … sans sucre syntaxique
(define hello-count2
  (lambda (name . args)
    (format "Hello ~a, you passed ~a extra args" name (length args))))

;; Avec des mot-clés cette fois
(define (hello-k #:name [name "World"] #:greeting [g "Hello"] . args)
  (format "~a ~a, ~a extra args" g name (length args)))
(hello-k)                 ; => "Hello World, 0 extra args"
(hello-k 1 2 3)           ; => "Hello World, 3 extra args"
(hello-k #:greeting "Hi") ; => "Hi World, 0 extra args"
(hello-k #:name "Finn" #:greeting "Hey") ; => "Hey Finn, 0 extra args"
(hello-k 1 2 3 #:greeting "Hi" #:name "Finn" 4 5 6)
                                         ; => "Hi Finn, 6 extra args"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 4. Égalité
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Pour les nombres, utilisez `=`
(= 3 3.0) ; => #t
(= 2 1) ; => #f

;; Pour tester l'identité des objets, utilisez `eq?`
(eq? 3 3) ; => #t
(eq? 3 3.0) ; => #f
(eq? (list 3) (list 3)) ; => #f

;; Pour les collections, utilisez `equal?`
(equal? (list 'a 'b) (list 'a 'b)) ; => #t
(equal? (list 'a 'b) (list 'b 'a)) ; => #f

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 5. Structures de contrôle
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; Conditions

(if #t               ; expression pour le test
    "this is true"   ; expression si vrai
    "this is false") ; expression si faux
; => "this is true"

;; Dans les condition, toutes les valeurs non-fausses sont traitées commentaires
;; étant vraies (c'est à dire toutes sauf #f)
(member 'Groucho '(Harpo Groucho Zeppo)) ; => '(Groucho Zeppo)
(if (member 'Groucho '(Harpo Groucho Zeppo))
    'yep
    'nope)
; => 'yep

;; `cond` permet d'enchaîner une série de tests afin d'obtenir un résultat
(cond [(> 2 2) (error "wrong!")]
      [(< 2 2) (error "wrong again!")]
      [else 'ok]) ; => 'ok

;;; Filtrage par motif (*pattern matching*)

(define (fizzbuzz? n)
  (match (list (remainder n 3) (remainder n 5))
    [(list 0 0) 'fizzbuzz]
    [(list 0 _) 'fizz]
    [(list _ 0) 'buzz]
    [_          #f]))

(fizzbuzz? 15) ; => 'fizzbuzz
(fizzbuzz? 37) ; => #f

;;; Les boucles

;; On peut boucler en utilisant la récursion (terminale)
(define (loop i)
  (when (< i 10)
    (printf "i=~a\n" i)
    (loop (add1 i))))
(loop 5) ; => i=5, i=6, ...

;; D'une manière similaire, avec un `let` nommé
(let loop ((i 0))
  (when (< i 10)
    (printf "i=~a\n" i)
    (loop (add1 i)))) ; => i=0, i=1, ...

;; Voir plus loin pour l'ajout d'une nouvelle forme `loop`, mais Racket
;; possède déjà une forme `for` flexible et élaborée pour les itérations
(for ([i 10])
  (printf "i=~a\n" i)) ; => i=0, i=1, ...
(for ([i (in-range 5 10)])
  (printf "i=~a\n" i)) ; => i=5, i=6, ...

;;; Itérer sur autre chose que des nombres
;; `for` permet d'itérer sur plein de type de séquences:
;; listes, vecteurs, chaînes de caractères, sets, tables de hashage, etc

(for ([i (in-list '(l i s t))])
  (displayln i))

(for ([i (in-vector #(v e c t o r))])
  (displayln i))

(for ([i (in-string "string")])
  (displayln i))

(for ([i (in-set (set 'x 'y 'z))])
  (displayln i))

(for ([(k v) (in-hash (hash 'a 1 'b 2 'c 3 ))])
  (printf "key:~a value:~a\n" k v))

;;; Itérations plus complexes

;; Balayage parallèle de plusieurs séquences (on stoppe sur la plus petite)
(for ([i 10] [j '(x y z)]) (printf "~a:~a\n" i j))
; => 0:x 1:y 2:z

;; Boucles imbriquées
(for* ([i 2] [j '(x y z)]) (printf "~a:~a\n" i j))
; => 0:x, 0:y, 0:z, 1:x, 1:y, 1:z

;; Conditions dans les boucles
(for ([i 1000]
      #:when (> i 5)
      #:unless (odd? i)
      #:break (> i 10))
  (printf "i=~a\n" i))
; => i=6, i=8, i=10

;;; Compréhensions de liste
;; Très similaires aux boucles `for` -- renvoient en plus une collection

(for/list ([i '(1 2 3)])
  (add1 i)) ; => '(2 3 4)

(for/list ([i '(1 2 3)] #:when (even? i))
  i) ; => '(2)

(for/list ([i 10] [j '(x y z)])
  (list i j)) ; => '((0 x) (1 y) (2 z))

(for/list ([i 1000] #:when (> i 5) #:unless (odd? i) #:break (> i 10))
  i) ; => '(6 8 10)

(for/hash ([i '(1 2 3)])
  (values i (number->string i)))
; => '#hash((1 . "1") (2 . "2") (3 . "3"))

;; Il y a plein d'autres fonctions natives pour collecter des données à
;; l'aide de boucles
(for/sum ([i 10]) (* i i)) ; => 285
(for/product ([i (in-range 1 11)]) (* i i)) ; => 13168189440000
(for/and ([i 10] [j (in-range 10 20)]) (< i j)) ; => #t
(for/or ([i 10] [j (in-range 0 20 2)]) (= i j)) ; => #t
;; Et pour n'importe quell combinaison arbitraire, utilisez `for/fold`
(for/fold ([sum 0]) ([i '(1 2 3 4)]) (+ sum i)) ; => 10
;; (Ceci peut souvent remplacer des boucles communes de style impératif)

;;; Exceptions

;; Pour capturer une exception, utilisez la forme `with-handlers`
(with-handlers ([exn:fail? (lambda (exn) 999)])
  (+ 1 "2")) ; => 999
(with-handlers ([exn:break? (lambda (exn) "no time")])
  (sleep 3)
  "phew") ; => "phew", but if you break it => "no time"

;; Utilisez `raise` pour soulever une exception, ou encore n'importe quelle
;; autre valeur
(with-handlers ([number?    ; capturer la valeur numérique soulevée
                 identity]) ; la renvoyer en tant que valeur simple
  (+ 1 (raise 2))) ; => 2

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 6. Mutabilité
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Utilisez `set!` pour réassigner une valeur à une variable existante
(define n 5)
(set! n (add1 n))
n ; => 6

;; Utilisez le mécanisme des boites (*box*) pour les valeurs explicitement
;; mutables (similaire aux pointeurs ou références dans d'autres langages)
(define n* (box 5))
(set-box! n* (add1 (unbox n*)))
(unbox n*) ; => 6

;; Beaucoup de types de données en Racket sont non-mutables (paires, listes,
;; etc), certains ont à la fois une version mutable et une version
;; non-mutable (chaînes, vecteurs, tables de hashage, etc)

;; Utilisez `vector` ou `make-vector` pour créer des vecteurs mutables
(define vec (vector 2 2 3 4))
(define wall (make-vector 100 'bottle-of-beer))
;; Utilisez `vector-set!` pour mettre à jour un emplacement
(vector-set! vec 0 1)
(vector-set! wall 99 'down)
vec ; => #(1 2 3 4)

;; Créer une table de hashage mutable vide et la manipuler
(define m3 (make-hash))
(hash-set! m3 'a 1)
(hash-set! m3 'b 2)
(hash-set! m3 'c 3)
(hash-ref m3 'a)   ; => 1
(hash-ref m3 'd 0) ; => 0
(hash-remove! m3 'a)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 7. Modules
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Les modules permettent d'organiser le code en plusieurs fichiers
;; et bibliothèques réutilisables. Ici, nous utiliserons des sous-modules,
;; imbriqués dans le grand module que forme ce texte (qui démarre à la
;; ligne `#lang`).

(module cake racket/base ; défini un module `cake', basé sur racket/base

  (provide print-cake) ; fonction exportée par le module (publique)

  (define (print-cake n)
    (show "   ~a   " n #\.)
    (show " .-~a-. " n #\|)
    (show " | ~a | " n #\space)
    (show "---~a---" n #\-))

  (define (show fmt n ch) ; fonction interne/privée
    (printf fmt (make-string n ch))
    (newline)))

;; Utilisez `require` pour importer les fonctions fournies par un
;; module (provide)
(require 'cake) ; le ' est pour un sous-module local
(print-cake 3)
; (show "~a" 1 #\A) ; => erreur, `show` n'est pas exportée

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 8. Classes et objets
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Créer une classe fish% (% est idiomatique pour les noms de classes)
(define fish%
  (class object%
    (init size) ; argument pour l'initialisation
    (super-new) ; initialisation de la super-classe
    ;; Les champs/membres/variables de classe
    (define current-size size)
    ;; Méthodes publiques
    (define/public (get-size)
      current-size)
    (define/public (grow amt)
      (set! current-size (+ amt current-size)))
    (define/public (eat other-fish)
      (grow (send other-fish get-size)))))

;; Créer une instance de fish%
(define charlie
  (new fish% [size 10]))

;; Utilisez `send` pour appeler une méthode d'un objet
(send charlie get-size) ; => 10
(send charlie grow 6)
(send charlie get-size) ; => 16

;; `fish%` est une simple valeur de «première classe», ce qui va permettre
;; la composition (*mixins*)
(define (add-color c%)
  (class c%
    (init color)
    (super-new)
    (define my-color color)
    (define/public (get-color) my-color)))
(define colored-fish% (add-color fish%))
(define charlie2 (new colored-fish% [size 10] [color 'red]))
(send charlie2 get-color)
;; ou, sans les noms:
(send (new (add-color fish%) [size 10] [color 'red]) get-color)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 9. Macros
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Les macros permettent d'étendre la syntaxe du langage

;; Ajoutons une boucle `loop`
(define-syntax-rule (while condition body ...)
  (let loop ()
    (when condition
      body ...
      (loop))))

(let ([i 0])
  (while (< i  10)
    (displayln i)
    (set! i (add1 i))))

;; Les macros sont hygiéniques, vous ne pouvez pas *clasher* avec les
;; variables existantes !
(define-syntax-rule (swap! x y) ; ! est idiomatique pour la mutation
  (let ([tmp x])
    (set! x y)
    (set! y tmp)))

(define tmp 2)
(define other 3)
(swap! tmp other)
(printf "tmp = ~a; other = ~a\n" tmp other)
;; La variable `tmp` est renommée en `tmp_1`
;; dans le but d'éviter un conflit de nom
;; (let ([tmp_1 tmp])
;;   (set! tmp other)
;;   (set! other tmp_1))

;; Mais il faut quand même faire bien attention avec les macros, par exemple:
(define-syntax-rule (bad-while condition body ...)
  (when condition
    body ...
    (bad-while condition body ...)))
;; cette macro est cassée : ell génère un code infini, si vous l'essayez
;; le compilateur va entrer dans une boucle infinie.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 10. Contrats
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Les contrats imposent des contraintes aux valeurs exportées depuis
;; les modules

(module bank-account racket
  (provide (contract-out
            [deposit (-> positive? any)] ; un dépot est toujours positif
            [balance (-> positive?)]))

  (define amount 0)
  (define (deposit a) (set! amount (+ amount a)))
  (define (balance) amount)
  )

(require 'bank-account)
(deposit 5)

(balance) ; => 5

;; Les clients qui essaient de déposer un montant non-positif sont blamés
;; (deposit -5) ; => deposit: contract violation
;; expected: positive?
;; given: -5
;; more details....
```

## Pour aller plus loin

Vous en voulez plus ? Essayez
[Getting Started with Racket](http://docs.racket-lang.org/getting-started/)