DSLs pour le Développement Agile de Transformations Laurent Thiry*, Bernard Thirion*, Michel Hassenforder* Ecole Nationale Supérieure d Ingénieurs du Sud Alsace Laboratoire MIPS, Université de Haute Alsace 12, rue des frères Lumière 68093 MULHOUSE cedex * {laurent.thiry, bernard.thirion, michel.hassenforder}@uha.fr RESUME. L IDM propose des concepts et des outils pour concevoir plus simplement des applications. Une approche complémentaire aux standards existants, et qui trouve un intérêt croissant dans le cadre des logiciels complexes, est la métamodélisation fonctionnelle. Celleci profite de la puissance d expression et de la généricité des langages fonctionnels pour définir des métamodèles et des transformations de modèles. Cet article propose de montrer les intérêts d une telle approche sur une famille de Langages Spécifiques de Domaines (DSLs) dédiés au prototypage de transformations. Plus précisément, l article décrit une méthode pour construire des DSLs permettant d intégrer en entrée ou en sortie d une transformation des modèles textuels et pour extraire/ajouter une information dans un modèle. ABSTRACT. Model driven engineering community proposes the concepts and the tools to design more easily software applications. Complementary to the elements proposed, the use of a functional approach becomes more and more important in the field of complex software. More precisely, current works use the genericity of functional programming languages to describe more easily and formally metamodels and model transformations. This article proposes to show the benefits of such an approach with a set of Domain Specific Languages (DSLs) to design more quickly model transformations. The article describes a process to build DSLs that makes it possible the integration of textual models at the input or the output of a transformation, and the extraction or the add of information from/to a model. MOTS CLEF. IDM, DSL, approche fonctionnelle, transformations de modèles KEYWORDS. IDM, DSL, Functional approach, Model transformations
IDM'2008 5-6 Juin Mulhouse 1. Introduction 1.1. Contexte L intérêt de l Ingénierie Dirigée par les Modèles (IDM) est qu elle propose des outils génériques devant simplifier le développement de systèmes logiciels, (Mellor et al., 2003). Ces outils sont construits autour du concept de métamodèle qui permet de définir un langage de modélisation particulier à un domaine ou d intégrer plus facilement plusieurs types de modèles (Thiry et al., 2006, Vangheluwe et al., 2003). Ils intègrent aussi le concept de transformations de modèles pour pouvoir, par exemple, relier un modèle à une plate forme technologique spécifique ; cette séparation, entre modèles métiers et aspects technologiques, permet ainsi de capitaliser plus facilement l expertise d un domaine. En particulier, l application des concepts de métamodèles et de transformations de modèles dans un cadre fonctionnel trouve un intérêt croissant dans le domaine des logiciels pour les systèmes (Mathaikutty, 2005 ; Thiry et al., 2008). Les métamodèles nécessaires pour décrire, par exemple, les aspects continus ou discrets des systèmes dynamiques sont exprimés dans un cadre fonctionnel ; des fonctions permettent alors de transformer les modèles ou de préciser la sémantique dynamique associée à ces modèles. L intérêt d un cadre fonctionnel est alors de pouvoir décrire formellement les modèles et les transformations nécessaires. Pour montrer les intérêts de cette approche, l article propose une interprétation des concepts de métamodèle et de transformation dans un cadre fonctionnel pour développer de manière agile, et prototyper plus rapidement, des transformations. Plus précisément, il montre comment le langage Haskell (Hudack et al., 2007) permet de mettre en oeuvre des Langages Spécifiques de Domaine simples, i.e. définis par quelques éléments de base associés à des combinateurs, pour les différentes parties d une transformation (Deursen et al., 2000). 1.2. Principes L approche proposée pour construire des DSLs dédiés aux transformations repose sur deux principes qui consistent à: 1) décomposer une transformation t en deux, ou plusieurs, fonctions plus simples u et v (i.e. t = u. v) ; 2) généraliser les sous-fonctions sous forme d éléments simples et d opérateurs de combinaison (où combinateurs). La décomposition permet d introduire une structure (un modèle) intermédiaire devant simplifier l expression de u et v (donc aussi de t) et permettre leur réutilisation dans un contexte plus large, avec d autres transformations. La généralisation doit rendre plus génériques les fonctions utilisées et faciliter l expression d un élément de transformation. L interprétation des concepts IDM dans un cadre fonctionnel consiste alors à représenter un métamodèle par un type de données particulier et une transformation par une fonction entre deux types. Les deux principaux intérêts sont alors de 1) pouvoir mettre en oeuvre relativement facilement les concepts IDM dans un langage généraliste ; 2) disposer d un cadre formel pour les éléments spécifiés. Comme illustration des éléments détaillés dans la suite de cet article, la figure 1 présente trois transformations/interprétations possibles
pour un modèle d expressions arithmétiques décrits en UML et interprété dans un cadre fonctionnel (avec Haskell). Val v:int - UML - Exp parse(string):exp unparse():string unparse2():int <<instanceof>> 1:Val Add :Add left right :Add 2:Val 3:Val parse transfo "(1+2)+3" = unparse i. parse DSLs pour le Développement Agile de Transformations 2 data Exp = Val { v:: Int } Add { left,right :: Exp } Figure 1. Interprétation d un modèle UML en Haskell et exemples de transformations = - Haskell - parse = choice add val val = many digit add = seq (seq val (char '+')) exp fold f g (Val v) = f v fold f g (Add x y) = g (fold f g x) (fold f g y) unparse = fold (("PUSH "++). show) (\x y->x++y++"add") unparse2= fold id (\x y->x+y) unparse3= fold show (\x y->x++"+"++y) = Add (Add (Val 1) (Val 2)) (Val 3) unparse "PUSH 1 PUSH 2 ADD PUSH 3 ADD" unparse 2 unparse 3 "(1+2)+3" La figure 1 illustre les points importants de l approche proposée avec: - La possibilité d appliquer les éléments IDM dans un cadre fonctionnel: un métamodèle défini par une hiérarchie est interprété comme une définition de type (ici, data Exp =), les différentes (sous-)classes sont interprétées comme les constructeurs de type (ici, Val et Add), les attributs ou compositions comme les paramètres de ces constructeurs, et les opérations comme des fonctions. Un modèle correspond alors à une expression Haskell d un certain type. - La décomposition de transformations en fonctions plus simples (cf. transfo = unparse. parse, avec (.) l opérateur de composition de fonctions) et leur généralisation pour définir plus simplement d autres transformations (cf. unparse est généralisé par fold qui permet alors de définir unparse2,3). - La définition de DSLs à partir d éléments génériques ; l intégration d un modèle textuel (cf. fonction parse) est réalisée à l aide de fonctions élémentaires (char) et de combinateurs (alt, seq et many, représentés en gras ci-dessus). L article se compose de trois parties. La première partie présente l IDM avec ses concepts et les principaux outils qu elle propose. Elle introduit aussi la notion de Langage Spécifique à un Domaine (DSL). La seconde partie propose une approche dédiée au développement agile de transformations (de modèles) illustré par trois DSLs pour l intégration de modèles textuels, l extraction ou l ajout d information à un modèle, et la mise en forme d information. La dernière partie conclut en reprenant les points importants de cet article et les perspectives envisagées. 6
IDM'2008 5-6 Juin Mulhouse 2. Programmation fonctionnelle et IDM 2.1. Ingénierie Dirigées par les Modèles L Ingénierie Dirigée par les Modèles (IDM) trouve son origine dans les concepts de métamodèle et de transformation de modèles (Mellor et al., 2003). Un métamodèle décrit de façon détaillée un langage de modélisation (le plus souvent visuel) et est lui-même décrit par un graphe (diagramme des classes UML, par exemple). Une transformation est une fonction pour passer d un langage de modélisation à un autre ; elle est typiquement modélisée par un ensemble de règles entre deux métamodèles. Les métamodèles et les transformations sont décrits avec des langages dédiés. En particulier, le MOF (Meta Object Facility) ou EMF-Ecore (Eclipse Modeling Framework) sont utilisés pour décrire des métamodèles, (OMG, 2006, Eclipse, 2007). ATL (Atlas Transformation Language), JET (Java Emitter Template), ou Kermeta (Muller et al., 2005) sont utilisés pour décrire des transformations (Eclipse, 2007, Triskell, 2006). Les langages précédents sont supportés par Eclipse. Il existe d autres outils possibles: GME (Karsai et al., 2003), Xactium (Clark et al., 2004), Atom3 (Vangheluwe et al., 2003), etc. A cette liste d outils IDM, il faut ajouter les modeleurs UML qui intègrent aujourd hui des mécanismes d extension pour réaliser des langages/transformations spécifiques. Une approche complémentaire aux standards précédents et proposée par cet article consiste à explorer les possibilités d utiliser un langage fonctionnel moderne, offrant des concepts et des mécanismes de généricité puissants, dans un cadre IDM. En particulier, le langage fonctionnel Haskell est bien adapté pour prototyper des outils de transformation. Les fonctions permettent de créer des modèles (i.e. des expressions d un certain type), d accéder ou de modifier ces modèles, et présentent l avantage de pouvoir être passées en paramètre ou en retour d autres fonctions. Ce dernier point est utilisé dans l article pour proposer des DSLs dédiés au prototypage de transformations. Un DSL sera défini par un groupe de fonctions élémentaires et de combinateurs (i.e. des fonctions prenant en paramètre les fonctions précédentes ou d autres combinateurs). L ensemble des fonctions définies associé à l opérateur de composition (.) spécifie alors un langage (i.e. un ensemble d expressions). 2.2. Langages Spécifiques de Domaine et Haskell Le concept de Language Spécifique à un Domaine (DSL) repris par la communauté IDM est apparu très tôt dans les langages fonctionnels (Hudack et al., 2007). Un DSL correspond à un ensemble de types (de données) et de fonctions pour décrire/réaliser plus simplement une application (Deursen et al., 2000). Dans un cadre IDM, les types introduits par un DSL peuvent être interprétés comme des éléments d un métamodèle et les fonctions entre ces types comme des transformations. Le langage fonctionnel Haskell est bien adapté pour mettre en oeuvre des DSLs (Hudack, 1998) et il intègre aujourd hui plusieurs langages spécifiques pour modéliser des langages (Hutton et al., 1992), des documents (Wadler, 2003), des commandes logicielles (Thiry et al., 2006), etc. Haskell présente plusieurs avantages dans un cadre IDM dont les plus importants sont donnés par (Mathaikutty, 2005). En particulier, il repose sur des concepts peu
nombreux, bien formalisés, et permettant de décrire simplement une information ou un traitement complexe. Parmi ces concepts, les fonctions sont les entités de première espèce: elles sont regroupées pour définir des types (de données) éventuellement récursifs et paramétrés, et peuvent être passées en paramètre ou en retour d autres fonctions. La définition d un type est similaire à la spécification d une grammaire, i.e. un ensemble d expressions et un langage représentant les valeurs possibles pour ce type ; cf. Figure 1. Enfin, Haskell privilégie un style déclaratif: les fonctions peuvent être définies de façon compacte et intelligible par filtrages de motifs (Pattern Matching). 2.3. Haskell comme métalangage Haskell (Bird et al., 1998) est un langage fonctionnel typé. Un type correspond simplement à un ensemble de fonctions (appelées constructeurs) pour construire des expressions et des valeurs pour ce type. Par exemple, le type List est défini cidessous ; de la même manière, le modèle de la figure 1 définit un arbre d expressions. Il faut noter que la plupart des langages fonctionnels intègrent le concept de liste (ou de collection) et un ensemble de fonctions facilitant leur traitement. En particulier, le type String utilisé par les langages de programmation pour représenter du texte correspond à une liste de caractères (i.e. List Char) ; toutes les fonctions sur les listes présentées plus bas sont donc applicables aux Strings et aux textes. data List a = Nil Cons a (List a) -- liste générique produit Nil = 1 produit (Cons x xs) = x * produit xs DSLs pour le Développement Agile de Transformations Cette définition générique, paramétrée par le type a, spécifie une valeur (i.e. Nil à le type List a, ce qui se note aussi Nil :: List a) et une fonction prenant en paramètre un élément de type a, et une liste de a et retournant une liste de a (Cons :: a -> List a -> List a). En fait, la fonction Cons peut être appliquée à 0 paramètre (elle se manipule alors comme une variable et peut être passée en paramètre ou en retour d autres fonctions), 1 paramètre x (dans ce cas, Cons x est une fonction qui ajoute x au début d une liste), ou 2 paramètres x et xs (l expression Cons x xs est alors une valeur de type List a, a étant le type de x). La définition de fonctions sur un type donné utilise alors le filtrage de motifs, ou pattern matching, sur les constructeurs (cf. exemple de produit ci-dessus). Dans un contexte IDM, Haskell peut être utilisé comme métalangage. Pour cela, un modèle est défini par une expression du langage ; un langage de modélisation est un type (un ensemble d expressions) ; un métamodèle est une définition de type (data T =...) ; une transformation est une fonction entre deux types et est définie par un ensemble de règles (i.e. pattern matching). A partir de cette description minimale du langage Haskell, la partie suivante propose une approche permettant de trouver les types et les fonctions pertinentes pour décrire/réaliser une transformation de modèles. Les éléments présentés sont regroupés sous la forme de DSLs pour intégrer des modèles textuels aux transformations ou pour extraire/ajouter/formater une information particulière.
IDM'2008 5-6 Juin Mulhouse 3. Développement de transformations 3.1. Méthodologie utilisée Dans un contexte IDM, une transformation correspond à une fonction t entre deux langages de modélisation M et N (t :: M -> N). La première étape proposée consiste alors à décomposer t en deux, ou plusieurs, fonctions plus simples u et v (i.e. t = u. v, avec (.) la composition de fonctions). Cette règle est largement utilisée pour exprimer plus simplement des fonctions/transformations: elle permet d introduire une structure/modèle intermédiaire qui facilite les traitements. Trois illustrations de ce principe sont données par la fonction factorielle, le tri rapide (quicksort) et la compilation de programme: factoriel = produit. (enumfromto 1) quicksort = flatten. btree compile = unparse. parse La fonction enumfromto x y retourne la liste des valeurs comprises entre x et y ; la fonction produit a été donnée précédemment. La fonction btree construit un arbre binaire de recherche ; la fonction flatten donne une représentation infixe d un arbre binaire sous la forme d une liste. Ces fonctions sont données ci-dessous. La fonction parse construit un arbre syntaxique à partir d une liste de caractères et unparse donne une représentation particulière de cet arbre (ces deux fonctions sont décrites à l aide de fonctions génériques et sont réalisées avec les DSLs proposés dans la suite de l article). enumfromto x y x <= y = Cons x (enumfromto (x+1) y) otherwise = Nil data BTree a = Leaf -- arbre binaire de recherche générique Node a (BTree a) (BTree a) flatten :: BTree a -> List a flatten Leaf = Nil flatten (Node x l r) = plus l (plus (unit x) r) where plus xs ys = -- voir plus bas unit x = Cons x Nil btree :: List a -> BTree a btree Nil = Leaf btree (Cons x xs) = Node x l r where l = filter (<=x) xs r = filter (>x ) xs
DSLs pour le Développement Agile de Transformations La figure 2 montre une application de ces différentes fonctions. 3 enumfromto 1 Cons produit * reduction 6 1 Cons 1 * 2 Cons 2 * 3 Nil factoriel btree [5,3,4,2] Node 3 flatten 0 [2,3,4,5] Node 5 Leaf Node 3 Node quicksort Leaf 2 Leaf Leaf Figure 2. Vision IDM d algorithmes usuels (calcul de factoriel et quicksort) dans laquelle les structures sont considérées comme des modèles. La deuxième étape proposée consiste à généraliser, et à rendre plus génériques, les fonctions u et v en introduisant des paramètres. En particulier, la fonction fold cidessous est obtenue en ajoutant une fonction f et une valeur v à la fonction produit (et produit = fold (*) 1), cf. figure 1. La fonction générique fold permet alors de définir simplement la plupart des fonctions sur les listes (Hutton, 1998) ; quelques exemples sont donnés ci-dessous. Et il est possible de définir une fonction fold pour la plupart des structures usuelles (listes, arbres, graphes, etc.). Par exemple, la fonction flatten est une application de la fonction fold pour les arbres binaires. De manière analogue, il est possible de définir une fonction unfold (inverse de fold) dont les applications sont données par enumfromto ou btree. 4 Leaf fold f v Nil = v fold f v (Cons x xs) = f x (fold f v xs) plus xs ys = (fold Cons ys) xs concat = fold plus Nil a Cons b Cons Nil (fold f v) fold vue comme une transformation de modèles a b f f v filter p map f = fold f Nil where f x xs = if p x then Cons x xs else xs = fold (Cons. f) Nil Dans les exemples précédents, la fonction filter permet d extraire d une structure, et donc aussi d un modèle, des éléments qui vérifient un prédicat p :: a -> Bool. La fonction map applique une fonction f :: a -> b à tous les éléments de cette structure. La fonction plus permet de fusionner deux structures et la fonction concat est une généralisation de l opérateur binaire (.) en un opérateur n- aire. Une approche alternative pour définir les fonctions u et v consiste à définir un ensemble de fonctions élémentaires et des combinateurs pour former un DSL (Hutton, 1998). Ceci représente la troisième étape proposée par notre approche. La
IDM'2008 5-6 Juin Mulhouse fonction fold et l opérateur de composition (.) sont des cas particuliers de combinateurs utilisables pour toute fonction. Comme illustration, la fonction séquence, définie par sequence = fold (.) id, permet d appliquer une liste de fonctions à un élément (i.e. sequence [f,g,h] x == f(g(h(x)))). Après cette explication de la méthode utilisée, les trois paragraphes suivants décrivent des combinateurs qui permettent l intégration de modèles textuels (basés sur le type Parser), l extraction d information (cf. type Filter) ou la mise en forme d information (cf. type Layout). 3.2. DSL pour les modèles textuels (DSL1) La fonction parse précédente retourne, à partir d une liste de caractères, un élément a et une liste de caractères (Hutton et al., 1992) ; le résultat est donc une paire d éléments et est notée (x,y) dans la suite. Ce type de fonctions se définit par: type String = List Char type Parser a = String -> List (a,string) Dans la définition précédente, une fonction de type Parser retourne une liste pour pouvoir représenter les échecs d un parse ; i.e. un parse impossible retournera Nil sinon il retournera une liste de résultats. A partir de là, un parser élémentaire consiste à récupérer un caractère c et est défini par: fail = Nil char :: Char -> Parser Char char c Nil = fail char c (Cons x xs) x == c = unit (c,xs) otherwise = fail "abc" "bcd" (char 'a') (char 'a') [ ('a',"bc") ] [ ] fail Les principaux combinateurs utilisés dans le domaine du grammarware sont la composition séquentielle de parsers (seq), l alternative (alt) et la récursion (many). Avec la définition de Parser, ces combinateurs deviennent: alt p q xs = plus (p xs) (q xs) seq p f xs = concat (map g (p xs)) where g (r,xs ) = f r xs many p = alt (seq p f) unitp Nil where f x = seq (many p) (g x) g x xs = unitp (Cons x xs) unitp x xs = unit (x,xs) epsilon xs = unitp () (char 'a') "bcd" (char 'b') (char 'a') "abc" [ ] plus (alt (char 'a') (char 'b')) [ ('b',"cd") ] [ ('b',"cd") ] [ ('a',"bc") ] (seq (char 'a') (char 'b')) (char 'b') [ ('b',"c") ] Il faut noter que les éléments proposés sont compacts, entièrement définis et permettent d intégrer une description textuelle en entrée d une transformation. Comme illustration, une fonction pour parser une lettre (en BNF letter := a b.. Z ) puis un texte (string) s écriront avec les éléments proposés: letter = fold alt (map char [ a.. Z ])
DSLs pour le Développement Agile de Transformations string = many letter Par exemple, string abc<d> retourne unit ( abc, <d> ). Il faut noter que les éléments ci-dessus profitent de ceux déjà présentés: l alternative (alt) consiste à utiliser la somme de deux listes (plus) représentant les deux résultats possibles ; la fonction map est utilisée pour transformer des caractères x en parser de x ; fold est utilisé pour généraliser alt, etc. Les parsers sont généralement associés à un arbre (syntaxique). Par exemple, l intégration de modèles XML en entrée d une transformation consiste à définir un modèle d arbre (défini ci-dessous) puis la structure d un document XML (i.e. sa grammaire) à l aide du DSL1 (= {char, alt, seq, many}), (Wallace et al., 1999). L intérêt de cet exemple est qu il est possible d intégrer des modèles standards (UML/XMI ou EMF) en entrée des transformations ; en particulier, ils pourront être transformés à l aide des DSL 2 et 3 présentés dans la suite de cet article (la partie 3.5 présente une illustration de ce principe). data Tree a = a a (List (Tree a)) -- arbre générique La grammaire du sous-ensemble XML considéré et son interprétation avec les éléments proposés est donnée par: xml :: Parser (Tree String) xml = alt text group text = seq (many (alt letter special)) f where f x = x group = seq (char < ) f where f x = seq string g g x = seq (char > ) (h x) h x = seq (many xml) (i x xs)... i x xs = x xs La fonction parse présentée sur la figure 1 pour les expressions arithmétiques est décrite de la même manière que la fonction xml ci-dessus. L inverse de la fonction xml est alors analogue à la fonction flatten et s écrit simplement: unparse = foldt t g where foldt t g ( x) = t x foldt t g ( x xs) = g x xs t x = x g x xs = concat [ <,x, >,concat xs, </,x, > ] L intérêt d utiliser la fonction générique foldt (pour fold Tree) est qu elle permet de dériver plus facilement d autres transformations en adaptant les paramètres t et g. En particulier, pour supprimer les balises <XML> et ne garder que le texte (respectivement pour ne garder que les balises), il suffit de remplacer la définition de g par g x xs = concat xs ; cf. unparse2 ci-dessous (respectivement unparse4). D autres transformations sont bien sur envisageables (cf. compile3 par exemple). Cela dit, il est aussi possible de proposer un DSL2 spécifique pour extraire
IDM'2008 5-6 Juin Mulhouse de l information d un arbre ou ajouter de nouveaux éléments. De la même manière, il est possible de proposer un DSL3 pour mettre en forme de l information et la rendre plus intelligible. Pour conclure cette présentation du DSL1 obtenu avec l approche proposée, la figure 3 donne une application des éléments précédents sur un exemple. <html> <head> <Title>Sample</Title> </head> <body> <B>Chapter</B> <I>Paragraph</I> Here </body> </html> compile 2 unparse 2 Sample, Chapter Paragraph, Here un/parse 1 head Figure 3. Illustration des transformationsi (unparsei. parse) 3.3. DSL pour l ajout et la suppression d information (DSL2) La représentation arborescente utilisée précédemment peut être associée à la notion de filtre (type Filter ci-dessous) qui est à l origine du deuxième DSL proposé. DSL2, comme DSL1, se compose d éléments simples et de combinateurs. Un filtre est défini ici par une fonction qui prend un arbre et retourne une liste d arbres (les sous-arbres par exemple). Les filtres de base (leaf, is et children) sont utilisés pour extraire une feuille, un noeud particulier ou des sous-arbres. type Filter a = Tree a -> List (Tree a) leaf, children :: Filter a leaf ( x) = unit ( x) leaf ( x xs) = fail is :: String -> Filter a is s ( x) = fail is s ( x xs) x == s = unit ( x xs) otherwise = fail children ( x) = fail children ( x xs) = xs title Sample compile 3 html B Chapter body I Paragraph (html (head (Title Sample)) (body (B Chapter) (I Paragraph) Here)) Here unparse 4 html, head, Title body, B, I Comme pour les parsers, les principaux combinateurs portent sur la composition séquentielle (and), les alternatives (or) et la récursion (deep). Ils sont définis simplement par le code suivant ; il faut noter la similitude entre les éléments de ce DSL et ceux du DSL1 (and seq, or alt, deep many). B A D C E children B [ C, ] D E
DSLs pour le Développement Agile de Transformations or f g t = plus (f t) (g t) and f g t = concat (map g (f t)) deep f = orelse f (and children (deep f)) where orelse f g t = case f t of fail -> g t otherwise -> f t B A D C (deep leaf) [ B, D, E ] E Ce deuxième DSL permet d exprimer des requêtes (Lammel, 2007) pour extraire une information particulière dans un arbre, et un modèle hiérarchique. En particulier, la requête pour obtenir tous les noeuds x d un arbre est donnée simplement par find x = deep (is x) qui peut aussi s écrire find = deep. is. Le DSL peut être complété par la fonction suivante qui permet d ajouter de l information à un arbre ; plus précisément, elle prend une fonction f qui transforme un arbre en un autre. tree :: (Tree a -> Tree a) -> Filter a tree f t = unit (f t) Comme illustration, le filtre suivant permet de décorer le texte d un document XML par la balise <> ; son fonctionnement est décrit sur la figure 4. complete = deep. (tree f) where f (Leaf x) = Node (unit (Leaf x)) f (Node x xs) = Node x (map f xs) La transformation de la figure 4 se compose de deux parties (cf. and): la première élimine les sous-arbres dont la racine est head (cf. notis) et la seconde décore les feuilles avec la balise (cf. complete). L intérêt de ce second DSL est qu il s intègre parfaitement au précédent. Par exemple, la fonction compile2 de la figure 3 peut se réécrire plus simplement par (deep is). xml. B A C (tree f) f (Node n cs) = Node n [Node "X" cs] B A X C html transformation html head body body title B I Here B I Sample Chapter Paragraph Here transformation = deep (and (notis "head") complete) Chapter Paragraph Figure 4. Transformation retirant/ajoutant de l information à un modèle
IDM'2008 5-6 Juin Mulhouse 3.4. DSL pour la mise en forme d information (DSL3) Pour montrer la généricité de l approche proposée, un troisième exemple de DSL pour la mise en forme d information est présenté. Pour cela, un document (cf. type Layout) est défini comme une liste/séquence de textes ayant chacun une indentation (Wadler, 2003). La fonction de base est donnée par la fonction paragraph qui convertit un texte en document ; les combinateurs down et right permettent de combiner verticalement et horizontalement deux documents. Comme les DSLs 1 et 2 déjà présentés, ces fonctions sont définies entièrement et complètement (i.e. tous les éléments nécessaires à leur mise en oeuvre sont donnés dans l article) par : type Layout = List (Int,String) down tostring paragraph t = unit (0,t) paragraph A right down p q = plus p q right Nil q = q paragraph B paragraph C right p q = plus p r where pos = maximum (map size p) size (x,t) = x + length t r = map (move pos) q move p (x,t)= (x+p,t) B A C tostring :: Layout -> String La fonction transformation peut alors être améliorée en remplaçant le texte généré par des Layout puis en appliquant la transformation tostring qui donne la représentation textuelle d un document. unparse = tostring. (foldt t g) where t x = paragraph x g x xs = down (right (pre x) xs) (post x) pre x = plus < (plus x > ) post x = plus < (plus x /> ) Ce troisième DSL permet ainsi de définir des modèles de templates (similaires à ceux trouvés dans des technologies comme JET (Java Emitter Template), par exemple) décrivant des formats de présentation de données/modèles ; ces modèles de format sont utiles dans le cadre d une approche générative pour passer d un modèle à un code exécutable ou vérifiable (cf. section suivante). Le DSL1 permet alors d établir des transformations inverses pour passer d un modèle textuel concret à un modèle plus abstrait ; l exemple d arbres syntaxiques est proposé comme illustration de cet article mais d autres modèles sont envisageables avec, en particulier, des modèles de graphes. L objectif du DSL2 est alors de filtrer ou décorer la structure intermédiaire utilisée en sortie du DSL1 et en entrée du DSL3. Ainsi, le guide proposé consistant à décomposer une fonction de type transformation de modèles en deux ou plusieurs sous-fonctions puis à exprimer chacune d elles à l aide d éléments génériques composés de primitives et de combinateurs permet de construire des DSLs utilisables pour décrire une large
IDM'2008 5-6 Juin Mulhouse DSL a été utilisé pour mettre en forme l information au format fsp utilisé par le model checker LTSA (Maggee et al., 2006) ; cf. fonction fsp dans le code suivant. trs, sts :: Filter trs = and (find "UML:StateMachine.transitions") (find "UML:Transition") uml2fsp in = do xmi <- readfile in let model = xml xmi -- Compilation transitions = trs model -- Extraction... -- Mise en forme result = fsp transitions states -- Generation in writefile (in++.lts ) res Comme illustration, le modèle UML de la figure 5 présente un Statechart partiel pour un contrôleur de train arrivant à passage à niveau et le résultat de la transformation réalisée. 4. Conclusion Cet article propose une interprétation des concepts IDM dans un cadre fonctionnel pour développer de manière agile, et prototyper rapidement, des transformations de modèles. Pour cela, une transformation est associée à un ensemble de fonctions génériques plus simples, partitionné sous forme de Langages Spécifiques de Domaines (DSL). En effet, les fonctions proposées pour définir des transformations sont organisées en éléments simples et en combinateurs permettant de décrire les différents composants d une transformation. Les DSLs proposés définissent les éléments essentiels pour décrire/réaliser une fonctionnalité à l intérieur d une transformation avec l intégration ou la génération de modèles textuels, et le filtrage ou l ajout d information à un modèle. Chaque DSL présenté dans cet article comme illustration de l approche proposée est défini complètement: l ensemble des éléments (primitives + combinateurs) nécessaires à la mise en oeuvre des DSL est donné. L utilisation d un cadre fonctionnel permet d apporter plus d abstraction et plus de rigueur dans le développement ou l expression de transformation ; en particulier, il existe plusieurs travaux décrivant comment établir les propriétés des fonctions (calcul de complexité et optimisation, par exemple), et avec l approche proposée il est possible de profiter de ces travaux. Ce dernier point fait partie des perspectives envisagées pour nos travaux. Les deux autres directions envisagées pour la suite de ces travaux consistent à étudier comment intégrer les éléments présentés aux standards existants (i.e. pouvoir lire et manipuler des modèle Eclipse EMF, par exemple) puis à identifier les autres DSLs utiles aux transformations. Ces travaux futurs doivent apporter un point de vue complémentaire sur l IDM.
DSLs pour le Développement Agile de Transformations 5. Bibliographie Bird R., Scruggs T.E., Mastropieri M.A., 1998, Introduction to Functional Programming using Haskell, Prentice Hall Eds Clark T., Evans A., Sammut P., Willans J.,, An executable Metamodelling Facility for Domain Specific Language Design, in the 4th Domain-Specific Modeling Workshop, DSM 04, p. 103-110 Deursen A., Klint P., Visser J., 2000, Domain Specific Languages: an Annoted Bibliography, SIGPLAN Notices, Vol. 35, N 6, p. 26-36 Eclipse Modeling Project, 2007, EMF, JET, and ATL, http://www.eclipse.org/modeling/ Hudack P., Hughes J., Peyton-Jones S., Wadler P.A., 2007, History of Haskell: Being Lazy with Class, 3rd ACM SIGPLAN on History of Programming Languages, p. 1-55 Hudack P., 1998, Modular Domain Specific Languages and Tools, In the 5th International Conference on Software Reuse, IEEE Computer Society Eds, p. 134-143 Hutton G., 1998, A tutorial on the universality and expressiveness of fold, In Journal of Functional Programming, Vol. 9, n 4, p. 355-372 Hutton G., Meijer E., 1992, Monadic Parser Combinators, In Journal of Functional Programming, Vol 2, n 3, p. 323-343 Karsai G., Sztipanovits J., Ledeczi A., Bapty T., 2003, Model-Integrated Development of Embedded Software, Proceedings of the IEEE, Vol. 91, n 1, p. 145-164 Lammel R., 2007, Scrap your Boilerplate with XPath-like Combinators, In ACM SIGPLAN- SIGACT Symposium on Principles of Programming Languages, POPL 07 Magee J., Kramer J., 2006, Concurrency: State Models & Java Programs, John Wiley and Sons Eds., Chichester, UK Mathaikutty D.A., 2005, Functional Programming and Metamodeling Frameworks for System Design, PhD Thesis of the Faculty of Virginia Polytechnic Institute and State University, USA Mellor S.J., Clark A.N., Futagami T., 2003, Guest Editors Introduction: Model-driven development, IEEE Software, Vol. 20, n 5, p. 14-18 Muller P.A., Fleurey F., Jézéquel J.M., Weaving Executability into Object-Oriented Meta- Languages, MODELS 05, octobre 2005, Kingston Okalas D.D., Mota J.M., Thiry L., Perronne J.M., Boulanger J.L., Mariano G., 2007, A method to Model Guidelines dor Developping Railway Safety Critical Systems with UML, International Conference on Software and Data Technologies, ICSOFT 07, Barcelone, Spain Object Management, 2006, Meta Object Facility Core Specification Version 2.0, www.omg.org/mof/ Thiry L., Thirion B., 2008, Functional (Meta)Models for the Development of Control Software, International Federation of Automatic Control world congress, IFAC 08, Seoul, 6-11 juillet, à paraître Thiry L., Perronne J.M., Thirion B., 2006, IDM pour une conception intégrée des logiciels de commande, Conférence Internationale Francophone d Automatique, CIFA 06, Bordeaux, France
IDM'2008 5-6 Juin Mulhouse Triskell project (IRISA), 2006, The metamodeling language Kermeta, www.kermeta.org Vangheluwe H., de Lara J., 2003, Foundations of multi-paradigm modeling and simulation: computer automated multi-paradigm modeling, 35th Conference on Simulation: driving innovation, p. 593-603 Wadler P., 2003, A prettier printer, In the Fun of Programming, p. 223-244 Wallace M., Runciman C., 1999, Haskell and XML: Generic Combinators or Type-Based Translation?, In the 4th International Conference on Functional Programming, ACM Eds