Développement Web frontend en Haskell, Elm et Purescript

Posté par (page perso) . Édité par Benoît Sibaud, Davy Defaud et palm123. Modéré par Benoît Sibaud. Licence CC by-sa.
46
19
août
2018
Programmation fonctionnelle

Actuellement, le développement Web côté client (frontend) est très souvent réalisé en JavaScript ou dans des langages dérivés comme TypeScript. Il existe cependant d’autres outils intéressants, basés sur des langages de programmation fonctionnelle, qui permettent notamment d’éviter de nombreuses erreurs lors de l’exécution sur le navigateur.

L’objectif de cette dépêche est de rappeler quelques généralités sur le développement Web frontend, et de présenter les outils Elm, Purescript, Miso et Reflex, à partir d’un exemple d’application (galerie d’images fournie via une API Web).

Attention : ceci n’est pas une étude rigoureuse et avancée mais juste un petit retour de petite expérience.

Voir également le dépôt de code de l’exemple et une présentation en vidéo.

Sommaire

Généralités sur le web frontend

Page web, application web, application native

Historiquement, les pages web se contentaient d'afficher un contenu statique et de proposer des liens vers d'autres pages. Des éléments dynamiques sont ensuite progressivement apparus : animations, formulaires avec vérifications de saisies… si bien, qu'aujourd'hui, de nombreuses pages web sont de véritables interfaces utilisateurs, comparables aux logiciels classiques installés sur le système. On appelle ce genre de pages des applications web (exécutées par un navigateur web), à distinguer des applications natives (exécutées par le système d'exploitation).

D'un point de vue utilisateur, les applications web et les applitions natives sont de plus en plus proches. Par exemple, on trouve des applition web de traitement de texte (frama-pad, google-docs) qui possèdent une bonne part des fonctionnalités de leurs équivalents natifs (libreoffice, msoffice).

D'un point de vue développeur, les technologies utilisées sont historiquement très différentes. Les applications natives utilisent généralement des langages comme Java, C#, Swift et leurs frameworks associés. Les applications web utilisent les technologies issues du web HTML/CSS/JavaScript et dépendent quasi-systématiquement d'un accès réseau via des websockets, requêtes AJAX ou autres. Cependant, on note une convergence web/natif également à ce niveau, notamment avec l'apparition du framework Electron, qui permet d'utiliser des technologies web pour développer des applications natives. De même, des architectures logicielles comme le Modèle-Vue-Contrôleur, très courant en natif, a été repris dans de nombreux frameworks web.

Les langages pour le web frontend

Le langage des applis web est sans conteste le JavaScript. C'est un langage assez controversé mais qui a tout de même des avantages indéniables, surtout depuis les récentes normes (ES2015…) : flexibilité, expressivité, compilateurs Just-In-Time performants, intégration dans les navigateurs…

Cependant, JavaScript permet facilement de faire des erreurs qui se produiront à l'exécution finale de l'application, les fameuses runtime errors. Pour éviter ces erreurs, on utilise souvent des outils comme des analyseurs statiques, debuggers ou tests unitaires. Une autre solution consiste à utiliser des frameworks (Angular, React…), des bibliothèques (Immutable, Redux…) voire des langages dérivés (TypeScript…) qui réduisent les sources d'erreurs possibles.

Les langages fonctionnels pour le web frontend

En fait, une bonne partie des solutions proposées pour rendre le développement en JavaScript plus robuste existe déjà naturellement dans des langages fonctionnels comme Haskell : immuabilité, fonctions pures, typage statique fort… Certains développeurs se sont donc naturellement inspirés des langages fonctionnels pour proposer des technos web frontend garantissant l'absence d'erreurs au runtime.

L'idée de ces technos est de fournir un écosystème complet (langage fonctionnel, compilateur, bibliothèques…) adapté au web et produisant du code JavaScript exécutable par un navigateur. Parmi ces technos, on trouve Elm et Purescript, qui proposent des langages inspirés d'Haskell. Il est également possible d'utiliser directement le langage Haskell (éventuellement avec des bibliothèques comme Miso ou Reflex) et le compilateur Ghcjs pour produire du code JavaScript. Enfin, il existe des outils dans d'autres langages fonctionnels comme ClojureScript, Ocsigen (OCaml)…

Les concepts de MVC, FRP, Virtual-DOM

Ces trois concepts sont généralement au cœur des technos web frontend fonctionnelles. Ils sont également assez fréquents dans l'écosystème JavaScript classique.

Le MVC (Model-View-Controler) est une architecture de code qui permet d'organiser une application en trois parties : le modèle (données « métier » à manipuler), la vue (affichage présenté à l'utilisateur) et le contrôleur (mise à jour de l'application en fonction des événements). Généralement, un MVC gére les événements de façon asynchrone et unidirectionnelle : les événements de la vue sont passés au contrôleur, qui modifie le modèle puis lance un rafraichissement de la vue…

MVC

crédit : Ossi Hanhinen

Le FRP (Functional Reactive Programming) est le principe de base des frameworks fonctionnels, sur lequel est implémenté le MVC. Le FRP permet d'implémenter le comportement dynamique des interfaces utilisateur. Il traite les flux d'événements au cours du temps et transmet ces flux entre les différents composants de l'application, le tout de façon fonctionnelle (sans effet de bord).

Functional Reactive Programming

crédit : André Staltz

Enfin, un concept essentiel aux applications web est le DOM virtuel. Le DOM (Document-Object-Model) décrit la structure d'une page web, donc de l'application, dans le navigateur. Au cours de son exécution, une appli web a besoin de manipuler le DOM, pour récupérer des données ou pour modifier l'affichage. Or, manipuler directement le DOM est coûteux et résulte en une application peu réactive. Pour améliorer les performances, les frameworks web utilisent un système de cache, le DOM virtuel, qui regroupe des modifications et ainsi minimise les accès au DOM.

DOM et DOM virtuel

crédit : Naukri Engineering

Un exemple de base

Pour illustrer les outils de web frontend fonctionnel, imaginons qu'on veuille implémenter une appli client-serveur de recherche et d'affichage d'images d'animaux. Le serveur fournit les images en HTTP à l'URL /img/<nom-du-fichier>. Il fournit également une API JSON à l'URL /api/animals <prefix> auquel il répond par la liste des animaux de sa base de données dont le type correspond au préfixe donné en paramètre. Chaque élément de la liste retournée contient le type de l'animal et le nom du fichier image (accessible via l'URL /img). Si aucun préfixe n'est donné, le serveur retourne la liste complète.

L'appli web client à réaliser contient simplement une zone de texte permettant de saisir le préfixe. Il envoie des requêtes AJAX au serveur pour récupérer les animaux correspondant puis affiche les images correspondantes après les avoir également demandées au serveur.

Ci-dessous une image du client implémenté en Purescript. L'ensemble du code est disponible sur ce dépôt git.

client en purescript

Elm

logo ELM

Elm est un environnement complet de développement web frontend fonctionnel. Il fournit un langage fonctionnel (inspiré d'Haskell mais en beaucoup plus simple), un compilateur Elm vers JavaScript et des bibliothèques. Elm est basé sur un DOM virtuel performant et permet de développer des applis web selon une architecture MVC.

Ci-dessous une implémentation en Elm de l'application d'exemple (note pour les développeurs Elm : désolé pour le non-respect des règles de formatage de code Elm mais j'ai subi des pressions de la part de la ligue de protection des molettes de souris). Ce code suit un schéma MVC et un style fonctionnel très classique. On définit un type Animal, avec ses fonctions de décodage de données JSON, ainsi que le modèle de l'application, c'est-à-dire simplement la liste des Animal à afficher.

Au niveau du contrôleur, le type Msg définit les événements qui peuvent se produire. L'événement MsgInput modélise une action de l'utilisateur sur la zone de texte et l'événement MsgAnimals un message du serveur transmettant les Animal en réponse à une requête à l'API. Ces événements sont gérés dans la fonction update : MsgInput ne change pas le modèle mais lance une requête à l'API via la fonction queryAnimals, MsgAnimals met à jour le modèle avec les données Animal reçues.

Enfin, la fonction view indique comment construire la vue de l'appli, à partir du modèle : un titre h1, une zone de texte input puis un div pour chaque Animal du modèle.

module Main exposing (..)

import Html exposing (..)
import Html.Attributes exposing (height, href, src, width)
import Html.Events exposing (onClick, onInput)
import Http
import Json.Decode as JD

main : Program Never Model Msg
main = Html.program
        { init = init
        , view = view
        , update = update
        , subscriptions = subscriptions
        }

-- Model

type alias Animal = 
  { animalType : String 
  , animalImage : String
  }

decodeAnimal : JD.Decoder Animal
decodeAnimal = JD.map2 Animal
        (JD.field "animalType" JD.string)
        (JD.field "animalImage" JD.string)

decodeAnimalList : JD.Decoder (List Animal)
decodeAnimalList = JD.list decodeAnimal

type alias Model = { modelAnimals : List Animal }

init : ( Model, Cmd Msg )
init = ( Model [], queryAnimals "" )

-- Controler

subscriptions : Model -> Sub Msg
subscriptions _ = Sub.none

type Msg 
  = MsgInput String 
  | MsgAnimals (Result Http.Error (List Animal))

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        MsgInput animalType -> ( model, queryAnimals animalType )
        MsgAnimals (Ok Model animals) -> ( Model animals, Cmd.none )
        MsgAnimals (Err _) -> ( Model [], Cmd.none )

queryAnimals : String -> Cmd Msg
queryAnimals txt =
    let url = "http://localhost:3000/api/animals/" ++ txt
    in Http.send MsgAnimals (Http.get url decodeAnimalList)

-- View

view model =
    span [] [ h1 [] [ text "Animals (Elm)" ]
            , p [] [ input [ onInput MsgInput ] [] ]
            , span []
                (List.map
                    (\a -> div [] [ p [] [ text a.animalType ]
                                  , img
                                    [ src ("http://localhost:3000/img/" ++ a.animalImage)
                                    , height 240
                                    , width 320
                                    ]
                                    []
                                  ]
                    ) model.modelAnimals
                )
            ]

Elm a l'avantage d'être particulièrement simple à utiliser. Les applis développées suivent toujours un MVC bien définis et les messages du compilateur sont particulièrement clairs. Le code JavaScript produit est léger et performant. Parmi les inconvénients, on peut noter que Elm est limité au schéma MVC qui, bien que très répandu, ne sera peut-être pas adapté à toutes les applications.

Purescript

logo purescript

Purescript est également un environnement complet (langage, compilateur, bibliothèques) mais plus général que Elm. En effet, son langage est plus proche d'Haskell et plus puissant (il supporte notamment les classes de type). De plus, Purescript propose différentes architecture de code, dont MVC. Enfin, Purescript peut être également utilisé pour le développement côté serveur.

Ci-dessous une implémentation en Purescript de l’application d’exemple, utilisant la bibliothèque Halogen (FRP + DOM virtuel). Ce code est très proche du code Elm. Pour le modèle, on définit également un type Animals, avec les fonctions de décodage JSON, et un type Model. Pour le contrôleur, on définit un type Query qui permet de gérer la requête AJAX et sa réponse, via la fonction eval. Enfin, la vue suit le même schéma que l'implémentation en Elm.

module Main where

import Control.Monad.Aff (Aff)
import Control.Monad.Eff (Eff)
import Data.Argonaut ((.?), class DecodeJson, decodeJson, Json)
import Data.Either (Either(..))
import Data.Maybe (Maybe(..))
import Data.Traversable (traverse)
import Halogen as H
import Halogen.Aff as HA
import Halogen.HTML as HH
import Halogen.HTML.Events as HE
import Halogen.HTML.Properties as HP
import Halogen.VDom.Driver (runUI)
import Network.HTTP.Affjax as AX
import Prelude

main :: Eff (HA.HalogenEffects (ajax :: AX.AJAX)) Unit
main = HA.runHalogenAff do
    body <- HA.awaitBody
    io <- runUI ui unit body
    io.query $ H.action $ QueryAnimals ""

ui :: forall eff. H.Component HH.HTML Query Unit Void (Aff (ajax :: AX.AJAX | eff))
ui = H.component
    { initialState: const initialState
    , render
    , eval
    , receiver: const Nothing
    }

-- Model

newtype Animal = Animal
  { animalType :: String
  , animalImage :: String
  } 

instance decodeJsonBlogPost :: DecodeJson Animal where
  decodeJson json = do
    obj <- decodeJson json
    animalType <- obj .? "animalType"
    animalImage <- obj .? "animalImage"
    pure $ Animal { animalType, animalImage }

decodeAnimalArray :: Json -> Either String (Array Animal)
decodeAnimalArray json = decodeJson json >>= traverse decodeJson

type Model = { modelAnimals :: Array Animal }

initialState :: Model
initialState = { modelAnimals: [] }

-- Controler

data Query a = QueryAnimals String a

eval :: forall eff. Query ~> H.ComponentDSL Model Query Void (Aff (ajax :: AX.AJAX | eff))
eval (QueryAnimals animal_type next) = do
    H.modify (_ { modelAnimals = [] })
    response <- H.liftAff $ AX.get ("http://localhost:3000/api/animals/" <> animal_type)
    let animals = case decodeAnimalArray response.response of
                    Left _ -> []
                    Right ra -> ra
    H.modify (_ { modelAnimals = animals })
    pure next

-- View

render :: Model -> H.ComponentHTML Query
render m =
    HH.span []
        [ HH.h1 [] [ HH.text "Animals (Purescript)" ]
        , HH.p [] [ HH.input [ HE.onValueInput (HE.input QueryAnimals) ] ]
        , HH.span [] 
            (map (\ (Animal {animalType, animalImage}) 
                  -> HH.div [] 
                        [ HH.p [] [ HH.text animalType ]
                        , HH.img [ HP.src ("http://localhost:3000/img/" <> animalImage)
                                 , HP.width 320
                                 , HP.height 240
                                 ]
                        ]
                 ) m.modelAnimals)
         ]

Purescript a l'avantage d'être plus puissant et plus général que Elm. En contrepartie, il est moins simple à utiliser. Son environnement est également plus compliqué à utiliser : il faut gérer les dépendances Purescript avec bower, les dépendances nodejs avec npm et la compilation avec pulp.

Haskell/Miso

Miso

Miso est une bibliothèque Haskell pour développer des applis web frontend. Miso permet de coder une appli MVC + DOM virtuel et de la compiler en JavaScript grâce à Ghcjs.

Ci-dessous une implémentation en Haskell + Miso de l’application d’exemple. Ce code est très similaire à l'implémentation en Elm. Miso annonce d'ailleurs explicitement s'inspirer de Elm.

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}

import Data.Aeson (FromJSON, decodeStrict)
import Data.Maybe (fromMaybe)
import Data.Monoid ((<>))
import Data.Text (Text)
import GHC.Generics (Generic)
import JavaScript.Web.XMLHttpRequest (Request(..), RequestData(..), Method(..), contents, xhrByteString)
import Miso
import Miso.String (MisoString, toMisoString, fromMisoString, pack)

main :: IO ()
main = startApp App 
    { model = Model []
    , update = updateModel
    , view = viewModel
    , subs = []
    , events = defaultEvents
    , initialAction = GetAnimals ""
    , mountPoint = Nothing
    }

-- Model

data Animal = Animal 
    { animalType :: Text
    , animalImage :: Text
    } deriving (Eq, Generic, Show)

instance FromJSON Animal

data Model = Model { modelAnimals :: [Animal] } deriving (Eq, Show)

-- Controler

data Action 
    = GetAnimals MisoString 
    | SetAnimals [Animal]
    | NoOp 
    deriving (Show, Eq)

updateModel :: Action -> Model -> Effect Action Model
updateModel (GetAnimals str) m = m <# (SetAnimals <$> queryAnimals str)
updateModel (SetAnimals animals) m = noEff m { modelAnimals = animals }
updateModel NoOp m = noEff m

queryAnimals :: MisoString -> IO [Animal]
queryAnimals str = do
    let uri = pack $ "http://localhost:3000/api/animals/" ++ fromMisoString str
        req = Request GET uri Nothing [] False NoData
    Just cont <- contents <$> xhrByteString req
    return $ fromMaybe [] $ decodeStrict cont

-- View

viewModel :: Model -> View Action
viewModel (Model animals) = 
    span_ []
        [ h1_ [] [ text "Animals (Miso)" ]
        , p_ [] [ input_ [ onInput GetAnimals ] ]
        , span_ [] $ map fmtAnimal animals
        ]

fmtAnimal :: Animal -> View Action
fmtAnimal animal = 
    div_ [] 
        [ p_ [] [ text $ toMisoString $ animalType animal ]
        , img_ [ src_ $ toMisoString $ "http://localhost:3000/img/" <> animalImage animal
               , width_ "320"
               , height_ "240"
               ]
        ]

Pour un développeur Haskell, Miso est une bibliothèque intéressante, car elle permet d'implémenter des applis simples « à la Elm » tout en restant dans l'écosystème Haskell. En revanche, son environnement est moins mature : les outils Ghcjs + Miso + Nix ne sont pas complètement triviaux à mettre en place et les temps d'installation d'installation et de compilation plus longs.

Haskell/Reflex

logo Reflex

Reflex est une bibliothèque Haskell de FRP générique. Elle est complétée par des projets associés : reflex-dom (DOM virtuel), reflex-platform (système de compilation multi-plateforme)… Une application Reflex peut être compilée avec Ghc ou avec Ghcjs et ainsi produire des applications web ou natives (PC ou mobile).

Ci-dessous une implémentation en Haskell + Reflex de l’application d’exemple. Contrairement aux implémentations précédentes, ce code ne suit pas une architecture MVC mais gère explicitement les éléments graphiques et leurs flux d'événements. Il est tout à fait possible d'organiser le code selon un MVC mais ceci est à la charge du programmeur.

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}

import Data.Aeson (FromJSON)
import Data.Default (def)
import Data.Maybe (fromJust)
import Data.Monoid ((<>))
import Data.Text (Text)
import GHC.Generics (Generic)
import Prelude
import Reflex (holdDyn)
import Reflex.Dom 
import Reflex.Dom.Xhr (decodeXhrResponse, performRequestAsync, XhrRequest(..))

main :: IO ()
main = mainWidget ui

-- Model

data Animal = Animal 
    { animalType :: Text
    , animalImage :: Text
    } deriving (Eq, Generic, Show)

instance FromJSON Animal

-- View / Controler

ui :: MonadWidget t m => m ()
ui = do
    el "h1" $ text "Animals (Reflex)"
    myInput <- el "p" $ textInput def
    evStart <- getPostBuild
    let evs = [ () <$ _textInput_input myInput , evStart ]
    let evCode = tagPromptlyDyn (value myInput) (leftmost evs)
    evResponse <- performRequestAsync $ queryAnimals <$> evCode
    let evResult = fromJust . decodeXhrResponse <$> evResponse
    dynAnimals :: (Dynamic t [Animal]) <- holdDyn [] evResult 
    _ <- el "span" $ simpleList dynAnimals displayAnimal
    return ()

queryAnimals :: Text -> XhrRequest ()
queryAnimals code = XhrRequest "GET" ("http://localhost:3000/api/animals/" <> code) def

displayAnimal :: MonadWidget t m => Dynamic t Animal -> m ()
displayAnimal dynAnimal = do
    let imgSrc = (<>) "http://localhost:3000/img/" . animalImage <$> dynAnimal
    let imgAttrs0 = ("width" =: "320") <> ("height" =: "240")
    let imgAttrs = ((<>) imgAttrs0) . (=:) "src" <$> imgSrc
    el "div" $ do
        el "p" $ dynText $ animalType <$> dynAnimal
        elDynAttr "img" imgAttrs $ dynText imgSrc

Reflex est un projet assez ambitieux, censé permettre de développer tout type d'interfaces utilisateur et pour de nombreuses plateformes différentes. Il a l'avantage d'utiliser le langage Haskell. En revanche, il est assez compliqué à prendre en main. Programmer directement en FRP nécessite un vrai apprentissage ainsi qu'une bonne compréhension des foncteurs, applicatives et monades. Enfin, il faut structurer son code soigneusement mais soi-même.

Conclusion

Elm, Purescript, Miso et Reflex permettent de développer des applis web tout en profitant des avantages de la programmation fonctionnelle. Ces outils facilitent effectivement la validation de code et le refactoring, et réduisent voire suppriment les erreurs au runtime.

Le principal inconvénient réside dans le choix parmi ces outils, qui nécessite de faire un compromis entre simplicité et flexibilité. Elm et Purescript sont de bonnes options pour du web « pur et dur ». Miso et Reflex sont plutôt à réserver à des développeurs Haskell expérimentés.

Enfin, en dehors d'Haskell, il peut être intéressant de considérer d'autres outils comme ClojureScript, Ocsigen…

Aller plus loin

  • # MVC

    Posté par . Évalué à 6.

    Je ne me suis pas penché sur les autres que je ne connais pas, mais ELM n'implémente pas MVC.

    Le contrôleur de MVC manipule le modèle souvent, le modèle est représenté par les données membres d'une classe contrôleur. Le contrôleur possède l'état, on appel l'une de ses méthode et il modifie l'état. Ici l'Update va prendre l'état courant en paramètre + un message qui est généré par la vue et il va en ressortir un nouvel état (+ éventuellement un message). L'Update ne possède pas l'état, c'est quelque chose qui lui est donné en paramètre et qu'il ne peux pas modifier. Un paquet de frameworks MVC obligent la vue à passer par le contrôler pour accéder au modèle (c'est lui qui le possède), alors que dans cet architecture ça n'est pas le cas.

    Ça paraît anodin mais conceptuellement c'est très différent et ça a pas mal d'impacts.

    • [^] # Re: MVC

      Posté par (page perso) . Évalué à 4.

      Ce que tu décris est une implémentation classique d'un MVC en POO, mais un MVC ne peut pas s'implémenter comme ça en programmation fonctionnelle. D'autre part, pour le web frontend, l'aspect asynchrone est important et c'est pour cela qu'on utilise souvent des MVC unidirectionnels. Le MVC n'a pas vraiment de définition rigoureuse. Elm parle effectivement de Model-View-Update https://guide.elm-lang.org/architecture/ et chaque framework propose sa variante (MVP, MVE, MVVM, MVW…). Ici, j'ai préféré utiliser MVC dans un sens un peu général.

      • [^] # Re: MVC

        Posté par . Évalué à 2. Dernière modification le 20/08/18 à 21:54.

        Ben je pense que c'est vraiment plus à rapprocher de l'architecture Flux décrit par Facebook. Il n'y a pas de binding du model dans la vue. L'objectif c'est que la donnée a un chemin unidirectionnelle. En tout cas c'est pour ça que Flux a était créé.

  • # Article sympa a lire

    Posté par . Évalué à 4.

    Merci pour cet article et comparaison, sympa a lire.

    Quelle est la solution la plus simple pour faire une application mobile venant du ELM/Haskell?
    Je souhaiterai avoir le ressenti d'un développeur Haskell avec Reflex, et si ça vaut le coup pour faire des applications mobiles?

    Quelques typos:
    - les applitions natives // applications
    - des applition web // applications
    - on définit également un type Animals // Animal

    Question de curiosite, immuabilité et immutabilité font référence au mème concept?

    • [^] # Re: Article sympa a lire

      Posté par (page perso) . Évalué à 4.

      Je n'ai aucune expérience dans le mobile mais apparemment ça fait partie des objectifs de Reflex. Le projet Reflex-platform permet de compiler pour du natif, du web, android et ios (https://github.com/reflex-frp/reflex-platform/blob/develop/docs/project-development.md). Perso, je trouve que Reflex n'est pas complètement trivial et qu'il faut prévoir un peu de temps pour l'apprendre correctement, mais Reflex-platform peut aussi compiler du Miso; c'est peut-être un bon compromis (https://github.com/dmjio/miso/issues/367).

      Pour immuable/immutable, oui c'est le même concept. Je crois que ce sont vraiment des synonymes mais immutable, ça fait un peu anglicisme.

    • [^] # Re: Article sympa a lire

      Posté par (page perso) . Évalué à 7.

      Hello,

      Pour ma part je ne suis absolument pas du tout du web / application mobile, alors le jour ou j'ai du en réaliser une, j'ai cherché comment faire en Haskell ;)

      J'ai fais ça pour ma femme: https://github.com/guibou/givorsDays

      J'ai utilisé Reflex et, une fois le setup de reflex-plateform effectué, c'est très agréable. Ce qui est vraiment fort c'est de pouvoir develloper sur son ordinateur en testant dans son navigateur, avec un rebuild instantané, et d'avoir l'application mobile qui sort automatiquement après.

      La façon de programmer de Reflex (i.e. le "Reactive programming") est très agréable à utiliser quand je compare au plat de nouille que j'aurais pu faire si je l'avais fais en mode evénement / callback. Ceci est à prendre avec des pincettes, car ce n'est pas du tout mon domaine de prédilection.

      Les plus de Reflex, de mon point de vu limité :

      • Reactive programming, le tout très bien intégré à Haskell. Un Event est une instance de Functor, donc map fonctionne dessus.
      • Ecrire un widget réutilisable est un bonheur.
      • Accès à tout l'écosystème Haskell et à (presque) toutes les librairies.
      • Le code frontend et backend (si besoin) peu être partagé. Ma première version de l'application avait un backend en Haskell (depuis j'utilise google drive), et c'était très agréable de manipuler des deux cotés les mêmes types, surtout pour les requêtes à l'API.
      • De façon amusante, Reflex s'en sort très bien avec les librairies qui modifient ton DOM en même temps tant que les choses restent cohérentes.

      Les moins:

      • Il n'y a pas de MVC ou autre. C'est à toi d'architecturer proprement.
      • Reflex.Dom c'est du web, donc il faut à minima y comprendre quelque chose en mise en page avec des CSS. Il y a quelques libraries (comme Clay: http://fvisser.nl/clay/) qui permettent d'éditer facilement du CSS, mais je me suis vraiment perdu dans ces histoire de flex ou table ou autre. Une personne habituée au CSS n'aura pas ce problème. On doit aussi pouvoir utiliser des librairies de layout toute prêtes, mais je n'y connais rien.
      • Le FFI avec javascript. Soit t'as un binding déjà fait pour faire ce que tu veux faire, soit c'est l'horreur. Par example, Reflex.Dom n'avait rien (à l'époque) pour accéder aux webstorage, j'ai du le faire à la main.
      • Pas de template Haskell si tu veux compiler pour mobile. Ce n'est pas dramatique, mais c'est agaçant. Et bien evidament toutes les librairies Haskell qui ont une dépendance à une sous librairie écrite en C ne passeront pas si tu veux compiler pour javascript.
      • Le javascript generé est gros et lent. Ce n'est pas un problème si tu vises une machine de bureau, mais c'est un problème pour mobile. Heureusement tu peux compiler en natif pour mobile (ios et android), donc ce n'est pas une grosse limitation.
      • Rien n'est fait pour utiliser le code natif en mode mobile, ce sera donc à toi de le faire si tu veux par example utiliser l'API fichier de ton Android.
  • # réinvention de la roue

    Posté par . Évalué à -6. Dernière modification le 27/08/18 à 11:27.

    1. Elm, Purescript, Miso et Reflex ne sont pas standards….
    2. la syntaxe, elle fait mal à la tête
    3. En utilisant une librairie telle que Backbone/underscore/jQuery, React, vuejs, Angularjs, etc. on arrive au même résultat
    4. En codant du JS natif (ES5, ES6, ES7, etc.), on arrive au même résultat avec le même nombre de lignes avec des perfs largement supérieurs

    Quel est l'intérêt de ces outils (Elm, Purescript, Miso et Reflex) ? aucun !

    • [^] # Re: réinvention de la roue

      Posté par . Évalué à 6.

      Wow du calme :)

      1. Elm, Purescript, Miso et Reflex ne sont pas standards….

      Mis à par Vanilla JS aucune lib ni framework front web n'est standard.

      1. la syntaxe, elle fait mal à la tête

      C'est ton avis.

      1. En utilisant une librairie telle que Backbone/underscore/jQuery, React, vuejs, Angularjs, etc. on arrive au même résultat

      Non. Déjà tu mélange vraiment beaucoup de choses… React et Vue sont des techno de vue uniquement, donc ils ne se placent pas en alternative. Et aucune des autres techno dont tu parle n'implémente véritablement de patern réactif. Tu aurais parlé de redux, vuex ou ngrx tu aurais déjà gardé un peu plus de crédibilité. Bon pour le coup ELM est celui qui a inspiré ces framework donc…

      Mais même sans être arrivé avant. ELM (je parle de celui que je connais) vient avec un ensemble assez cohérent de fonctionnalités. Redux te dis de ne jamais muter ton état, mais pour le faire soit il dois te faire confiance soit passer par des bibliothèques pour faire ça. Elles font un peu comme elles peuvent là où un compilateur va assurer dans une phase avant même l'exécution (ELM a une étape de compilation vers JS). Son système de type lui permet aussi de garantir que certaines classes d'erreur ne peuvent pas exister par construction. Là où dans des langages ayant un système de type moins puissant c'est impossible.

      1. En codant du JS natif (ES5, ES6, ES7, etc.), on arrive au même résultat avec le même nombre de lignes avec des perfs largement supérieurs

      Pour les perf je n'en sais rien, mais pour le résultat, c'est possible mais pas facile. T'assurer que tu n'a aucune erreur de type en JS ça n'est pas possible quelque soit le nombre que tu accole à ES.

      Quel est l'intérêt de ces outils (Elm, Purescript, Miso et Reflex) ? aucun !

      Ils simplifient fortement l'implémentation d'une architecture réactive/flux dans les applications web. Ce sont aussi des langages plus sûr que JS.

  • # Projet de réalisation d'un site internet utilisant ces technologies.

    Posté par . Évalué à 0.

    J'envisage d'utiliser ces technos pour réaliser un site de prise de décision par émergence de consensus.

    Ce site permettra à chaque participant (d'un Conseil d'Administration d'une Scop ou d'association, par exemple) d'émettre des propositions et suggestions dans une sorte de forum (une agora) et d'ordonner préférentiellement celles-ci.
    Par la méthode de Condorcet de ce rangement émerge le consensus et fait office de vote.

    Je développe en Agda mais ne maitrisant pas suffisamment Haskell pour me lancer seul dans un tel projet, toute aide sera bienvenue.

    Si vous souhaitez plus de précisions ou que cela vous intéresse, merci de me contacter :

    Serge Leblanc 33dbqNxPy7IF@gmail.com
    GnuPG --fingerprint : 2B2D AC93 8620 43D3 D2C2 C2D3 B67C F631 67B1 7A3F

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.