• Aucun résultat trouvé

On vient de regarder des modules plutôt cool, mais comment crée-t-on nos propres modules ? Presque tous les langages de programmation vous permettent de découper votre code en plusieurs fichiers et Haskell également. Lorsqu’on programme, il est de bonne pratique de prendre des fonctions et des types qui partagent un but similaire et de les placer dans un module. Ainsi, vous pouvez facilement réutiliser ces fonctions plus tard dans d’autres programmes juste en important le module.

Voyons comment faire nos propres modules en créant un petit module fournissant des fonctions de calcul de volume et d’aire d’objets géométriques. Commençons par créer un fichier Geometry.hs. On dit qu’un module exporte des fonctions. Cela signifie que quand j’importe un module, je peux utiliser les fonctions que celui-ci exporte. Il peut définir des fonctions que ses propres fonctions appellent en interne, mais on peut seulement voir celles qu’il a exportées.

Au début d’un module, on spécifie le nom du module. On a créé un fichier Geometry.hs, nous devrions donc nommer notre module Geometry. Puis, nous spécifions les fonctions qu’il exporte, et après cela, on peut commencer à écrire nos fonctions. Démarrons.

module Geometry ( sphereVolume , sphereArea , cubeVolume , cubeArea , cuboidArea , cuboidVolume ) where

Comme vous pouvez le voir, nous allons faire des aires et des volumes de sphères, de cubes et de pavés droits. Définissons nos fonctions :

module Geometry ( sphereVolume , sphereArea , cubeVolume , cubeArea , cuboidArea , cuboidVolume ) where

sphereVolume :: Float -> Float

sphereVolume radius = (4.0 / 3.0) * pi * (radius ^ 3)

sphereArea :: Float -> Float

sphereArea radius = 4 * pi * (radius ^ 2)

cubeVolume :: Float -> Float

cubeVolume side = cuboidVolume side side side

cubeArea :: Float -> Float

cubeArea side = cuboidArea side side side

cuboidVolume :: Float -> Float -> Float -> Float

cuboidVolume a b c = rectangleArea a b * c

cuboidArea :: Float -> Float -> Float -> Float

cuboidArea a b c = rectangleArea a b * 2 + rectangleArea a c * 2 + rectangleArea c b * 2

rectangleArea :: Float -> Float -> Float

rectangleArea a b = a * b

De la géométrie élémentaire. Quelques choses à noter tout de même. Puisqu’un cube est un cas spécial de pavé droit, on a défini son aire et son volume comme ceux d’un pavé dont les côtés ont tous la même longueur. On a également défini la fonction auxiliaire rectangleArea, qui calcule l’aire d’un rectangle à partir des longueurs de ses côtés. C’est plutôt trivial puisqu’il s’agit d’une simple multiplication. Remarquez comme on l’utilise dans nos fonctions de ce module (dans cuboidArea et cuboidVolume ), mais on ne l’exporte pas ! On souhaite que notre module présente des fonctions de calcul sur des objets en trois dimensions, donc on n’exporte pas rectangleArea.

Quand on crée un module, on n’exporte en général que les fonctions qui agissent en rapport avec l’interface de notre module, de manière à cacher

l’implémentation. Si quelqu’un utilise notre module Geometry, il n’a pas à se soucier des fonctions que l’on n’a pas exportées. On peut décider de changer ces fonctions complètement ou de les effacer dans une nouvelle version (on pourrait supprimer rectangleArea et utiliser * à la place) et personne ne s’en souciera parce qu’on ne les avait pas exportées.

Pour utiliser notre module, on fait juste :

import Geometry

Geometry.hs doit tout de même être dans le même dossier que le programme qui souhaite l’importer.

Les modules peuvent aussi être organisés hiérarchiquement. Chaque module peut avoir un nombre de sous-modules et eux-mêmes peuvent avoir leurs sous- modules. Découpons ces fonctions de manière à ce que Geometry soit un module avec trois sous-modules, un pour chaque type d’objet.

D’abord, créons un dossier Geometry. Attention à la majuscule à G. Dans ce dossier, placez trois dossiers : Sphere.hs, Cuboid.hs et Cube.hs (NDT : “Cuboid” signifie pavé droit). Voici ce que les fichiers contiennent :

Sphere.hs

module Geometry.Sphere ( volume

, area ) where

volume :: Float -> Float

volume radius = (4.0 / 3.0) * pi * (radius ^ 3)

area :: Float -> Float

area radius = 4 * pi * (radius ^ 2)

Cuboid.hs

module Geometry.Cuboid ( volume

, area ) where

volume :: Float -> Float -> Float -> Float

volume a b c = rectangleArea a b * c

area :: Float -> Float -> Float -> Float

area a b c = rectangleArea a b * 2 + rectangleArea a c * 2 + rectangleArea c b * 2

rectangleArea :: Float -> Float -> Float

rectangleArea a b = a * b Cube.hs module Geometry.Cube ( volume , area ) where

import qualified Geometry.Cuboid as Cuboid

volume :: Float -> Float

volume side = Cuboid.volume side side side

area :: Float -> Float

area side = Cuboid.area side side side

Parfait ! Tout d’abord, nous avons Geometry.Sphere. Remarquez comme on l’a placé dans le dossier Geometry puis nommé Geometry.Sphere. Idem pour le pavé. Remarquez aussi comme dans chaque sous-module, nous avons défini des fonctions avec le même nom. On peut le faire car les modules sont séparés. On veut utiliser des fonctions de Geometry.Cuboid dans Geometry.Cube, mais on ne peut pas simplement import Geometry.Cuboid parce que ce module exporte des fonctions ayant le même nom que celles de Geometry.Cube. C’est pourquoi l’import est qualifié, et tout va bien.

Donc maintenant, si l’on se trouve dans un fichier qui se trouve au même niveau que le dossier Geometry, on peut par exemple :

import Geometry.Sphere

Et maintenant, on peut utiliser area et volume, qui nous donneront l’aire et le volume d’une sphère. Et si l’on souhaite jongler avec deux ou plus de ces modules, on doit utiliser des imports qualifiés car ils exportent des fonctions avec des noms identiques. Tout simplement :

import qualified Geometry.Cuboid as Cuboid

import qualified Geometry.Cube as Cube

Et maintenant, on peut appeler Sphere.area, Sphere.volume, Cuboid.area, etc. et chacune calculera l’aire ou le volume de l’objet correspondant.

La prochaine fois que vous vous retrouvez en train d’écrire un fichier très gros avec plein de fonctions, essayez de voir lesquelles partagent un but commun et si vous pouvez les regrouper dans un module. Vous n’aurez plus qu’à importer ce module si vous souhaitez réutiliser ces fonctionnalités dans un autre programme.