Type Classes, Traits, Implicits...

Bigre !

Frédéric Cabestre
@fcabestre
I'm not a number, I'm a freelance.
— Number 6, The Prisoner
Making Ad-Hoc Polymorphism Less Ad-Hoc
Making Ad-Hoc Polymorphism Less Ad-Hoc

Polymorphisme(s)

Inclusion

                            // C#  
            
                            interface Shape
                            {
                                public double Area { get; }
                            }
                        
                            class Square : Shape {
                                public double Side { get; set; }
                                public double Area => Side * Side;
                            }    
                                            
                            class Circle : Shape {
                                public double Radius { get; set; }
                                public double Area => Radius * Radius * Math.PI;
                            }
                                
                            shapes.Aggregate(0d, (acc, shape) => acc + shape.Area);    
           

Polymorphisme(s)

Paramétrique

                            (* Objective Caml *)
                                
                            # let rec map f l = match l with
                              | h :: t -> (f h) :: (map f t)
                              | nil -> nil
                              ;;
                            val map : ('a -> 'a) -> 'a list -> 'a list = <fun>                    
            

                            // Java
                                
                            public class Packet<T extends Serializable> : {
                                payload : T;
                                header : Header;
                                ...
                            }
            

Polymorphisme(s)

Ad Hoc [Source Wikipédia]

                            (* Pascal *)
                            
                            program Adhoc;
                            
                            function Add(x, y : Integer) : Integer;
                            begin
                                Add := x + y
                            end;
                            
                            function Add(s, t : String) : String;
                            begin
                                Add := Concat(s, t)
                            end;
                            
                            begin
                                Writeln(Add(1, 2));                
                                Writeln(Add('Hello, ', 'Mammals!'));  
                            end.
                

Algorithmes Générique

  • Fil rouge
    algorithme de tri (par insertion) de listes
    constituées d'éléments d'un type arbitraire
Type Classes as Objects and Implicits

Première Itération [ C# ]

Polymorphisme Paramétrique

&

Inclusion

Paramétrique + Inclusion


                public static class Algorithm<T> where T : Ordered<T>
                {
                    public static List<T> Sort(List<T> source)
                    {
                        var result = new List<T>();
                        foreach (var s in source)
                        {
                            var position = result.FindIndex(r => r.gt(s)) switch
                            {
                                -1 => result.Count,
                                int p => p
                            };
                            result.Insert(position, s);
                        }
                        return result;
                    }
            
                    public static bool Equality(List<T> list1, List<T> list2) => 
                        list1.Count == list2.Count && list1.Zip(list2).All(p => p.First.eq(p.Second));
                }
            

Paramétrique + Inclusion


                public static class Algorithm<T> where T : Ordered<T>
                {
                    public static List<T> Sort(List<T> source)
                    {
                        var result = new List<T>();
                        foreach (var s in source)
                        {
                            var position = result.FindIndex(r => r.gt(s)) switch
                            {
                                -1 => result.Count,
                                int p => p
                            };
                            result.Insert(position, s);
                        }
                        return result;
                    }
            
                    public static bool Equality(List<T> list1, List<T> list2) => 
                        list1.Count == list2.Count && list1.Zip(list2).All(p => p.First.eq(p.Second));
                }
            

Paramétrique + Inclusion


                public static class Algorithm<T> where T : Ordered<T>
                {
                    public static List<T> Sort(List<T> source)
                    {
                        var result = new List<T>();
                        foreach (var s in source)
                        {
                            var position = result.FindIndex(r => r.gt(s)) switch
                            {
                                -1 => result.Count,
                                int p => p
                            };
                            result.Insert(position, s);
                        }
                        return result;
                    }
            
                    public static bool Equality(List<T> list1, List<T> list2) => 
                        list1.Count == list2.Count && list1.Zip(list2).All(p => p.First.eq(p.Second));
                }
            

Paramétrique + Inclusion


        public interface Ordered<in T>
        {
            bool gt(T that);
            bool eq(T that);
        }
            

        public class IntOrdered : Ordered<IntOrdered>
        {
            public int Value { get; set; }
            public bool gt(IntOrdered that) => this.Value > that.Value;
            public bool eq(IntOrdered that) => this.Value == that.Value;
        }
            

        public class StringOrdered : Ordered<StringOrdered>
        {
            public string Value { get; set; }
            public bool gt(StringOrdered that) => string.CompareOrdinal(this.Value, that.Value) > 0;
            public bool eq(StringOrdered that) => string.CompareOrdinal(this.Value, that.Value) == 0;
        }
            

Paramétrique + Inclusion


                            public void IntOrdTest()
                            {
                                var intOrds = new List<IntOrdered> {
                                    new() {Value = 1}, 
                                    new() {Value = -4}, 
                                    new() {Value = 42}, 
                                    new() {Value = 12}
                                };
                                var actual = Sort(intOrds);
                                var expected = new List<IntOrdered> {
                                    new() {Value = -4}, 
                                    new() {Value = 1}, 
                                    new() {Value = 12}, 
                                    new() {Value = 42}
                                };
                                Assert.IsTrue(Equality(actual, expected));
                            }
            

Paramétrique + Inclusion


                            public void StringOrdTest()
                            {
                                var stringOrds = new List<StringOrdered> {
                                    new() {Value = "a"}, 
                                    new() {Value = "z"}, 
                                    new() {Value = "e"}, 
                                    new() {Value = "r"}
                                };
                                var actual = Sort(stringOrds);
                                var expected = new List<StringOrdered> {
                                    new() {Value = "a"}, 
                                    new() {Value = "e"}, 
                                    new() {Value = "r"}, 
                                    new() {Value = "z"}
                                };
                                Assert.IsTrue(Equality(actual, expected));
                            }
            

Paramétrique + Inclusion

  • Liaison Dynamique
  • Extension Non Rétroactive

Deuxième Itération [ C# ]

Concept & Modèle

Concept / Modèle

  • Concept
    expression des exigences que doivent remplir les
    paramètres de type d'un algorithme générique
  • Modèle
    Mise en œuvre d'un concept pour un type donné

Concept / Modèle


                public static class Algorithm
                {
                    public static List<T> Sort<T>(List<T> source, Ordered<T> model)
                    {
                        var result = new List<T>();
                        foreach (var s in source)
                        {
                            var position = result.FindIndex(r => model.gt(r, s)) switch
                            {
                                -1 => result.Count,
                                int p => p
                            };
                            result.Insert(position, s);
                        }
            
                        return result;
                    }
            
                    public static bool Equality<T>(List<T> list1, List<T> list2, Ordered<T> model) =>
                        list1.Count == list2.Count && list1.Zip(list2).All(p => model.eq(p.First, p.Second));
                }
            

Concept / Modèle


                public static class Algorithm
                {
                    public static List<T> Sort<T>(List<T> source, Ordered<T> model)
                    {
                        var result = new List<T>();
                        foreach (var s in source)
                        {
                            var position = result.FindIndex(r => model.gt(r, s)) switch
                            {
                                -1 => result.Count,
                                int p => p
                            };
                            result.Insert(position, s);
                        }
            
                        return result;
                    }
            
                    public static bool Equality<T>(List<T> list1, List<T> list2, Ordered<T> model) =>
                        list1.Count == list2.Count && list1.Zip(list2).All(p => model.eq(p.First, p.Second));
                }
            

Concept / Modèle


                public static class Algorithm
                {
                    public static List<T> Sort<T>(List<T> source, Ordered<T> model)
                    {
                        var result = new List<T>();
                        foreach (var s in source)
                        {
                            var position = result.FindIndex(r => model.gt(r, s)) switch
                            {
                                -1 => result.Count,
                                int p => p
                            };
                            result.Insert(position, s);
                        }
            
                        return result;
                    }
            
                    public static bool Equality<T>(List<T> list1, List<T> list2, Ordered<T>) =>
                        list1.Count == list2.Count && list1.Zip(list2).All(p => model.eq(p.First, p.Second));
                }
            

Concept / Modèle


            public interface Ordered<in T>
            {
                bool gt(T l, T r);
                bool eq(T l, T r);
            }
            

            public class IntOrdered : Ordered<int>
            {
                public bool gt(int l, int r) => l > r;
                public bool eq(int l, int r) => l == r;
            }
            

Concept / Modèle


            public interface Ordered<in T>
            {
                bool gt(T l, T r);
                bool eq(T l, T r);
            }
            

            public class IntOrdered : Ordered<int>
            {
                public bool gt(int l, int r) => l > r;
                public bool eq(int l, int r) => l == r;
            }
            

            public class StringOrdered : Ordered<string>
            {
                public bool gt(string l, string r) => string.CompareOrdinal(l, r) > 0;
                public bool eq(string l, string r) => string.CompareOrdinal(l, r) == 0;
            }
            

Concept / Modèle


            public interface Ordered<in T>
            {
                bool gt(T l, T r);
                bool eq(T l, T r);
            }
            

            public class IntOrdered : Ordered<int>
            {
                public bool gt(int l, int r) => l > r;
                public bool eq(int l, int r) => l == r;
            }
            

            public class StringOrdered : Ordered<string>
            {
                public bool gt(string l, string r) => string.CompareOrdinal(l, r) > 0;
                public bool eq(string l, string r) => string.CompareOrdinal(l, r) == 0;
            }
            

Concept / Modèle


            public class PairOrdered<A , B> : Ordered<(A, B)>
            {
                public Ordered<A> modelA { get; set; }
                public Ordered<B> modelB { get; set; }
                
                public bool gt((A, B) l, (A, B) r) => 
                    modelA.gt(l.Item1, r.Item1) || 
                    modelA.eq(l.Item1, r.Item1) && modelB.gt(l.Item2, r.Item2);
                
                public bool eq((A, B) l, (A, B) r) => 
                    modelA.eq(l.Item1, r.Item1) && modelB.eq(l.Item2, r.Item2);
            }
            

Concept / Modèle


            public class PairOrdered<A , B> : Ordered<(A, B)>
            {
                public Ordered<A> modelA { get; set; }
                public Ordered<B> modelB { get; set; }
                
                public bool gt((A, B) l, (A, B) r) => 
                    modelA.gt(l.Item1, r.Item1) || 
                    modelA.eq(l.Item1, r.Item1) && modelB.gt(l.Item2, r.Item2);
                
                public bool eq((A, B) l, (A, B) r) => 
                    modelA.eq(l.Item1, r.Item1) && modelB.eq(l.Item2, r.Item2);
            }
            

Concept / Modèle


            public class PairOrdered<A , B> : Ordered<(A, B)>
            {
                public Ordered<A> modelA { get; set; }
                public Ordered<B> modelB { get; set; }
                
                public bool gt((A, B) l, (A, B) r) => 
                    modelA.gt(l.Item1, r.Item1) || 
                    modelA.eq(l.Item1, r.Item1) && modelB.gt(l.Item2, r.Item2);
                
                public bool eq((A, B) l, (A, B) r) => 
                    modelA.eq(l.Item1, r.Item1) && modelB.eq(l.Item2, r.Item2);
            }
            

Concept / Modèle


        public void IntOrdTest()
        {
            var model = new IntOrdered();
            var intOrds = new List<int> {1, -4, 42, 12};
            var actual = Sort(intOrds, model);
            var expected = new List<int> {-4, 1, 12, 42};
            Assert.IsTrue(Equality(actual, expected, model));
        }
            

Concept / Modèle


        public void IntOrdTest()
        {
            var model = new IntOrdered();
            var intOrds = new List<int> {1, -4, 42, 12};
            var actual = Sort(intOrds, model);
            var expected = new List<int> {-4, 1, 12, 42};
            Assert.IsTrue(Equality(actual, expected, model));
        }
            

Concept / Modèle


        public void IntOrdTest()
        {
            var model = new IntOrdered();
            var intOrds = new List<int> {1, -4, 42, 12};
            var actual = Sort(intOrds, model);
            var expected = new List<int> {-4, 1, 12, 42};
            Assert.IsTrue(Equality(actual, expected, model));
        }
            

        public void PairOrdTest()
        {
            var model = new PairOrdered<int, string>() { 
                modelA = new IntOrdered(), 
                modelB = new StringOrdered()
            };
            var pairOrds = new List<(int, string)> {(1, "a"), (-4, "z"), (-4, "e"), (42, "r")};
            var actual = Sort(pairOrds, model);
            var expected = new List<(int, string)> {(-4, "e"), (-4, "z"), (1, "a"), (42, "r")};
            Assert.IsTrue(Equality(actual, expected, model));
        }
            

Concept / Modèle


        public void IntOrdTest()
        {
            var model = new IntOrdered();
            var intOrds = new List<int> {1, -4, 42, 12};
            var actual = Sort(intOrds, model);
            var expected = new List<int> {-4, 1, 12, 42};
            Assert.IsTrue(Equality(actual, expected, model));
        }
            

        public void PairOrdTest()
        {
            var model = new PairOrdered<int, string>() { 
                modelA = new IntOrdered(), 
                modelB = new StringOrdered()
            };
            var pairOrds = new List<(int, string)> {(1, "a"), (-4, "z"), (-4, "e"), (42, "r")};
            var actual = Sort(pairOrds, model);
            var expected = new List<(int, string)> {(-4, "e"), (-4, "z"), (1, "a"), (42, "r")};
            Assert.IsTrue(Equality(actual, expected, model));
        }
            

Concept / Modèle

  • Extension Rétroactive
  • Mise en Œuvre Multiples
  • Liaison Dynamique
  • Bruit

Troisième Itération [ Scala ]

Arguments Implicites

Poor Man's Type Clases

Poor Mans's Type Classes

Poor Man's Type Clases

Poor Mans's Type Classes

Arguments Implicites

  • Arguments implicites
  • Valeurs et définitions implicites
  • Conversions implicites
  • Classes implicites

Pause

Cachet

Arguments Implicites

  • Arguments implicites
  • Valeurs et définitions implicites
  • Conversions implicites
  • Classes implicites

Arguments Implicites

Arguments Implicites


        object Algorithm {
          def Sort[T](source: Vector[T], model: Ordered[T]): Vector[T] = {
            val result = ArrayBuffer[T]()
            for (s <- source) {
              val position = result.indexWhere(r => model.gt(r, s)) match {
                case -1 => result.length
                case p => p
              }
              result.insert(position, s)
            }
            result.toVector
          }
        
          def Equality[T](vector1 : Vector[T], vector2 : Vector[T], model: Ordered[T]): Boolean =
            vector1.length == vector2.length && 
                vector1.zip(vector2).forall(p => model.eq(p._1, p._2))
        }
            

Arguments Implicites


        object Algorithm {
          def Sort[T](source: Vector[T])(implicit model: Ordered[T]): Vector[T] = {
            val result = ArrayBuffer[T]()
            for (s <- source) {
              val position = result.indexWhere(r => model.gt(r, s)) match {
                case -1 => result.length
                case p => p
              }
              result.insert(position, s)
            }
            result.toVector
          }
        
          def Equality[T](vector1 : Vector[T], vector2 : Vector[T])(implicit model: Ordered[T]): Boolean =
            vector1.length == vector2.length && 
                vector1.zip(vector2).forall(p => model.eq(p._1, p._2))
        }
            

Arguments Implicites


        object Algorithm {
          def Sort[T](source: Vector[T])(implicit model: Ordered[T]): Vector[T] = {
            val result = ArrayBuffer[T]()
            for (s <- source) {
              val position = result.indexWhere(r => model.gt(r, s)) match {
                case -1 => result.length
                case p => p
              }
              result.insert(position, s)
            }
            result.toVector
          }
        
          def Equality[T](vector1 : Vector[T], vector2 : Vector[T])(implicit model: Ordered[T]): Boolean =
            vector1.length == vector2.length && 
                vector1.zip(vector2).forall(p => model.eq(p._1, p._2))
        }
            

Arguments Implicites


        object Algorithm {
          def Sort[T : Ordered](source: Vector[T]): Vector[T] = {
            val result = ArrayBuffer[T]()
            for (s <- source) {
              val position = result.indexWhere(r => implicitly[Ordered[T]].gt(r, s)) match {
                case -1 => result.length
                case p => p
              }
              result.insert(position, s)
            }
            result.toVector
          }
          
          def Equality[T : Ordered](vector1 : Vector[T], vector2 : Vector[T]): Boolean =
            vector1.length == vector2.length && 
                vector1.zip(vector2).forall(p => implicitly[Ordered[T]].eq(p._1, p._2))
        } 
            

Arguments Implicites


        object Algorithm {
          def Sort[T : Ordered](source: Vector[T]): Vector[T] = {
            val result = ArrayBuffer[T]()
            for (s <- source) {
              val position = result.indexWhere(r => implicitly[Ordered[T]].gt(r, s)) match {
                case -1 => result.length
                case p => p
              }
              result.insert(position, s)
            }
            result.toVector
          }
          
          def Equality[T : Ordered](vector1 : Vector[T], vector2 : Vector[T]): Boolean =
            vector1.length == vector2.length && 
                vector1.zip(vector2).forall(p => implicitly[Ordered[T]].eq(p._1, p._2))
        } 
            

Arguments Implicites


        object Algorithm {
          def Sort[T : Ordered](source: Vector[T]): Vector[T] = {
            val result = ArrayBuffer[T]()
            for (s <- source) {
              val position = result.indexWhere(r => implicitly[Ordered[T]].gt(r, s)) match {
                case -1 => result.length
                case p => p
              }
              result.insert(position, s)
            }
            result.toVector
          }
          
          def Equality[T : Ordered](vector1 : Vector[T], vector2 : Vector[T]): Boolean =
            vector1.length == vector2.length && 
                vector1.zip(vector2).forall(p => implicitly[Ordered[T]].eq(p._1, p._2))
        } 
            

Arguments Implicites


                        sealed trait Ordered[-T] {
                          def gt(l: T, r: T): Boolean
                          def eq(l: T, r: T): Boolean
                        }
                        
                        object instances {
                          object IntOrd extends Ordered[Int] {
                            override def gt(l: Int, r: Int): Boolean = l > r
                            override def eq(l: Int, r: Int): Boolean = l == r
                          }
                        
                          object StringOrd extends Ordered[String] {
                            override def gt(l: String, r: String): Boolean = l > r
                            override def eq(l: String, r: String): Boolean = l == r
                          }
                        }
            

Arguments Implicites


                        sealed trait Ordered[-T] {
                          def gt(l: T, r: T): Boolean
                          def eq(l: T, r: T): Boolean
                        }
                        
                        object instances {
                          implicit object IntOrd extends Ordered[Int] {
                            override def gt(l: Int, r: Int): Boolean = l > r
                            override def eq(l: Int, r: Int): Boolean = l == r
                          }
                        
                          implicit object StringOrd extends Ordered[String] {
                            override def gt(l: String, r: String): Boolean = l > r
                            override def eq(l: String, r: String): Boolean = l == r
                          } 
                        }
            

Arguments Implicites


                        sealed trait Ordered[-T] {
                          def gt(l: T, r: T): Boolean
                          def eq(l: T, r: T): Boolean
                        }
                        
                        object instances {
                          implicit object IntOrd extends Ordered[Int] {
                            override def gt(l: Int, r: Int): Boolean = l > r
                            override def eq(l: Int, r: Int): Boolean = l == r
                          }
                        
                          implicit object StringOrd extends Ordered[String] {
                            override def gt(l: String, r: String): Boolean = l > r
                            override def eq(l: String, r: String): Boolean = l == r
                          } 
                        }
            

Arguments Implicites


    object instances {
                
      implicit def PairOrd[A, B](implicit modelA: Ordered[A], modelB: Ordered[B]): Ordered[(A, B)] = 
 
          new Ordered[(A, B)] {
                
                override def gt(l: (A, B), r: (A, B)): Boolean = 
                  modelA.gt(l._1, r._1) || 
                    (modelA.eq(l._1, r._1) && modelB.gt(l._2, r._2))
                
                override def eq(l: (A, B), r: (A, B)): Boolean = 
                  modelA.eq(l._1, r._1) && modelB.eq(l._2, r._2)
          }
    }
            

Arguments Implicites


    object instances {
                
      implicit def PairOrd[A, B](implicit modelA: Ordered[A], modelB: Ordered[B]): Ordered[(A, B)] = 
  
          new Ordered[(A, B)] {
                
                override def gt(l: (A, B), r: (A, B)): Boolean = 
                  modelA.gt(l._1, r._1) || 
                    (modelA.eq(l._1, r._1) && modelB.gt(l._2, r._2))
                
                override def eq(l: (A, B), r: (A, B)): Boolean = 
                  modelA.eq(l._1, r._1) && modelB.eq(l._2, r._2)
          }
    }
            

Arguments Implicites


    object instances {
                
      implicit def PairOrd[A, B](implicit modelA: Ordered[A], modelB: Ordered[B]): Ordered[(A, B)] = 
   
          new Ordered[(A, B)] {
                
                override def gt(l: (A, B), r: (A, B)): Boolean = 
                  modelA.gt(l._1, r._1) || 
                    (modelA.eq(l._1, r._1) && modelB.gt(l._2, r._2))
                
                override def eq(l: (A, B), r: (A, B)): Boolean = 
                  modelA.eq(l._1, r._1) && modelB.eq(l._2, r._2)
          }
    }
            

Arguments Implicites

                 
                      test("StringOrd Test") {
                        import net.sigusr.implicit1.instances.StringOrd
                        val stringOrds = Vector("a", "z", "e", "r")
                        val actual = Sort(stringOrds)
                        val expected = Vector("a", "e", "r", "z")
                        assert(Algorithm.Equality(actual, expected))
                      }
            

Arguments Implicites

                 
                      test("StringOrd Test") {
                        import net.sigusr.implicit1.instances.StringOrd
                        val stringOrds = Vector("a", "z", "e", "r")
                        val actual = Sort(stringOrds)
                        val expected = Vector("a", "e", "r", "z")
                        assert(Algorithm.Equality(actual, expected))
                      }
            
                 
                      test("PairOrd Test") {
                        import net.sigusr.implicit1.instances.{IntOrd, PairOrd, StringOrd}
                        val pairOrds = Vector((1, "a"), (-4, "z"), (-4, "e"), (42, "r"))
                        val actual = Sort(pairOrds)
                        val expected = Vector((-4, "e"), (-4, "z"), (1, "a"), (42, "r"))
                        assert(Algorithm.Equality(actual, expected))
                      }
            

Arguments Implicites

                 
                      test("StringOrd Test") {
                        import net.sigusr.implicit1.instances.StringOrd
                        val stringOrds = Vector("a", "z", "e", "r")
                        val actual = Sort(stringOrds)
                        val expected = Vector("a", "e", "r", "z")
                        assert(Algorithm.Equality(actual, expected))
                      }
            
                 
                      test("PairOrd Test") {
                        import net.sigusr.implicit1.instances.{IntOrd, PairOrd, StringOrd}
                        val pairOrds = Vector((1, "a"), (-4, "z"), (-4, "e"), (42, "r"))
                        val actual = Sort(pairOrds)
                        val expected = Vector((-4, "e"), (-4, "z"), (1, "a"), (42, "r"))
                        assert(Algorithm.Equality(actual, expected))
                      }
            

Arguments Implicites

  • Moins de Bruit
  • Type Classes Idiomatiques
  • Type Classes Idiomatiques
  • Complexe... Parce que Magique ?

Et Maintenant...

Haskell


                        insert :: Ordered a => a -> [a] -> [a]
                        insert elem [] = [elem]
                        insert elem (h:t) = if gt elem h then h:(insert elem t) else elem:h:t
                        
                        sort :: Ordered a => [a] -> [a]
                        sort = foldr insert [] 
            

Haskell


                        insert :: Ordered a => a -> [a] -> [a]
                        insert elem [] = [elem]
                        insert elem (h:t) = if gt elem h then h:(insert elem t) else elem:h:t
                        
                        sort :: Ordered a => [a] -> [a]
                        sort = foldr insert [] 
            

Haskell


                        class Ordered a where
                          gt :: a -> a -> Bool 
                          eq :: a -> a -> Bool 
            

Haskell


                        class Ordered a where
                          gt :: a -> a -> Bool 
                          eq :: a -> a -> Bool 
            

                        instance Ordered Integer where
                          gt r l = r > l
                          eq r l = r == l
                          
                        instance Ordered String where
                          gt r l = r > l
                          eq r l = r == l
            

Haskell


                        class Ordered a where
                          gt :: a -> a -> Bool 
                          eq :: a -> a -> Bool 
            

                        instance Ordered Integer where
                          gt r l = r > l
                          eq r l = r == l
                          
                        instance Ordered String where
                          gt r l = r > l
                          eq r l = r == l
            
                 
                        instance (Ordered a, Ordered b) => Ordered (a, b) where
                          gt (r1, r2) (l1, l2) = gt r1 l1 || (eq r1 l1 && gt r2 l2)
                          eq (r1, r2) (l1, l2) = eq r1 l1 && eq r2 l2
            

Haskell


                        class Ordered a where
                          gt :: a -> a -> Bool 
                          eq :: a -> a -> Bool 
            

                        instance Ordered Integer where
                          gt r l = r > l
                          eq r l = r == l
                          
                        instance Ordered String where
                          gt r l = r > l
                          eq r l = r == l
            
                 
                        instance (Ordered a, Ordered b) => Ordered (a, b) where
                          gt (r1, r2) (l1, l2) = gt r1 l1 || (eq r1 l1 && gt r2 l2)
                          eq (r1, r2) (l1, l2) = eq r1 l1 && eq r2 l2
            

Haskell


                    Loaded GHCi configuration from /tmp/haskell-stack-ghci/a76106fb/ghci-script

                    *Ordered Algorithm Ordered> sort [(1, "a"), (-4, "z"), (-4, "e"), (42, "r")]
                    [(-4,"e"),(-4,"z"),(1,"a"),(42,"r")]

                    *Ordered Algorithm Ordered>        
            

Haskell

  • Traduction λ-calcul Second Ordre
  • Passage Dictionnaire
  • Construction du Langage
  • Complexe... Parce que Magique ?

Rust


                pub fn sort<T: Ordered + Clone>(source: &Vec<T>) -> Vec<T> {
                    let mut result: Vec<T> = Vec::new();
                    for s in source {
                        let position =
                            match result.iter().position(|r| Ordered::gt(r, &s)) {
                                None => result.len(),
                                Some(p) => p
                            };
                        result.insert(position, s.clone());
                    }
                    return result;
                }
                
                pub fn equality<T: Ordered>(vector1: &Vec<T>, vector2: &Vec<T>) -> bool {
                    vector1.len() == 
                        vector2.len() && 
                        vector1.iter()
                            .zip(vector2.iter())
                            .all(|p| Ordered::eq(p.0, p.1))
                }            
            

Rust


                pub fn sort<T: Ordered + Clone>(source: &Vec<T>) -> Vec<T> {
                    let mut result: Vec<T> = Vec::new();
                    for s in source {
                        let position =
                            match result.iter().position(|r| Ordered::gt(r, &s)) {
                                None => result.len(),
                                Some(p) => p
                            };
                        result.insert(position, s.clone());
                    }
                    return result;
                }
                
                pub fn equality<T: Ordered>(vector1: &Vec<T>, vector2: &Vec<T>) -> bool {
                    vector1.len() == 
                        vector2.len() && 
                        vector1.iter()
                            .zip(vector2.iter())
                            .all(|p| Ordered::eq(p.0, p.1))
                }            
            

Rust


                pub fn sort<T: Ordered + Clone>(source: &Vec<T>) -> Vec<T> {
                    let mut result: Vec<T> = Vec::new();
                    for s in source {
                        let position =
                            match result.iter().position(|r| Ordered::gt(r, &s)) {
                                None => result.len(),
                                Some(p) => p
                            };
                        result.insert(position, s.clone());
                    }
                    return result;
                }
                
                pub fn equality<T: Ordered>(vector1: &Vec<T>, vector2: &Vec<T>) -> bool {
                    vector1.len() == 
                        vector2.len() && 
                        vector1.iter()
                            .zip(vector2.iter())
                            .all(|p| Ordered::eq(p.0, p.1))
                }            
        

Rust


                    pub trait Ordered {
                        fn gt(l: &Self, r: &Self) -> bool;
                        fn eq(l: &Self, r: &Self) -> bool;
                    }
            
                 
                    impl Ordered for i32 {
                        fn gt(l: &Self, r: &Self) -> bool { return l > r; }
                        fn eq(l: &Self, r: &Self) -> bool { return l == r; }
                    }
                    
                    impl Ordered for &str {
                        fn gt(l: &Self, r: &Self) -> bool { return str::gt(*l, *r); }
                        fn eq(l: &Self, r: &Self) -> bool { return str::eq(*l, *r); }
                    }
            

Rust


                    pub trait Ordered {
                        fn gt(l: &Self, r: &Self) -> bool;
                        fn eq(l: &Self, r: &Self) -> bool;
                    }
            
                 
                    impl Ordered for i32 {
                        fn gt(l: &Self, r: &Self) -> bool { return l > r; }
                        fn eq(l: &Self, r: &Self) -> bool { return l == r; }
                    }
                    
                    impl Ordered for &str {
                        fn gt(l: &Self, r: &Self) -> bool { return str::gt(*l, *r); }
                        fn eq(l: &Self, r: &Self) -> bool { return str::eq(*l, *r); }
                    }
            

Rust


                    impl<A: Ordered, B: Ordered> Ordered for (A, B) {
                
                        fn gt(l: &Self, r: &Self) -> bool {
                            A::gt(&l.0, &r.0) || (A::eq(&l.0, &r.0) && B::gt(&l.1, &r.1))
                        }
                
                        fn eq(l: &Self, r: &Self) -> bool {
                            A::eq(&l.0, &r.0) && B::eq(&l.1, &r.1)
                        }
                    }                
            

Rust


                    impl<A: Ordered, B: Ordered> Ordered for (A, B) {
                
                        fn gt(l: &Self, r: &Self) -> bool {
                            A::gt(&l.0, &r.0) || (A::eq(&l.0, &r.0) && B::gt(&l.1, &r.1))
                        }
                
                        fn eq(l: &Self, r: &Self) -> bool {
                            A::eq(&l.0, &r.0) && B::eq(&l.1, &r.1)
                        }
                    }                
            

Rust


                    #[test]
                    fn  string_ord_test() {
                        use crate::algorithm::sort;
                        let string_ords = vec!("a", "z", "e", "r");
                        let actual = sort(&string_ords);
                        let expected = vec!("a", "e", "r", "z");
                        assert!(equality(&actual, &expected))
                    }
            
                 
                    #[test]
                    fn  pair_ord_test() {
                        use crate::algorithm::sort;
                        let pair_ords = vec!((1, "a"), (-4, "z"), (-4, "e"), (42, "r"));
                        let actual = sort(&pair_ords);
                        let expected = vec!((-4, "e"), (-4, "z"), (1, "a"), (42, "r"));
                        assert!(equality(&actual, &expected))
                    }
            

Rust

  • Monomorphisation
  • Construction du Langage
  • Extension rétroactive ?

Rust


                    use std::hash::{Hash, Hasher};
                    use guid_create::GUID;
        
                    impl Hash for GUID {
                        fn hash<H: Hasher>(&self, state: &mut H) {
                            state.write_u32(self.0.data1());
                            state.write_u16(self.0.data2());
                            state.write_u16(self.0.data3());
                            for b in self.0.data4().iter() {
                                state.write_u8(*b)
                            }
                    }
            

Rust


                    use std::hash::{Hash, Hasher};
                    use guid_create::GUID;
        
                    impl Hash for GUID {
                        fn hash<H: Hasher>(&self, state: &mut H) {
                            state.write_u32(self.0.data1());
                            state.write_u16(self.0.data2());
                            state.write_u16(self.0.data3());
                            for b in self.0.data4().iter() {
                                state.write_u8(*b)
                            }
                    }
            
Rejected

Rust

RustError

Rust

❝ Only traits defined in the current crate
can be implemented for arbitrary types

Rust


                    use guid_create::GUID;
                    
                    #[derive(Copy, Clone, PartialEq, Eq, Debug)]
                    pub struct HGuid(GUID);
                    
                    impl HGuid {
                        pub fn new() -> HGuid {
                            HGuid(GUID::rand())
                        }
                    }
            

Rust


                    use std::hash::{Hash, Hasher};
                    
                    impl Hash for HGuid {
                        fn hash<H: Hasher>(&self, state: &mut H) {
                            state.write_u32(self.0.data1());
                            state.write_u16(self.0.data2());
                            state.write_u16(self.0.data3());
                            for b in self.0.data4().iter() {
                                state.write_u8(*b)
                            }
                        }
                    }
            

Ressources

  • How to make ad-hoc polymorphism less ad hoc [PDF]
  • Type Classes as Objects and Implicits [PDF]
  • Implementing Haskell Overloading [PDF]
  • Implementing, and Understanding Type Classes [BLOG]
  • Code de cette présentation [CODE]
Frédéric Cabestre
@fcabestre

Merci

qrcode