--- 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 [] 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<_>() [] 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/).