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
629
630
|
---
language: F#
lang: fr-fr
contributors:
- ["Scott Wlaschin", "http://fsharpforfunandprofit.com/"]
translators:
- ["Alois de Gouvello", "https://github.com/aloisdg"]
filename: learnfsharp-fr.fs
---
F# est un langage de programmation fonctionnel et orienté objet. Il est gratuit et son code source est ouvert. Il tourne sur Linux, Mac, Windows et plus.
Il possède un puissant système de type qui piège de nombreuses erreurs à la compilation, mais il utilise l'inférence de type ce qui lui permet d'être lu comme un langage dynamique.
La syntaxe de F# est différente des langages héritant de C.
* Les accolades ne sont pas utilisées pour délimiter les blocs de code. À la place, l'indentation est utilisée (à la manière de Python).
* Les espaces sont utilisés pour séparer les paramètres à la place des virgules.
Si vous voulez essayer le code ci-dessous, vous pouvez vous rendre sur [tryfsharp.org](http://www.tryfsharp.org/Create) et le coller dans le [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop).
```fsharp
// Les commentaires d'une seule ligne commencent par un double slash
(* Les commentaires multilignes utilise les paires (* . . . *)
-fin du commentaire multilignes- *)
// ================================================
// Syntaxe de base
// ================================================
// ------ "Variables" (mais pas vraiment) ------
// Le mot clé "let" définit une valeur (immutable)
let myInt = 5
let myFloat = 3.14
let myString = "hello" // Notons qu'aucun type n'est nécessaire
// ------ Listes ------
let twoToFive = [2;3;4;5] // Les crochets créent une liste avec
// des point-virgules pour délimiteurs.
let oneToFive = 1 :: twoToFive // :: crée une liste avec un nouvel élément
// Le résultat est [1;2;3;4;5]
let zeroToFive = [0;1] @ twoToFive // @ concatène deux listes
// IMPORTANT: les virgules ne sont jamais utilisées pour délimiter,
// seulement les point-virgules !
// ------ Fonctions ------
// Le mot clé "let" définit aussi le nom d'une fonction.
let square x = x * x // Notons qu'aucune parenthèse n'est utilisée.
square 3 // Maitenant, exécutons la fonction.
// Encore une fois, aucune parenthèse.
let add x y = x + y // N'utilisez pas add (x,y) ! Cela signifie
// quelque chose de complètement différent.
add 2 3 // À présent, exécutons la fonction.
// Pour définir une fonction sur plusieurs lignes, utilisons l'indentation.
// Les point-virgules ne sont pas nécessaires.
let evens list =
let isEven x = x%2 = 0 // Définit "isEven" comme une fonction imbriquée
List.filter isEven list // List.filter est une fonction de la librairie
// à deux paramètres: un fonction retournant un
// booléen et une liste sur laquelle travailler
evens oneToFive // À présent, exécutons la fonction.
// Vous pouvez utilisez les parenthèses pour clarifier.
// Dans cet exemple, "map" est exécutée en première, avec deux arguments,
// ensuite "sum" est exécutée sur le résultat.
// Sans les parenthèses, "List.map" serait passé en argument à List.sum.
let sumOfSquaresTo100 =
List.sum ( List.map square [1..100] )
// Vous pouvez rediriger la sortie d'une fonction vers une autre avec "|>"
// Rediriger des données est très commun en F#, comme avec les pipes UNIX.
// Voici la même fonction sumOfSquares écrite en utilisant des pipes
let sumOfSquaresTo100piped =
[1..100] |> List.map square |> List.sum // "square" est déclaré avant
// Vous pouvez définir des lambdas (fonctions anonymes) grâce au mot clé "fun"
let sumOfSquaresTo100withFun =
[1..100] |> List.map (fun x -> x*x) |> List.sum
// En F#, il n'y a pas de mot clé "return". Une fonction retourne toujours
// la valeur de la dernière expression utilisée.
// ------ Pattern Matching ------
// Match..with.. est une surcharge de la condition case/switch.
let simplePatternMatch =
let x = "a"
match x with
| "a" -> printfn "x is a"
| "b" -> printfn "x is b"
| _ -> printfn "x is something else" // underscore correspond à tout le reste
// F# n'autorise pas la valeur null par défaut -- vous devez utiliser le type Option
// et ensuite faire correspondre le pattern.
// Some(..) et None sont approximativement analogue à des wrappers de Nullable
let validValue = Some(99)
let invalidValue = None
// Dans cet exemple, match..with trouve une correspondance à "Some" et à "None",
// et affiche la valeur du "Some" en même temps.
let optionPatternMatch input =
match input with
| Some i -> printfn "input is an int=%d" i
| None -> printfn "input is missing"
optionPatternMatch validValue
optionPatternMatch invalidValue
// ------ Affichage ------
// Les fonctions printf/printfn sont similaires aux fonctions
// Console.Write/WriteLine de C#.
printfn "Printing an int %i, a float %f, a bool %b" 1 2.0 true
printfn "A string %s, and something generic %A" "hello" [1;2;3;4]
// Il y a aussi les fonctions printf/sprintfn pour formater des données
// en string. C'est similaire au String.Format de C#.
// ================================================
// Plus sur les fonctions
// ================================================
// F# est un véritable langage fonctionel -- les fonctions sont des
// entités de premier ordre et peuvent êtres combinées facilement
// pour créer des constructions puissantes
// Les modules sont utilisés pour grouper des fonctions ensemble.
// L'indentation est nécessaire pour chaque module imbriqué.
module FunctionExamples =
// définit un simple fonction d'addition
let add x y = x + y
// usage basique d'une fonction
let a = add 1 2
printfn "1+2 = %i" a
// application partielle des paramètres (curryfication ou "currying" en anglais)
// add42 est une nouvelle fonction qui ne prend plus qu'un paramètre
let add42 = add 42
let b = add42 1
printfn "42+1 = %i" b
// composition pour combiner des fonctions
let add1 = add 1
let add2 = add 2
let add3 = add1 >> add2
let c = add3 7
printfn "3+7 = %i" c
// fonctions de premier ordre
[1..10] |> List.map add3 |> printfn "new list is %A"
// listes de fonction et plus
let add6 = [add1; add2; add3] |> List.reduce (>>)
let d = add6 7
printfn "1+2+3+7 = %i" d
// ================================================
// Listes et collections
// ================================================
// Il y a trois types de collection ordonnée :
// * Les listes sont les collections immutables les plus basiques
// * Les tableaux sont mutables et plus efficients
// * Les séquences sont lazy et infinies (e.g. un enumerator)
//
// Des autres collections incluent des maps immutables et des sets
// plus toutes les collections de .NET
module ListExamples =
// les listes utilisent des crochets
let list1 = ["a";"b"]
let list2 = "c" :: list1 // :: pour un ajout au début
let list3 = list1 @ list2 // @ pour la concatenation
// Compréhensions des listes (aka générateurs)
let squares = [for i in 1..10 do yield i*i]
// Générateur de nombre premier
let rec sieve = function
| (p::xs) -> p :: sieve [ for x in xs do if x % p > 0 then yield x ]
| [] -> []
let primes = sieve [2..50]
printfn "%A" primes
// le pattern matching pour les listes
let listMatcher aList =
match aList with
| [] -> printfn "the list is empty"
| [first] -> printfn "the list has one element %A " first
| [first; second] -> printfn "list is %A and %A" first second
| _ -> printfn "the list has more than two elements"
listMatcher [1;2;3;4]
listMatcher [1;2]
listMatcher [1]
listMatcher []
// Récursion en utilisant les listes
let rec sum aList =
match aList with
| [] -> 0
| x::xs -> x + sum xs
sum [1..10]
// -----------------------------------------
// Fonctions de la librairie standard
// -----------------------------------------
// map
let add3 x = x + 3
[1..10] |> List.map add3
// filtre
let even x = x % 2 = 0
[1..10] |> List.filter even
// beaucoup plus -- se référer à la documentation
module ArrayExamples =
// les tableaux utilisent les crochets avec des barres
let array1 = [| "a";"b" |]
let first = array1.[0] // accès à l'index en utilisant un point
// le pattern matching des tableaux est le même que celui des listes
let arrayMatcher aList =
match aList with
| [| |] -> printfn "the array is empty"
| [| first |] -> printfn "the array has one element %A " first
| [| first; second |] -> printfn "array is %A and %A" first second
| _ -> printfn "the array has more than two elements"
arrayMatcher [| 1;2;3;4 |]
// Fonctions de la librairie standard comme celles des listes
[| 1..10 |]
|> Array.map (fun i -> i+3)
|> Array.filter (fun i -> i%2 = 0)
|> Array.iter (printfn "value is %i. ")
module SequenceExamples =
// Les séquences utilisent des accolades
let seq1 = seq { yield "a"; yield "b" }
// Les séquences peuvent utiliser yield et
// peuvent contenir des sous-sequences
let strange = seq {
// "yield" ajoute un élément
yield 1; yield 2;
// "yield!" ajoute une sous-sequence complète
yield! [5..10]
yield! seq {
for i in 1..10 do
if i%2 = 0 then yield i }}
// test
strange |> Seq.toList
// Les séquences peuvent être créées en utilisant "unfold"
// Voici la suite de fibonacci
let fib = Seq.unfold (fun (fst,snd) ->
Some(fst + snd, (snd, fst + snd))) (0,1)
// test
let fib10 = fib |> Seq.take 10 |> Seq.toList
printf "first 10 fibs are %A" fib10
// ================================================
// Types de données
// ================================================
module DataTypeExamples =
// Toutes les données sont immutables par défaut
// Les tuples sont de simple et rapide types anonymes
// -- Utilisons une virgule pour créer un tuple
let twoTuple = 1,2
let threeTuple = "a",2,true
// Pattern match pour déballer
let x,y = twoTuple // assigne x=1 y=2
// ------------------------------------
// Record types ont des champs nommés
// ------------------------------------
// On utilise "type" avec des accolades pour définir un type record
type Person = {First:string; Last:string}
// On utilise "let" avec des accolades pour créer un record (enregistrement)
let person1 = {First="John"; Last="Doe"}
// Pattern match pour déballer
let {First=first} = person1 // assigne first="john"
// ------------------------------------
// Union types (ou variants) ont un set (ensemble) de choix
// Un seul cas peut être valide à la fois.
// ------------------------------------
// On utilise "type" avec bar/pipe pour definir un union type
type Temp =
| DegreesC of float
| DegreesF of float
// On utilise un de ces choix pour en créér un
let temp1 = DegreesF 98.6
let temp2 = DegreesC 37.0
// Pattern match on all cases to unpack(?)
let printTemp = function
| DegreesC t -> printfn "%f degC" t
| DegreesF t -> printfn "%f degF" t
printTemp temp1
printTemp temp2
// ------------------------------------
// Types récursif
// ------------------------------------
// Les types peuvent être combinés récursivement de façon complexe
// sans avoir à créer des sous-classes
type Employee =
| Worker of Person
| Manager of Employee list
let jdoe = {First="John";Last="Doe"}
let worker = Worker jdoe
// ------------------------------------
// Modelling with types(?)
// ------------------------------------
// Les types union sont excellents pour modelling state without using flags(?)
type EmailAddress =
| ValidEmailAddress of string
| InvalidEmailAddress of string
let trySendEmail email =
match email with // utilisations du pattern matching
| ValidEmailAddress address -> () // envoyer
| InvalidEmailAddress address -> () // ne pas envoyer
// Combiner ensemble, les types union et les types record
// offrent une excellente fondation pour le domain driven design.
// Vous pouvez créer des centaines de petit types qui reflèteront fidèlement
// le domain.
type CartItem = { ProductCode: string; Qty: int }
type Payment = Payment of float
type ActiveCartData = { UnpaidItems: CartItem list }
type PaidCartData = { PaidItems: CartItem list; Payment: Payment}
type ShoppingCart =
| EmptyCart // aucune donnée
| ActiveCart of ActiveCartData
| PaidCart of PaidCartData
// ------------------------------------
// Comportement natif des types
// ------------------------------------
// Les types natifs ont un comportement "prêt-à-l'emploi" des plus utiles, sans code à ajouter.
// * Immutabilité
// * Pretty printing au debug
// * Egalité et comparaison
// * Sérialisation
// Le Pretty printing s'utilise avec %A
printfn "twoTuple=%A,\nPerson=%A,\nTemp=%A,\nEmployee=%A"
twoTuple person1 temp1 worker
// L'égalité et la comparaison sont innés
// Voici un exemple avec des cartes.
type Suit = Club | Diamond | Spade | Heart
type Rank = Two | Three | Four | Five | Six | Seven | Eight
| Nine | Ten | Jack | Queen | King | Ace
let hand = [ Club,Ace; Heart,Three; Heart,Ace;
Spade,Jack; Diamond,Two; Diamond,Ace ]
// tri
List.sort hand |> printfn "sorted hand is (low to high) %A"
List.max hand |> printfn "high card is %A"
List.min hand |> printfn "low card is %A"
// ================================================
// Les Active patterns
// ================================================
module ActivePatternExamples =
// F# a un type particulier de pattern matching nommé "active patterns"
// où le pattern peut être parsé ou détecté dynamiquement.
// "banana clips" est la syntaxe pour l'active patterns
// par exemple, on définit un "active" pattern pour correspondre à des types "character"...
let (|Digit|Letter|Whitespace|Other|) ch =
if System.Char.IsDigit(ch) then Digit
else if System.Char.IsLetter(ch) then Letter
else if System.Char.IsWhiteSpace(ch) then Whitespace
else Other
// ... et ensuite on l'utilise pour rendre la logique de parsing plus claire
let printChar ch =
match ch with
| Digit -> printfn "%c is a Digit" ch
| Letter -> printfn "%c is a Letter" ch
| Whitespace -> printfn "%c is a Whitespace" ch
| _ -> printfn "%c is something else" ch
// afficher une liste
['a';'b';'1';' ';'-';'c'] |> List.iter printChar
// -----------------------------------------
// FizzBuzz en utilisant les active patterns
// -----------------------------------------
// Vous pouvez créer un partial matching patterns également
// On utilise just un underscore dans la définition, et on retourne Some si ça correspond.
let (|MultOf3|_|) i = if i % 3 = 0 then Some MultOf3 else None
let (|MultOf5|_|) i = if i % 5 = 0 then Some MultOf5 else None
// la fonction principale
let fizzBuzz i =
match i with
| MultOf3 & MultOf5 -> printf "FizzBuzz, "
| MultOf3 -> printf "Fizz, "
| MultOf5 -> printf "Buzz, "
| _ -> printf "%i, " i
// test
[1..20] |> List.iter fizzBuzz
// ================================================
// Concision
// ================================================
module AlgorithmExamples =
// F# a un haut ratio signal/bruit, permettant au code de se lire
// presque comme un véritable algorithme
// ------ Exemple: definir une fonction sumOfSquares ------
let sumOfSquares n =
[1..n] // 1) Prendre tous les nombres de 1 à n
|> List.map square // 2) Elever chacun d'entre eux au carré
|> List.sum // 3) Effectuer leur somme
// test
sumOfSquares 100 |> printfn "Sum of squares = %A"
// ------ Exemple: definir un fonction de tri ------
let rec sort list =
match list with
// Si la liste est vide
| [] ->
[] // on retourne une liste vide
// si la list n'est pas vide
| firstElem::otherElements -> // on prend le premier élément
let smallerElements = // on extrait les éléments plus petits
otherElements // on prend les restants
|> List.filter (fun e -> e < firstElem)
|> sort // et on les trie
let largerElements = // on extrait les plus grands
otherElements // de ceux qui restent
|> List.filter (fun e -> e >= firstElem)
|> sort // et on les trie
// On combine les 3 morceaux dans une nouvelle liste que l'on retourne
List.concat [smallerElements; [firstElem]; largerElements]
// test
sort [1;5;23;18;9;1;3] |> printfn "Sorted = %A"
// ================================================
// Code Asynchrone
// ================================================
module AsyncExample =
// F# inclus des fonctionnalités pour aider avec le code asynchrone
// sans rencontrer la "pyramid of doom"
//
// L'exemple suivant télécharge une séquence de page web en parallèle.
open System.Net
open System
open System.IO
open Microsoft.FSharp.Control.CommonExtensions
// Récupérer le contenu d'une URL de manière asynchrone
let fetchUrlAsync url =
async { // Le mot clé "async" et les accolades
// créent un objet "asynchrone"
let req = WebRequest.Create(Uri(url))
use! resp = req.AsyncGetResponse()
// use! est un assignement asynchrone
use stream = resp.GetResponseStream()
// "use" déclenche automatiquement close()
// sur les ressources à la fin du scope
use reader = new IO.StreamReader(stream)
let html = reader.ReadToEnd()
printfn "finished downloading %s" url
}
// une liste des sites à rapporter
let sites = ["http://www.bing.com";
"http://www.google.com";
"http://www.microsoft.com";
"http://www.amazon.com";
"http://www.yahoo.com"]
// C'est parti!
sites
|> List.map fetchUrlAsync // créez une liste de tâche asynchrone
|> Async.Parallel // dites aux tâches de tourner en parallèle
|> Async.RunSynchronously // démarrez les!
// ================================================
// .NET compatabilité
// ================================================
module NetCompatibilityExamples =
// F# peut réaliser presque tout ce que C# peut faire, et il s'intègre
// parfaitement avec les librairies .NET ou Mono.
// ------- Travaillez avec les fonctions des librairies existantes -------
let (i1success,i1) = System.Int32.TryParse("123");
if i1success then printfn "parsed as %i" i1 else printfn "parse failed"
// ------- Implémentez des interfaces à la volée! -------
// Créer un nouvel objet qui implémente IDisposable
let makeResource name =
{ new System.IDisposable
with member this.Dispose() = printfn "%s disposed" name }
let useAndDisposeResources =
use r1 = makeResource "first resource"
printfn "using first resource"
for i in [1..3] do
let resourceName = sprintf "\tinner resource %d" i
use temp = makeResource resourceName
printfn "\tdo something with %s" resourceName
use r2 = makeResource "second resource"
printfn "using second resource"
printfn "done."
// ------- Code orienté objet -------
// F# est aussi un véritable language OO.
// Il supporte les classes, l'héritage, les méthodes virtuelles, etc.
// interface avec type générique
type IEnumerator<'a> =
abstract member Current : 'a
abstract MoveNext : unit -> bool
// Classe de base abstraite avec méthodes virtuelles
[<AbstractClass>]
type Shape() =
// propriétés en lecture seule
abstract member Width : int with get
abstract member Height : int with get
// méthode non-virtuelle
member this.BoundingArea = this.Height * this.Width
// méthode virtuelle avec implémentation de la classe de base
abstract member Print : unit -> unit
default this.Print () = printfn "I'm a shape"
// classe concrète qui hérite de sa classe de base et surcharge
type Rectangle(x:int, y:int) =
inherit Shape()
override this.Width = x
override this.Height = y
override this.Print () = printfn "I'm a Rectangle"
// test
let r = Rectangle(2,3)
printfn "The width is %i" r.Width
printfn "The area is %i" r.BoundingArea
r.Print()
// ------- extension de méthode -------
// Juste comme en C#, F# peut étendre des classes existantes avec des extensions de méthode.
type System.String with
member this.StartsWithA = this.StartsWith "A"
// test
let s = "Alice"
printfn "'%s' starts with an 'A' = %A" s s.StartsWithA
// ------- événements -------
type MyButton() =
let clickEvent = new Event<_>()
[<CLIEvent>]
member this.OnClick = clickEvent.Publish
member this.TestEvent(arg) =
clickEvent.Trigger(this, arg)
// test
let myButton = new MyButton()
myButton.OnClick.Add(fun (sender, arg) ->
printfn "Click event with arg=%O" arg)
myButton.TestEvent("Hello World!")
```
## Plus d'information
Pour plus de démonstration de F#, rendez-vous sur le site [Try F#](http://www.tryfsharp.org/Learn), ou suivez la série [why use F#](http://fsharpforfunandprofit.com/why-use-fsharp/).
Apprenez en davantage à propose de F# sur [fsharp.org](http://fsharp.org/).
|