Liaison de fonction dynamique (ou pas)

Voir aussi : vidéo peertube - vidéo youtube - dépôt git

Comment appeler des fonctions sur des collections hétérogènes. Exemple : type Forme + sous-types Carre et Rectangle, fonctions surface.

Duck typing

#!/usr/bin/env python3

class Carre:
    def __init__(self, c):
        self.c = c

    def surface(self):
        return self.c * self.c

class Rectangle:
    def __init__(self, w, h):
        self.w = w
        self.h = h

    def surface(self):
        return self.w * self.h

formes = [Carre(2), Rectangle(2, 3)]

for f in formes:
    # duck typing :
    # appelle la méthode surface de Carre ou de Rectangle, selon le type de f
    print(f.surface())

Introspection

#!/usr/bin/env python3

class Carre:
    def __init__(self, c):
        self.c = c

    def surface(self):
        return self.c * self.c

class Rectangle:
    def __init__(self, w, h):
        self.w = w
        self.h = h

    def surface(self):
        return self.w * self.h

formes = [Carre(2), Rectangle(2, 3), 42]

for f in formes:
    # introspection : teste si surface est une méthode de f
    if 'surface' in dir(f):
        print(f.surface())

Typage dynamique

#!/usr/bin/env python3

class Carre:
    def __init__(self, c):
        self.c = c

    def surfaceCarre(self):
        return self.c * self.c

class Rectangle:
    def __init__(self, w, h):
        self.w = w
        self.h = h

    def surfaceRectangle(self):
        return self.w * self.h

formes = [Carre(2), Rectangle(2, 3), 42]

for f in formes:
    # typage dynamique :
    # teste si f est de type Rectangle
    if isinstance(f, Rectangle):
        print(f.surfaceRectangle())
    # teste si f est de type Carre
    if isinstance(f, Carre):
        print(f.surfaceCarre())

Multiple dispatch / multiméthodes

#!/usr/bin/env julia

struct Carre
  c :: Int
end

struct Rectangle
  w :: Int
  h :: Int
end

# définition multiple de fonction
surface(f :: Carre) = f.c * f.c
surface(f :: Rectangle) = f.w * f.h
surface(f1 :: Carre, f2 :: Rectangle) = surface(f1) + surface(f2)

formes = [Carre(2), Rectangle(2, 3)]

# multiple dispatch : 
# appelle la fonction correspondant aux paramètres
for f in formes
  println(surface(f))
end

println(surface(Carre(2), Rectangle(2, 3)))

Héritage avec fonctions virtuelles

#include <iostream>
#include <memory>
#include <vector>
using namespace std;

// type principal
struct Forme {
  // méthode redéfinie dans les sous-types
  virtual double surface() const = 0;
};

struct Carre : Forme {
  double _c;
  Carre(double c) : _c(c) {}
  double surface() const override { return _c * _c; }
};

struct Rectangle : Forme {
  double _w, _h;
  Rectangle(double w, double h) : _w(w), _h(h) {}
  double surface() const override { return _w * _h; }
};

int main() {

  // collection de Carre/Rectangle
  vector<unique_ptr<Forme>> formes;
  formes.push_back(make_unique<Carre>(2));
  formes.push_back(make_unique<Rectangle>(2, 3));

  for (const auto & f : formes) {
    // appelle la méthode correspondant au sous-type
    cout << f->surface() << endl;
  }

  return 0;
}

En Haskell

import Data.Dynamic

data Carre = Carre Double

surfaceCarre :: Carre -> Double
surfaceCarre (Carre c) = c*c

data Rectangle = Rectangle Double Double

surfaceRectangle :: Rectangle -> Double
surfaceRectangle (Rectangle w h) = w*h

-- liste hétérogène (Dynamic représente le type universel)
formes :: [Dynamic]
formes = [ toDyn (Carre 2), toDyn (Rectangle 2 3) ]

-- calcule la surface d'une valeur inclu dans un Dynamic, si possible 
dynToSurface :: Dynamic -> Maybe Double
dynToSurface x = case fromDynamic x :: Maybe Carre of
    Just c -> Just $ surfaceCarre c
    Nothing -> case fromDynamic x :: Maybe Rectangle of
        Just r -> Just $ surfaceRectangle r
        Nothing -> Nothing

main = print (map dynToSurface formes)
{-# LANGUAGE ExistentialQuantification #-}

class Surfacable a where
  surface :: a -> Double

data Carre = Carre Double
instance Surfacable Carre where
  surface (Carre c) = c*c

data Rectangle = Rectangle Double Double
instance Surfacable Rectangle where
  surface (Rectangle w h) = w*h

-- type existentiel :
-- empaquette des données de classe Surfacable
data MkSurfacable = forall a . (Surfacable a) => MkSurfacable a

-- instancie la classe Surfacable pour le type existentiel (pas obligatoire)
instance Surfacable MkSurfacable where
   surface (MkSurfacable x) = surface x

-- liste hétérogène de valeurs de classe Surfacable
surfacables :: [MkSurfacable]
surfacables = [ MkSurfacable (Carre 2), MkSurfacable (Rectangle 2 3) ]

main = do
  -- avec instance de Surfacable
  print $ map surface surfacables
  -- sans instance de Surfacable
  print $ map (\(MkSurfacable x) -> surface x) surfacables
-- type algébrique
data Forme = Carre Double
           | Rectangle Double Double

-- fonction traitant toutes les valeurs possibles du type
surface :: Forme -> Double
surface (Carre c) = c*c
surface (Rectangle w h) = w*h

formes :: [Forme]
formes = [Carre 2, Rectangle 2 3]

main = print $ map surface formes