Dot.Blog

C#, XAML, WinUI, WPF, Android, MAUI, IoT, IA, ChatGPT, Prompt Engineering

Faut-il utiliser F# ?

[new:30/07/2014]Déjà en mai 2009 je vous proposais de réfléchir sur l’arrivée de F# dans l’environnement Visual Studio (lire “F# et le paradoxe de la secte”). Plus de 5 longues années se sont écoulées depuis ! Je n’arriverais même pas à le croire si mes billets n’étaient pas datés ! Aujourd’hui qu’en est-il de F# et faut-il l’utiliser ?

Une question qui est sa propre réponse ?

Plus de 5 ans que F# est intégré à Visual Studio, et on se pose encore la question de savoir s’il faut s'en servir… Peut-être que la réponse est justement dans la question. Car se poser cette question là prouve qu’en plus de 5 ans peu de choses ont changé et que F# ne s'est pas imposé. Ce qui de fait semble le disqualifier pour nous qui sommes tournés vers la production plus que vers l’abstraction de la recherche pure en informatique…

Donc d’une façon pragmatique il semble rationnel de répondre tout de suite par “non”. Non il faut pas utiliser F# car ce n’est pas un langage qui a percé et ce parce qu’il ne semble pas bien taillé pour la production. Si tel était le cas nous serions aujourd’hui des hasbeen de ne pas nous en servir, et ce n’est pas le cas. Je peux parfaitement dire que je n’aime pas F# à la machine à café et conserver toute mon honorabilité. Dire que je connais pas ferait léger, on se doit de savoir que ça existe et l’avoir testé un minimum. Mais ne pas aimer et ne pas utiliser reste tout à fait possible.

Cela peu paraitre anecdotique à certains lecteurs mais j’aime bien cette mise en abime des situations en les replaçant dans le contexte de la “machine à café”. C’est un endroit convivial où tout le monde se croise et échange quelques mots. Certains sujets ne déclencheront rien, d’autres vireront au pugilat. Certaines prises de position, de reconnaissance d’une lacune ou d’une autre passeront sans problème, d’autres déclencheront au mieux un rictus de dédain chez les uns au pire une humiliation publique… Et tout cela dépend majoritairement de la mode. Les informaticiens et les midinettes partagent en commun le gout irrépressible du nouveau pour le nouveau. Si H&M ou Zara décident de sortir des ceintures en plastique violet avec une tête de la pain en peluche comme boucle, vous avez intérêt à le savoir, à en connaitre le prix, et en avoir déjà acheté trois. Sinon les copines n’osent même plus se montrer en public avec vous !

Chez l’informaticien c’est la même chose. Notre métier fleurit tous les jours d’acronymes, d’environnements de développement, de librairies, de procédés techniques, de langages, etc. Et d’une on ne vous pardonnera pas de l’ignorer, de deux vous passerez pour un crétin pour la même raison, et si vous n’en connaissez même pas les grands principes on vous donnera l’impression que votre carrière s’achève ici et maintenant et si vous ne savez pas vous en servir la honte doit rougir votre front pour la fin des temps, le monastère semblant la seule solution pour oublier votre incompétence auprès de vos pairs…

F# fait partie de cette génération spontanée. Un peu comme le vide quantique grouille en réalité de paires de particules qui s’annihilent en quelques fractions de millisecondes le petit monde de l’informatique grouille de nouveautés toutes plus essentielles les unes que les autres dont ne se rappelle plus de leur existence deux mois après.

Parfois, des particules naissent vraiment de ce vide et de cette énergie grouillante (fluctuations quantiques du vide). A l’approche de l’horizon d’un trou noir l’une des particules d’une paire créée peut se trouver attirer par le trou noir alors que l’autre peut partir plus loin. C’est le rayonnement de Hawking qui expliquerait l’évaporation des trous noirs et la création de matière ex-nihilo.

F# est-il l’une de ces particules chanceuses issue de la fluctuation quantique du vide qui profitant des effets de marées du trou noir de l’oubli arriverait à exister dans notre univers au lieu de retourner au néant ?

C’est une question de fond car tout informaticien doit avoir l’œil à la fois sur le rétroviseur mais aussi sur les embuches qui obstruent éventuellement la route au loin, devant. Savoir piler à temps et changer de moyen de transport fait partie de la bonne gestion de sa carrière. Faut-il se déplacer en vieux tank rouillé C++ ou en jeep C# ou bien en papamobile C ? N’est-il pas plus judicieux d’utiliser le train à vapeur Java ou la fusée expérimentale F# ?

Et bien ne comptez pas sur moi pour vous donner la réponse !

En arriver jusque là pour ne pas donner son avis ? Pure folie ou grande sagesse ? … Je parlais de mode un peu plus haut. La mode ne se contrôle pas, pas plus en matière vestimentaire qu’en informatique. Affirmer aujourd’hui que F# ne s’étant pas imposé dans les 5 dernières années cela sera vrai pour les 5 suivantes est une prise de risque très dangereuse. Qui aurait dit il y a encore quelques années qu’un moteur de recherche du Web prendrait la 1ère place devant Apple et Microsoft avec un Système d’Exploitation ! Qui aurait prévu que le format GIF tombé en disgrâce depuis 15 ans reviendrait à la mode pour animer des petites scènes dans les posts des réseaux sociaux ?

La mode est absurde par essence. Vouloir la prédire est donc folie.

En revanche je peux vous aider à avoir l’air moins bête à la machine à café. Je peux même vous aider à tester et peut-être à aimer F#… Loin donc de toute affirmation dogmatique c’est pas l’exemple de notre code que nous nous améliorerons et améliorerons le monde, peu importe en quoi il est écrit. La tâche est vaste mais nous n’aspirons pas au repos - sinon nous aurions choisi un métier dans l’administration – et c’est en dégrossissant sans relâche des pierres brutes que nous nous perfectionnerons. Jouer avec F# fait parti de cette saine activité de remise en question de ses connaissances, une mise en danger salutaire qui conservent les neurones en mouvement même si F# ne détrônera peut-être jamais C# ou Java en production . .   .

Jouons un peu !

Comme la meilleure façon de répondre à la question posée par ce billet est de mettre les mains dans cambouis il nous faut des outils pour “jouer” avec F#.

Au même titre que LinqPad ou CS-Script nous permettent rapidement de jouer avec C# il existe des moyens pour taper rapidement quelques lignes de F# et en voir immédiatement le résultat.

F# se démocratisant, il existe plusieurs possibilités, même pour Linux.

Sous Mac ou pourra utiliser Xamarin Studio qui offre désormais F# à côté de C# pour construire des applications cross-plateformes. C’est peut-être un signe du temps ou bien juste un effet de mode, à vous de voir. Mais l’intégration de F# à Xamarin Studio marque malgré tout une seconde étape, un second coup de boutoir dans la porte de la forteresse Entreprise.

Car sous Windows depuis Visual Studio 2010 on peut aussi créer des projets F# et même jouer avec une fenêtre de code immédiat…

Sous Linux on peut installer le compilateur, sous Android et iOS il existe désormais les compilateurs de Xamarin (sous VS ou Xamarin Studio), sous Html/JS on peut utiliser WebSharper ou utiliser FunScript, voire utiliser F# côté serveur avec JS, CoffeeScript ou TypeScript de Microsoft.

Il est même parfaitement possible d’utiliser F# en programmation GPU avec Alea.cuBase qui se trouve dans la gallerie Nuget, StatFactory, FSCL, GpuLINQ, Brahama.FSharp, pour beaucoup des projets disponibles sur Github…

C’est donc une avalanche de possibilités qui s’offre à vous !

Mais pour commencer à se familiariser avec la syntaxe je vous conseille, outre la fenêtre immédiate de VS, le petit logiciel gratuit Tsunami qui offre un mini IDE tout à fait sympathique un peu comme LinqPad mais pour F#. On peut exécuter du code, quelques lignes ou un code entier.

image

Tsunami peut être téléchargé depuis le site suivant http://tsunami.io/ 

Tout comme LinqPad ou Notepad++ avec CS-Script, Tsunami est un compagnon de travail rapide, facile à utiliser qui ne nécessite pas de lancer de gros EDI complexes pour juste tester une idée. Cette légèreté convient parfaitement bien à l’apprentissage, ou au moins à l’essai d’un nouveau langage comme F#.

Les avantages de F#

Très honnêtement je vous avouerais que je ne suis pas encore totalement convaincu et que j’hésiterais bien si je devais prendre la décision d’utiliser F# en production. Il y a malgré tout une tournure d’esprit qu’il faut prendre.

Utiliser F# ce n’est pas seulement changer de langage comme on pourrait le concevoir entre C# et Java ou entre Delphi et C++. Malgré les différences entre tous ces langages ils partagent le fait d’être des langages conçus pour une programmation impérative.

F# est un langage fonctionnel. Cela n’a pas grand chose à voir en réalité. Il ne s’agira donc pas de remplacer IF/THEN/ELSE par une autre construction du langage mais bien de penser le code autrement. Ce qui est toujours plus difficile.

Alors pourquoi poser la question de l’utilité de F#, pourquoi 5 ans après un premier billet remettre ce langage sous les feux de l’actualité ? Parce qu’on sent bien, même confusément, que F# dispose d’un potentiel intéressant. On se sent attiré par un “je ne sais quoi” autant que repoussé par la forme et le fond par trop différents de nos habitudes. On l’écarte et puis on y revient. On tente de l’oublier et il se rappelle à nous…

Cela n’est la preuve de rien en tout cas de rien d’autre qu’il existe assurément dans F# et la vision fonctionnelle quelque chose d’attrayant qui force la curiosité.

Parmi les avantages de F# les plus souvent cités :

  • La concision. La syntaxe de F# ne s’encombre pas de tonnes de parenthèses, accolades, de symboles de fin d’instruction, non rien de tout cela. L’indentation du code suffit à jouer le même rôle comme en Python. On aime ou pas mais il est vrai que le code résultant est plus concis. Et malgré tout un code concis est plus facile à lire et à maintenir.
  • La commodité. Pas de mal de choses sont plus simples à faire en F# qu’avec d’autres langages. Pas des choses exotiques mais au contraire des tâches très courantes comme travailler sur des types complexes, travailler sur des listes, etc. Les fonctions (méthodes) étant des citoyens “first class” elles peuvent être utilisées partout où une valeur est attendue, ouvrant la voie à une programmation plus libre, plus réutilisable.
  • La justesse. F# est un langage très fortement typé même si on n’utilise jamais (ou presque) de précision de type… De même il est conçu de telle façon que les erreurs sur des références nulles sont presque impossibles. La compilation élimine beaucoup plus de mauvaises constructions par la tournure même du code, son esprit (le fonctionnel).
  • Le support multitâche. F# offre de base de nombreux moyens d’écrire un code multitâche efficace et peu bogué. La programmation asynchrone (qu’on retrouve dans C# désormais), un système de queue de messages, le support du parallélisme,… On notera aussi une spécificité de F# très intéressante en multitâche : l’immuabilité des valeurs.
  • La complétude. Si F# est à la base un langage purement fonctionnel, il a su s’enrichir en puisant dans les langages orientés objets certaines constructions ou facilités qui lui ouvrent de nouvelles portes. A ce titre C# et F# sont vraiment complémentaire : le premier est un langage impératif avec un peu de fonctionnel, le second est un langage fonctionnel avec un peu d’impératif et d’objet. Cela permet à F# de se sentir parfaitement à l’aise dans le framework .NET qui est programmé en langage impératif.

    Syntaxe

    Tout langage se caractérise d’abord par cette partie très visible qu’est sa syntaxe. Je n’ai jamais pu me faire à certains langages juste en raison de leur syntaxe et de “la tête” du code qu’on peut écrire. Ainsi je déteste C++ ou Visual Basic, non pas que professionnellement je les juge de moindre qualité, mais uniquement parce que personnellement je n’arrive pas à apprécier leur syntaxe. Nous ne sommes plus dans le monde logique mais dans le ressenti subjectif. A tel point que je n’ai pas aimé Java alors que j’adore C# dont la syntaxe ressemble au premier à quelques variations près. Va comprendre Charles !

    Que dire de la syntaxe de F# ? … Hmm au premier abord, et même au deuxième rabort (!), et même en attendant que la routourne ne tourne, ça ressemble un peu à du Python que je n’aime pas. Mais comme je le disais tout à l’heure F# malgré parfois un premier contact négatif est un langage qui attire et re-attire(alors que Python me laisse de marbre).

    Comme on se dit qu’il y a du bon dans F# on essaye de faire un effort et de dépasser cette première impression négative… Surtout qu’elle est très personnelle et que je suis certains que certains lecteurs adorent Python et trouveront du premier coup F# absolument génial ! C’est bien pour cela que j’en parle, Dot.Blog informe en profondeur et ne reflète pas forcément toujours mes gouts personnels, je sais reconnaitre qu’il y a du bon dans un produit, un langage et que ce “bon” pourra plaire même si moi je n’adhère pas forcément en sautant de joie.

    La syntaxe de F# fait partie des choses qui ne font pas sauter de joie. Mais je ne dis pas qu’avec le temps en le pratiquant longuement si l’occasion se présentait je ne finirai pas la trouver supérieure aux autres…

    Donc à quoi ressemble du code F# ?

    C’est bien de philosopher sur mes gouts mais finalement ici ce sont les vôtres qui comptent ! C’est pour vous que j’écris tout ça, moi je le sais déjà…

    Quoi de mieux que de récupérer un bel exemple complet qui montre (presque) tous les aspects du langage ? Rien, c’est pour cela que j’ai farfouiné et que je vous ai trouvé cela :

    // single line comments use a double slash
    (* multi line comments use (* . . . *) pair
    
    -end of multi line comment- *)
    
    // ======== "Variables" (but not really) ==========
    // The "let" keyword defines an (immutable) value
    let myInt = 5
    let myFloat = 3.14
    let myString = "hello"   //note that no types needed
    
    // ======== Lists ============
    let twoToFive = [2;3;4;5]        // Square brackets create a list with
                                     // semicolon delimiters.
    let oneToFive = 1 :: twoToFive   // :: creates list with new 1st element
    // The result is [1;2;3;4;5]
    let zeroToFive = [0;1] @ twoToFive   // @ concats two lists
    
    // IMPORTANT: commas are never used as delimiters, only semicolons!
    
    // ======== Functions ========
    // The "let" keyword also defines a named function.
    let square x = x * x          // Note that no parens are used.
    square 3                      // Now run the function. Again, no parens.
    
    let add x y = x + y           // don't use add (x,y)! It means something
                                  // completely different.
    add 2 3                       // Now run the function.
    
    // to define a multiline function, just use indents. No semicolons needed.
    let evens list =
       let isEven x = x%2 = 0     // Define "isEven" as a sub function
       List.filter isEven list    // List.filter is a library function
                                  // with two parameters: a boolean function
                                  // and a list to work on
    
    evens oneToFive               // Now run the function
    
    // You can use parens to clarify precedence. In this example,
    // do "map" first, with two args, then do "sum" on the result.
    // Without the parens, "List.map" would be passed as an arg to List.sum
    let sumOfSquaresTo100 =
       List.sum ( List.map square [1..100] )
    
    // You can pipe the output of one operation to the next using "|>"
    // Here is the same sumOfSquares function written using pipes
    let sumOfSquaresTo100piped =
       [1..100] |> List.map square |> List.sum  // "square" was defined earlier
    
    // you can define lambdas (anonymous functions) using the "fun" keyword
    let sumOfSquaresTo100withFun =
       [1..100] |> List.map (fun x->x*x) |> List.sum
    
    // In F# there is no "return" keyword. A function always
    // returns the value of the last expression used.
    
    // ======== Pattern Matching ========
    // Match..with.. is a supercharged case/switch statement.
    let simplePatternMatch =
       let x = "a"
       match x with
        | "a" -> printfn "x is a"
        | "b" -> printfn "x is b"
        | _ -> printfn "x is something else"   // underscore matches anything
    
    // Some(..) and None are roughly analogous to Nullable wrappers
    let validValue = Some(99)
    let invalidValue = None
    
    // In this example, match..with matches the "Some" and the "None",
    // and also unpacks the value in the "Some" at the same time.
    let optionPatternMatch input =
       match input with
        | Some i -> printfn "input is an int=%d" i
        | None -> printfn "input is missing"
    
    optionPatternMatch validValue
    optionPatternMatch invalidValue
    
    // ========= Complex Data Types =========
    
    //tuples are quick 'n easy anonymous types
    let twoTuple = 1,2
    let threeTuple = "a",2,true
    
    //record types have named fields
    type Person = {First:string; Last:string}
    let person1 = {First="john"; Last="Doe"}
    
    //union types have choices
    type Temp = 
        | DegreesC of float
        | DegreesF of float
    let temp = DegreesF 98.6
    
    //types can be combined recursively in complex ways
    type Employee = 
      | Worker of Person
      | Manager of Employee list
    let jdoe = {First="John";Last="Doe"}
    let worker = Worker jdoe
    
    // ========= Printing =========
    // The printf/printfn functions are similar to the
    // Console.Write/WriteLine functions in 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]
    
    // all complex types have pretty printing built in
    printfn "twoTuple=%A,\nPerson=%A,\nTemp=%A,\nEmployee=%A" 
             twoTuple person1 temp worker
    
    // There are also sprintf/sprintfn functions for formatting data
    // into a string, similar to String.Format.

     

    c’est assez court mais cela montre l’essentiel et assez pour se faire une idée.

    F# vs C#

    Je rappelle que nous sommes dans la section “syntaxe” c’est donc uniquement sur cet aspect que je vais faire quelques comparaisons.

    Voici comment on déclarerait une fonction “square” (mise au carré) et un autre fonction qui ferait la somme de ces carrés et comment on utiliserait cette dernière, en F# bien sûr :

    // définir la fonction square
    let square x = x * x
    
    // définir sumOfSquares, autre fonction
    let sumOfSquares n = 
       [1..n] |> List.map square |> List.sum
    
    // ... l'utiliser !
    sumOfSquares 100

     

    La sortie console donne cela :

    val square : x:int -> int
    val sumOfSquares : n:int -> int
    val it : int = 338350

    Ces précisions sont intéressantes car nous voyons qu’en fonction des valeurs utilisées l’inférence de type s’est bien effectuée et que notre code est fortement typé alors qu’aucun nom de type n’apparait dans ce dernier… Ainsi la fonction square est bien inférée en une fonction prenant un int en paramètre et retournant un int. Idem pour la fonction suivante. Quand au résultat il est bien un int et vaut 338.350.

    Il est possible de forcer le type dans certains cas particulier mais il faut comprendre F# un peu comme une programmation C# qui serait systématiquement et par défaut générique.

    Le même code écrit en C# pourrait être :

    public static class SumOfSquaresHelper
    {
       public static int Square(int i)
       {
          return i * i;
       }
    
       public static int SumOfSquares(int n)
       {
          int sum = 0;
          for (int i = 1; i <= n; i++)
          {
             sum += Square(i);
          }
          return sum;
       }
    }

     

    Toutes ces accolades, parenthèses, déclarations etc, tout cela est assez verbeux et, on le voit dans le code F#, inutile dans la pratique. On notera que le code ci-dessus ne fait que déclarer des méthodes équivalentes mais qu’elles ne sont pas utilisées à la différence du code F# qui utilise directement les définitions pour calculer une valeur exemple. Ici il faudrait ajouter un appel à
    Console.WriteLine(SumOfSquaresHelper.SumOfSquares(100));

    C’est quand même moins clair que SumOfSquares 100. Qu’on aime ou pas cette syntaxe elle est vraiment plus claire.

    On voit dans ce court exemple que :

  • Le code F# est bien plus concis
  • Le code F# bien que fortement typé n’utilise aucune nom de classe
  • Le code F# peut se développer de façon interactive comme un langage de scripting

     

    Il est vrai que LinqPad ou CS-Script permettent un peu la même chose avec C#, mais pas Visual Studio alors que ce dernier offre une fenêtre d’exécution immédiate pour F#.

    Pour nuancer encore un peu on peu noter que si j’utilise la partie fonctionnelle de C# le code exemple sera lui aussi plus concis. Je pourrais remplacer tout le code de SumOfSquares par le suivant :

    public int SumOfSquares(int n)
    { return Enumerable.Range(1,n).Select(x=>x*x).Sum(); }

Rien d’exotiques dans tout cela et je pourrais en écrire des pages pour comparer les deux langages, mon but est ici uniquement de vous faire découvrir ces nuances et non de les lister toutes.

Le code F# est-il “mieux” que le code C# ? A vrai dire c’est difficile à dire. Les accolades, le fait que tout test “if” doivent être entre parenthèses m’a toujours énervé en C#, tout viré est une position extrême à laquelle mon œil n’est pas habitué. Mais quelque part je trouve cela séduisant, pas vous ?

Conclusion

Je ne peux pas ici faire un cours complet de F#, un post de blog n’est vraiment pas adapté à un tel objectif. D’autant que mon objectif n’était pas non plus d’être exhaustif. Comme souvent mon intention était de vous faire découvrir quelque chose, aujourd’hui F#, qui est un langage séduisant, un enfant du framework .NET à égalité avec les autres comme C#, mais offrant une approche fonctionnelle très différente des langages conventionnels.

J’ai essayé de faire court tout en en disant assez pour vous allécher ! Je vous ai même donné les moyens de tester facilement et gratuitement F#.

Si avez tout ça vous n’essayez même pas d’écrire deux lignes de code dans ce langage la prochaine fois je vous parlerais de la recette du lapin chasseur ! (quoi que ce soit un sujet très intéressant surtout à l’heure de se mettre à table ce qui est le cas pour moi maintenant !).

Alors avons nous répondu à la question du billet ? Faut-il utiliser F# ?

Je crois que la réponse vous appartient, dites moi ce que vous en pensez les commentaires sont ouverts pour ça !

Stay Tuned !

Faites des heureux, partagez l'article !
blog comments powered by Disqus