Récupérer le code: learnfsharp.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.
Si vous voulez essayer le code ci-dessous, vous pouvez vous rendre sur tryfsharp.org et le coller dans le REPL.
// 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!")
Pour plus de démonstration de F#, rendez-vous sur le site Try F#, ou suivez la série why use F#.
Apprenez en davantage à propose de F# sur fsharp.org.
Vous avez une suggestion ? Peut-être une correction ? Ouvrez un ticket sur GitHub, ou faites vous-même une pull request !
Version originale par Scott Wlaschin, mis à jour par 3 contributeur(s).