Article Discussion
The Point of Pattern Matching in Scala
Summary: Martin Odersky talks with Bill Venners and Frank Sommers about the mechanics and purpose of pattern matching in Scala.
35 posts.
The ability to add new comments in this discussion is temporarily disabled.
Most recent reply: June 2, 2009 7:25 PM by Howard
    Bill
     
    Posts: 409 / Nickname: bv / Registered: January 17, 2002 4:28 PM
    The Point of Pattern Matching in Scala
    May 24, 2009 9:00 PM      
    In this final installment of a four part interview, Scala's designer Martin Odersky talks with Bill Venners and Frank Sommers about the mechanics and purpose of pattern matching in Scala.

    http://www.artima.com/scalazine/articles/pattern_matching.html

    What is your opinion of pattern matching?
    • Arnold
       
      Posts: 6 / Nickname: arnoldd / Registered: December 2, 2002 8:25 AM
      Re: The Point of Pattern Matching in Scala
      May 25, 2009 2:57 PM      
      It was this second direction of extensibility that sent me looking for a higher level language than java a while back. I was suffering the pain of repeated instanceof's and casts that you need to graft behaviour onto objects from the outside.

      The visitor pattern is one way to wrap this ugliness up but it is tedious to encode everywhere it is needed.

      I thought multimethod dispatch was the solution that fitted best with OO concepts. I found this implemented in the Nice language. The python community seems to have flirted with this idea too.

      I realised that pattern matching was another solution when I saw it in scala. I asked Martin why he didn't go down the multimethod route. From memory I think he was concerned that multimethods lead to open classes that would ultimately undo OO encapsulation. (Sorry if I misquote you Martin!)

      Now that I understand scala a little better I can see how pattern matching fits in with the rest of the language in a number of ways. And it keeps the type system more or less out of sight when dealing with objects from heterogeneous collection or other source.
    • Howard
       
      Posts: 25 / Nickname: hlovatt / Registered: March 3, 2003 1:20 PM
      Re: The Point of Pattern Matching in Scala
      May 25, 2009 9:27 PM      
      In my pet project, PEC _ Pattern Enforcing Compiler:

      https://pec.dev.java.net/nonav/compile/index.html

      Which is an extended Java compiler that allows you to specify design patterns and have the compiler enforce them.

      One of the patterns I have written is multiple dispatch. Using multiple dispatch you can add operations to data, the expression problem, but you don't need to use pattern matching. For example the converting of a List to a String example given in the text would be two overloaded toText methods, a default method that returned "was an empty list" and another method that returned "head was " + l.head() + ", tail was " + l.tail().

      Pattern matching isn't that far removed from a switch statement on an instanceof test. With multiple dispatch you simply call an overloaded method, which to my eyes is a nicer solution.
      • James
         
        Posts: 128 / Nickname: watson / Registered: September 7, 2005 3:37 AM
        Re: The Point of Pattern Matching in Scala
        May 26, 2009 6:09 AM      
        > Pattern matching isn't that far removed from a switch
        > statement on an instanceof test. With multiple dispatch
        > you simply call an overloaded method, which to my eyes is
        > a nicer solution.

        I actually have been swayed the other way in that I think overloading should not be allowed at all. Multiple-dispatch and operator-overloading in general has a stability problem when it comes to long-term maintenance. The problem is that when you add overloaded methods to a class, it's not obvious what other calls may be affected by that addition. This is especially true with multiple dispatch.

        Pattern matching solves a superset of the issues addressed by multi-methods and does so in a way that is explicit. The developer controls the dispatching and the intention is apparent. Overloading depends on rules of the compiler and/or runtime that are implicit and cannot be changed.
        • Carson
           
          Posts: 18 / Nickname: cgross / Registered: October 16, 2006 3:21 AM
          Re: The Point of Pattern Matching in Scala
          May 26, 2009 10:48 AM      
          > I actually have been swayed the other way in that I think
          > overloading should not be allowed at all.

          How apropos.

          http://guidewiredevelopment.wordpress.com/2009/05/22/i-am-hate-method-overloading-and-so-can-you/

          Cheers,
          Carson
        • Howard
           
          Posts: 25 / Nickname: hlovatt / Registered: March 3, 2003 1:20 PM
          Re: The Point of Pattern Matching in Scala
          May 26, 2009 7:18 PM      
          I haven't have an instability happen to me by adding a method, so I assume the occurrence of such errors is rare. Do you have examples? How often etc.?

          On the other hand I have often seen problems with code like this:

          class Point {
          private final double x, y;
          ...
          public boolean equals( final Object other ) {
          if ( !(other instanceof Point) ) { return false; } // Oops, derived classes!
          final Point that = (Point) other;
          return x == that.x && y == that.y;
          }
          }

          class Point3D extends Point {
          private final double z;
          ...
          public boolean equals( final Object other ) {
          if ( !(other instanceof Point3D) ) { return false; } // Oops, derived classes!
          final Point3D that = (Point3D) other;
          return x == that.x && y == that.y && z == that.z;
          }
          }

          The problem is with an instanceof test that will match derived classes as well as the class itself, which in this case is not what is required (that is why Point3dD has an equals method). This is a difficult to deal with problem, e.g. Point3D could be added by a third party who has no ability to modify Point.

          When I emailed Josh Bloch about this problem with instanceof, a similar example is in Effective Java, he replied that this problem was raised by many people with him (I think from memory that he said it was by far the most common comment on Effective Java). Therefore there is considerable evidence to suggest that instanceof tests cause problems in real code.

          All these problems go away with multiple dispatch and therefore I find this a very effective solution and at least in my own code have not experianced any problems. By the way multiple dispatch isn't that hard to do. I have written up my approach and it should take long, under a week, to get that fully implemented in a language (it took me less than a month from scratch including investigating half a dozen alternative implementation strategies).
          • James
             
            Posts: 128 / Nickname: watson / Registered: September 7, 2005 3:37 AM
            Re: The Point of Pattern Matching in Scala
            May 26, 2009 7:46 PM      
            > The problem is with an instanceof test that will match
            > derived classes as well as the class itself, which in this
            > case is not what is required (that is why Point3dD has an
            > equals method). This is a difficult to deal with problem,
            > e.g. Point3D could be added by a third party who has no
            > ability to modify Point.

            I don't see how multiple solves anything here. If you add a equals(Point3D) to Point3D, it doesn't change the way that Point.equals(Object) works. It's not symmetric.

            The instability comes when you have something like this:
            interface A {
                boolean foo(Object o);
            }
            

            And then do something like this:
            public class B implements A {
                public boolean foo(Object o) {
                    // ...
                }
             
                public boolean foo(String s) {
                    // ...
                }
            }
            

            No big deal right? But what if the foo in Bar is implemented as part of a different contract? But more of a concern is this:
            interface A {
                boolean foo(Object o);
            }
             
            public abstract class B implements A {
            }
             
            public abstract class C extends B {
                public boolean foo(Object o) {
                    // ...
                }
            }
            

            Then later, we decide to add foo(String) to B. Now, out of nowhere B is intercepting Strings passed to foo.
            • James
               
              Posts: 128 / Nickname: watson / Registered: September 7, 2005 3:37 AM
              Re: The Point of Pattern Matching in Scala
              May 27, 2009 7:06 AM      
              > I don't see how multiple solves anything here. If you add
              > a equals(Point3D) to Point3D, it doesn't change the way
              > that Point.equals(Object) works. It's not symmetric.

              Sorry, what I mean here is that it's not transitive.
            • Howard
               
              Posts: 25 / Nickname: hlovatt / Registered: March 3, 2003 1:20 PM
              Re: The Point of Pattern Matching in Scala
              May 27, 2009 1:13 PM      
              With symmetric multiple dispatch you have:

              static boolean equals( Object lhs, Object rhs ) { return lhs == rhs; } // default for Objects and also nulls
              static boolean equals( Point lhs, Point rhs ) { return lhs.x == rhs.x && lhs.y == rhs.y; }
              


              Now when you add Point3D you add:

              static boolean equals( Point3D lhs, Point3D rhs ) { return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z; }
              


              and it works as expected.

              If you want to compare a Point to a Point3D then you have to add both:

              static boolean equals( Point lhs, Point3D rhs ) { return lhs.x == rhs.x && lhs.y == rhs.y && 0 == rhs.z; }
              static boolean equals( Point3D lhs, Point rhs ) { return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == 0; }
              


              If you add just one of these it is flagged as an error.
              • James
                 
                Posts: 128 / Nickname: watson / Registered: September 7, 2005 3:37 AM
                Re: The Point of Pattern Matching in Scala
                May 27, 2009 2:02 PM      
                > [snip]
                > and it works as expected.

                The implementation you give works but only because you've limited the usefulness of it. Only points with z of zero can ever be equal to a 2D point which is unlikely to be what is desired.

                That's beside the point, however. There's nothing about this that can't be done using explicit instanceofs. Multimethods (as I understand them) are really just implicit instanceof. I'm not really understanding your argument here.
                • Howard
                   
                  Posts: 25 / Nickname: hlovatt / Registered: March 3, 2003 1:20 PM
                  Re: The Point of Pattern Matching in Scala
                  May 27, 2009 4:46 PM      
                  [snip]
                  > That's beside the point, however. There's nothing about
                  > this that can't be done using explicit instanceofs.
                  > Multimethods (as I understand them) are really just
                  > t implicit instanceof. I'm not really understanding your
                  > argument here.

                  The difference is two fold:

                  1. Point3D can be written and compiled *completely* separately from Point, no access to Point's source is needed. The writer of Point3D simply writes the three equals methods and puts them in Point3D. A call point.equals(point3D) then just works without even recompiling Point (syntax shown is the syntax I use - other syntaxes for multi-methods have been proposed).

                  2. Yes the multiple dispatch is implemented as a series of instanceof tests. Crucially though this is not a fixed set of tests and if a new class containing new relevant methods, e.g. Point3D, is loaded then the tests are rewritten automatically at load-time (i.e. the instanceof tests are not generated at compile-time but at load-time and the tests are rewritten, if needed, when a new class loads).
                  • James
                     
                    Posts: 128 / Nickname: watson / Registered: September 7, 2005 3:37 AM
                    Re: The Point of Pattern Matching in Scala
                    May 27, 2009 5:34 PM      
                    > Point3D can be written and compiled *completely*
                    > separately from Point, no access to Point's source is
                    > needed. The writer of Point3D simply writes the three
                    > equals methods and puts them in Point3D. A call
                    > point.equals(point3D) then just works without even
                    > recompiling Point (syntax shown is the syntax I use -
                    > other syntaxes for multi-methods have been proposed).

                    I guess I don't understand. How does point.equals(point3d) resolve to a method that is not defined on Point?
                    • Howard
                       
                      Posts: 25 / Nickname: hlovatt / Registered: March 3, 2003 1:20 PM
                      Re: The Point of Pattern Matching in Scala
                      May 27, 2009 7:39 PM      
                      [snip]
                      > I guess I don't understand. How does
                      > point.equals(point3d) resolve to a method that is not
                      > defined on Point?

                      The short answer is that equals uses a dispatcher object to select the correct method to call. This dispatcher object contains a method, dispatch, that does the instanceof tests and calls the appropriate static method. The dispatcher object is replaced each time a new set of methods are loaded and the new dispatcher has the new set of instanceof tests in it. Method equals, which is compiler generated, looks something like:
                      public static $Equals$Dispatcher$ $equals$Dispatcher$; // in abstract base class
                       
                      public final boolean equals( final Object other ) {
                        return $equals$Dispatcher$.dispatch( other );
                      }
                      

                      The long answer is given here:

                      http://pec.dev.java.net/nonav/compile/javadoc/pec/compile/multipledispatch/package-summary.html#package_description
                      • James
                         
                        Posts: 128 / Nickname: watson / Registered: September 7, 2005 3:37 AM
                        Re: The Point of Pattern Matching in Scala
                        May 28, 2009 7:56 AM      
                        > [snip]
                        > > I guess I don't understand. How does
                        > > point.equals(point3d) resolve to a method that is not
                        > > defined on Point?
                        >
                        > The short answer is that equals uses a dispatcher object
                        > to select the correct method to call.

                        If I understand correctly, the point.equals() is effectively overridden by a method that is not part of it's class. If that right, you aren't just adding multi-methods, you are changing the semantics of polymorphism in Java. Again, assuming this is correct, this is IMO a very dangerous and tricky thing to do.
            • Howard
               
              Posts: 25 / Nickname: hlovatt / Registered: March 3, 2003 1:20 PM
              Re: The Point of Pattern Matching in Scala
              May 27, 2009 1:23 PM      
              [snip]
              > The instability comes when you have something like this:
              >
              interface A {
              >     boolean foo(Object o);
              > }
              

              > And then do something like this:
              >
              public class B implements A {
              >     public boolean foo(Object o) {
              >         // ...
              >     }
              > 
              >     public boolean foo(String s) {
              >         // ...
              >     }
              > }
              

              [snip]

              I am not saying that something like this can't happen in Java - I am just saying I have never had it happen to me and I am not aware of this being a problem - unlike instanceof which is a well known problem.
              • James
                 
                Posts: 128 / Nickname: watson / Registered: September 7, 2005 3:37 AM
                Re: The Point of Pattern Matching in Scala
                May 27, 2009 5:52 PM      
                > I am not saying that something like this can't happen in
                > Java - I am just saying I have never had it happen to me
                > and I am not aware of this being a problem - unlike
                > instanceof which is a well known problem.

                It can't happen in Java because Java doesn't support multiple dispatch. There is a related issue with overloading where adding a method causes unexpected changes in behaviors when the calling class is recompiled. But mostly the issues have to do with people expecting overloading to be double-dispatch. And that's why I had thought in the past that multi-methods would be better. But my experience with pattern matching in Scala has shown me that it solves that problem and more without the magic.
                • Howard
                   
                  Posts: 25 / Nickname: hlovatt / Registered: March 3, 2003 1:20 PM
                  Re: The Point of Pattern Matching in Scala
                  May 27, 2009 7:22 PM      
                  [snip]
                  > But my experience with pattern matching in Scala has
                  > s shown me that it solves that problem and more without
                  > the magic.

                  If you have:
                  trait Points { def test( p : Points ) : Boolean }
                   
                  case class Point( x : Int, y : Int ) extends Points {
                    override def test( p : Points ) = p match {
                      case Point( px, py ) => x == px && y == py
                      case _ => false
                    }
                  }
                  

                  And you can't change Point (e.g. third party library) then you have two options for Point3D, either:
                  case class Point3D( x : Int, y : Int, z : Int ) extends Points {
                    override def test( p : Points ) = p match {
                      case Point( px, py ) => x == px && y == py && z == 0
                      case Point3D( px, py, pz ) => x == px && y == py && z == pz
                      case _ => false
                    }
                  }
                  

                  or
                  case class Point3D2( override val x : Int, override val y : Int, z : Int ) extends Point( x, y ) {
                    override def test( p : Points ) = p match {
                      case Point3D2( px, py, pz ) => x == px && y == py && z == pz
                      case _ => super.test( p )
                    }
                  }
                  

                  (For the second case there are a number of options for test - but none of them work).

                  The following tests should all print true:
                      val p = new Point( 2, 1 )
                      val p3D = new Point3D( 2, 1, 0 )
                      val p23D = new Point3D( 2, 1, 2 )
                      println( "Option 1 - Point3D extends Points (note s)" )
                      println( p.test( p ) )
                      println( p.test( p3D ) )
                      println( p3D.test( p ) )
                      println( p3D.test( p3D ) )
                      println( !p.test( p23D ) )
                      println( !p3D.test( p23D ) )
                      val p3D2 = new Point3D2( 2, 1, 0 )
                      val p23D2 = new Point3D2( 2, 1, 2 )
                      println( "Option 2 - Point3D2 extends Point (no s)" )
                      println( p.test( p ) )
                      println( p.test( p3D2 ) )
                      println( p3D2.test( p ) )
                      println( p3D2.test( p3D2 ) )
                      println( !p.test( p23D2 ) )
                      println( !p3D2.test( p23D2 ) )
                  

                  Unfortunately the output is:

                  Option 1 - Point3D extends Points (note s)
                  true
                  false
                  true
                  true
                  true
                  true
                  Option 2 - Point3D2 extends Point (no s)
                  true
                  true
                  true
                  true
                  false
                  true

                  With multiple dispatch you can get the correct answer by simply writing 3 methods.
                  • James
                     
                    Posts: 128 / Nickname: watson / Registered: September 7, 2005 3:37 AM
                    Re: The Point of Pattern Matching in Scala
                    May 28, 2009 8:14 AM      
                    > With multiple dispatch you can get the correct answer by
                    > simply writing 3 methods.

                    Only if you constrain your correct answer to something that is useless. If I want to compare a 2D point to a 3D point, there are a number of ways I might want that to work and none of are that only 3D points on the z == 0 plane can be considered equal to a 2D point. You might as well just say that 2D points and 3D points can't never be equal as it solves the same problems.

                    The real problem is that equals and polymorphism don't mix. There real solution to this issue is to have a third party do the comparison. In fact, this same problem that the Comparator interface in Java resolves for Comparables.

                    You definitely have a clever solution here but I don't think it's really going in the right direction. Not for my needs, anyway.
                    • Mark
                       
                      Posts: 48 / Nickname: mthornton / Registered: October 16, 2005 11:22 PM
                      Re: The Point of Pattern Matching in Scala
                      May 28, 2009 10:26 AM      
                      > The real problem is that equals and polymorphism don't
                      > mix.
                      The solution outlined in "Programming in Scala", chapter 28 works well.
                      • Bill
                         
                        Posts: 409 / Nickname: bv / Registered: January 17, 2002 4:28 PM
                        Re: The Point of Pattern Matching in Scala
                        May 28, 2009 1:19 PM      
                        > > The real problem is that equals and polymorphism don't
                        > > mix.
                        > The solution outlined in "Programming in Scala", chapter
                        > 28 works well.
                        >
                        Yes, it does. Stay tuned as next week I'm planning on publishing an article describing the technique, with the code translated to Java.

                        However, that's about equality, not about multi-methods versus pattern matching.
                        • Arnold
                           
                          Posts: 6 / Nickname: arnoldd / Registered: December 2, 2002 8:25 AM
                          Re: The Point of Pattern Matching in Scala
                          May 28, 2009 4:44 PM      
                          > about multi-methods versus pattern matching.

                          Let me have a crack at this comparison:

                          A scala match statement is the equivalent of a family of overriding, open, multimethods. (Where an open multimethod is one declared independently of the argument types dispatched.)

                          Some comparison points...

                          1. Introducing a match statement is lighter weight than defining a family of multimethods.

                          2. The alternatives are all gathered together in a match statement. I can't extend a match "from the outside". But I can add another override to a family of multimethods. (Good or bad?)

                          3. The dispatch rules for match statements are simpler. The dispatch rules for a family of multmethods resemble overload resolution (albeit at runtime).

                          4. An open multimethod is written in terms of the public features of its arguments while simple pattern matching systems need access to internal state.

                          In scala, we have the innovation of extractors which restore encapsulation. An extractor can be separate from the matched type in which case it only has access to public features.

                          5. Patterns can be nested and may involve values as well as types. Some multimethod systems are limited to dispatching on types.


                          It is interesting to contemplate extending an existing family of multimethods with new overrides. ie my point (2). Most of this thread seems to be discussing whether this ability is a can of worms in practice.
                          • Howard
                             
                            Posts: 25 / Nickname: hlovatt / Registered: March 3, 2003 1:20 PM
                            Re: The Point of Pattern Matching in Scala
                            May 28, 2009 8:47 PM      
                            [snip]
                            > 1. Introducing a match statement is lighter weight than
                            > defining a family of multimethods.

                            Sure for an instanceof test types alone, but when you then add things like sealed classes and matches on values there isn't much in it.

                            > 2. The alternatives are all gathered together in a match
                            > statement. I can't extend a match "from the outside". But
                            > I can add another override to a family of multimethods.
                            > (Good or bad?)

                            That isn't true, see Point example in chapter 28 of the Scalabook.

                            > 3. The dispatch rules for match statements are simpler.
                            > The dispatch rules for a family of multmethods resemble
                            > e overload resolution (albeit at runtime).

                            Yes, but you do need to generate errors to detect unreachable cases - which is the same procedure as you do with multiple dispatch (so not much in it)

                            > 4. An open multimethod is written in terms of the public
                            > features of its arguments while simple pattern matching
                            > systems need access to internal state.

                            No, if the multimethod is inside a class then it has access to private data just like any other class member does.

                            > In scala, we have the innovation of extractors which
                            > restore encapsulation. An extractor can be separate from
                            > the matched type in which case it only has access to
                            > public features.

                            A multimethod can be outside a class also and then it only has access to public members

                            > 5. Patterns can be nested and may involve values as well
                            > as types. Some multimethod systems are limited to
                            > dispatching on types.

                            No reason why any method, multiple or single dispatch, can't match on values. EG in Haskell and many functional languages you can write:

                            def factorial(1) = 1
                            def factorial(n) = n * factorial(n - 1)

                            > It is interesting to contemplate extending an existing
                            > family of multimethods with new overrides. ie my point
                            > t (2). Most of this thread seems to be discussing whether
                            > this ability is a can of worms in practice.

                            Not in my experience, in fact quite the opposite. Take chapter 28 of the Scalabook, it is a long chapter discussing the pitfalls of pattern matching for a well known method, equals. I would suggest that using mutimethods would be much simpler (see previous post).
                            • Arnold
                               
                              Posts: 6 / Nickname: arnoldd / Registered: December 2, 2002 8:25 AM
                              Re: The Point of Pattern Matching in Scala
                              May 28, 2009 9:44 PM      
                              > > 2. The alternatives are all gathered together in a match
                              > > statement. I can't extend a match "from the outside".But
                              > > I can add another override to a family of multimethods.
                              > > (Good or bad?)
                              >
                              > That isn't true, see Point example in chapter 28 of the
                              > Scalabook.
                              >

                              Actually, I was just referring to the fact that no addition cases can be added to an existing match statement. (Without directly modifying it of course.)

                              The examples in chapter 28 don't change which pattern is matched by adding new code elsewhere.

                              But with open multimethods a new override can be written after-the-fact, in a different compilation unit, in such a way that it will affect the dispatch.

                              Without buying into the debate on whether this is good or bad, I think it is a fundamental difference.
                              • Howard
                                 
                                Posts: 25 / Nickname: hlovatt / Registered: March 3, 2003 1:20 PM
                                Re: The Point of Pattern Matching in Scala
                                May 29, 2009 0:37 PM      
                                > > > 2. The alternatives are all gathered together in a
                                > match
                                > > > statement. I can't extend a match "from the
                                > outside".But
                                > > > I can add another override to a family of
                                > multimethods.
                                > > > (Good or bad?)
                                > >
                                > > That isn't true, see Point example in chapter 28 of the
                                > > Scalabook.
                                > >
                                >
                                > Actually, I was just referring to the fact that no
                                > addition cases can be added to an existing match
                                > statement. (Without directly modifying it of course.)
                                >
                                > The examples in chapter 28 don't change which pattern is
                                > matched by adding new code elsewhere.
                                >
                                > But with open multimethods a new override can be written
                                > after-the-fact, in a different compilation unit, in such a
                                > way that it will affect the dispatch.
                                >
                                > Without buying into the debate on whether this is good or
                                > bad, I think it is a fundamental difference.

                                Sorry I misunderstood your point - yes what you are saying is correct
                        • Mark
                           
                          Posts: 48 / Nickname: mthornton / Registered: October 16, 2005 11:22 PM
                          Re: The Point of Pattern Matching in Scala
                          May 28, 2009 11:58 PM      
                          I tried to extend the Rational example from "Programming in Scala" in several respects. In the book it shows how to get 4 * 1/3 and 1/3 * 4 to produce the expected result (4/3). OK, but I also want 4.0 * 1/3 and 1/3 * 4.0 to produce 1.3333..., and here I failed. As I understand it, multimethods should be able to get the right answer for cases like this, but I don't see pattern matching helping.
                          • Bill
                             
                            Posts: 409 / Nickname: bv / Registered: January 17, 2002 4:28 PM
                            Re: The Point of Pattern Matching in Scala
                            May 29, 2009 0:57 PM      
                            Hi Mark,

                            > I tried to extend the Rational example from "Programming
                            > in Scala" in several respects. In the book it shows how to
                            > get 4 * 1/3 and 1/3 * 4 to produce the expected result
                            > (4/3). OK, but I also want 4.0 * 1/3 and 1/3 * 4.0 to
                            > produce 1.3333..., and here I failed. As I understand it,
                            > multimethods should be able to get the right answer for
                            > cases like this, but I don't see pattern matching helping.
                            >
                            Although any floating point number you can express on the JVM, float or double, could be represented by a rational number (because every floating point number has a finite number of digits), that doesn't mean you could represent them in a Rational as shown in the book, because in the book the numerator and denominators are Ints. Fortunately, you are wanting to go in the other direction. Given a floating point number, multiple it by a Rational number and get another floating point number. You would like to do this either way:


                            4.0 * (new Rational(1, 3))


                            and


                            (new Rational(1, 3)) * 4.0


                            If you can change class Rational, then you could at *, +, -, /, etc. methods that take Double, and that would give you the latter one. For the former, or the latter if you can't change Rational, you'd need to create an implicit conversion from Rational to Double. Here's what it looks like in the Scala interpreter. Before this code I just copied the final version of Rational shown in chapter 6 (in listing 6.5) and pasted it into the interpreter. Then:


                            scala> implicit def convert(rat: Rational) = rat.numer.toDouble / rat.denom.toDouble
                            convert: (Rational)Double

                            scala> val oneThird = new Rational(1, 3)
                            oneThird: Rational = 1/3

                            scala> 4.0 * oneThird
                            res0: Double = 1.3333333333333333

                            scala> oneThird * 4.0
                            res1: Double = 1.3333333333333333
                            • Mark
                               
                              Posts: 48 / Nickname: mthornton / Registered: October 16, 2005 11:22 PM
                              Re: The Point of Pattern Matching in Scala
                              May 29, 2009 1:16 PM      
                              >

                              > scala> implicit def convert(rat: Rational) =
                              > rat.numer.toDouble / rat.denom.toDouble
                              > convert: (Rational)Double
                              >


                              Unfortunately then the Int case no longer produces the desired result:

                              4*(new Rational(1/3))

                              1.3333...
                              • Bill
                                 
                                Posts: 409 / Nickname: bv / Registered: January 17, 2002 4:28 PM
                                Re: The Point of Pattern Matching in Scala
                                May 29, 2009 3:58 PM      
                                > >

                                > > scala> implicit def convert(rat: Rational) =
                                > > rat.numer.toDouble / rat.denom.toDouble
                                > > convert: (Rational)Double
                                > >

                                >
                                > Unfortunately then the Int case no longer produces the
                                > desired result:
                                >
                                > 4*(new Rational(1/3))
                                >
                                > 1.3333...
                                >
                                Oops. I guess I should have tested better!

                                I'll go back to the drawing board.
                          • Howard
                             
                            Posts: 25 / Nickname: hlovatt / Registered: March 3, 2003 1:20 PM
                            Re: The Point of Pattern Matching in Scala
                            May 29, 2009 0:41 PM      
                            > I tried to extend the Rational example from "Programming
                            > in Scala" in several respects. In the book it shows how to
                            > get 4 * 1/3 and 1/3 * 4 to produce the expected result
                            > (4/3). OK, but I also want 4.0 * 1/3 and 1/3 * 4.0 to
                            > produce 1.3333..., and here I failed. As I understand it,
                            > multimethods should be able to get the right answer for
                            > cases like this, but I don't see pattern matching helping.

                            Yes with multimethods you could do this. (You could also hand code multiple dispatch or use the Visitor pattern.)
                            • Howard
                               
                              Posts: 25 / Nickname: hlovatt / Registered: March 3, 2003 1:20 PM
                              Re: The Point of Pattern Matching in Scala
                              May 29, 2009 1:38 PM      
                              Surprisingly the case classes in Scala don't follow the recommendation in Chapeter 28, e.g.:

                              trait Points {}
                               
                              case class Point( x : Int, y : Int ) extends Points {}
                               
                              case class Point3D2( override val x : Int, override val y : Int, z : Int ) extends Point( x, y ) {}
                               
                                  val p = new Point( 2, 1 )
                                  val p3D2 = new Point3D2( 2, 1, 0 )
                                  val p23D2 = new Point3D2( 2, 1, 2 )
                                  println( "Option 2e - equals and Point3D2 extends Point (no s)" )
                                  println( p == p )
                                  println( p == p3D2 )
                                  println( p3D2 == p )
                                  println( p3D2 == p3D2 )
                                  println( !(p == p23D2) )
                                  println( !(p3D2 == p23D2) )
                              


                              Gives:


                              Option 2e - equals and Point3D2 extends Point (no s)
                              true
                              true
                              false
                              true
                              false
                              true
                              • Bill
                                 
                                Posts: 409 / Nickname: bv / Registered: January 17, 2002 4:28 PM
                                Re: The Point of Pattern Matching in Scala
                                May 29, 2009 4:05 PM      
                                Hi Howard,

                                > Surprisingly the case classes in Scala don't follow the
                                > recommendation in Chapeter 28, e.g.:
                                >
                                Yes, that bugged me too. I did bring it up here:

                                http://www.scala-lang.org/node/987#comment-4558

                                And was told it had been brought up before. They may drop case class inheritance in 2.8, but even if they do, you'll still be able to subclass a case class with a regular class. So the problem still holds. I think case classes should get a canEqual method. I'll bring it up again with Martin next week at JavaOne. Howard, will you be at JavaOne this year?
                                • Howard
                                   
                                  Posts: 25 / Nickname: hlovatt / Registered: March 3, 2003 1:20 PM
                                  Re: The Point of Pattern Matching in Scala
                                  May 29, 2009 5:22 PM      
                                  [snip]
                                  > And was told it had been brought up before. They may drop
                                  > case class inheritance in 2.8, but even if they do, you'll
                                  > still be able to subclass a case class with a regular
                                  > class. So the problem still holds. I think case classes
                                  > should get a canEqual method. I'll bring it up again with
                                  > Martin next week at JavaOne. Howard, will you be at
                                  > JavaOne this year?

                                  Unfortunately I can't make JaveOne. But I will be in Switzerland, for my day job (not Scala related), from August to January, so if you are going over maybe we could meet up there.
                        • Howard
                           
                          Posts: 25 / Nickname: hlovatt / Registered: March 3, 2003 1:20 PM
                          Re: The Point of Pattern Matching in Scala
                          May 28, 2009 7:42 PM      
                          > > > The real problem is that equals and polymorphism
                          > don't
                          > > > mix.
                          > > The solution outlined in "Programming in Scala",
                          > chapter
                          > > 28 works well.

                          Chapter 28 of the Scala Book is great, particularly since I am a fan of multiple dispatch and the suggested solution is to hand code multiple dispatch :). Quick recap of chapter 28 the recommended solution is:

                          class Point(val x: Int, val y: Int) { 
                            override def hashCode = 41 * (41 + x) + y 
                            override def equals(other: Any) = other match { 
                              case that: Point => (that canEqual this) && (this.x == that.x) && (this.y == that.y) 
                              case _ => false 
                            } 
                            def canEqual(other: Any) = other.isInstanceOf[Point] 
                          } 
                           
                          class ColoredPoint(x: Int, y: Int, val color: Color.Value) extends Point(x, y) { 
                            override def hashCode = 41 * super.hashCode + color.hashCode 
                            override def equals(other: Any) = other match { 
                              case that: ColoredPoint => (that canEqual this) && super.equals(that) && this.color == that.color 
                              case _ => false 
                            } 
                            override def canEqual(other: Any) = other.isInstanceOf[ColoredPoint] 
                          } 
                          


                          The crucial bit of the solution is the call:

                          that canEqual this
                          


                          which you note is *not*:

                          this canEqual that
                          


                          IE it is a second dispatch. The solution works and I thought it was widely known, but maybe it isn't widely know since people on this forum have bought it up in the context suggesting that it is novel (there may be some novelty in that canEqual only tests types, but essentially the method is the same as double dispatch or the Visitor pattern). There are a number of limitations:

                          1. It quickly gets out of hand for multiple arguments, you need can1, can2, etc which rotate the arguments.

                          2. It can only do the stricter comparison (page 578 says "Making the equals relation more general seems to lead to a dead end").

                          3. The code is verbose and ugly.

                          Lets pretend that Scala got multiple dispatch and a method declared with multidef defined a multiple dispatch method, then the above example would become:

                          class Point(val x: Int, val y: Int) { 
                            override def hashCode = 41 * (41 + x) + y 
                            override multidef equals(this, that: Any) = false
                            override multidef equals(this, that: Point) = (x == that.x) && (y == that.y) 
                          } 
                           
                          class ColoredPoint(x: Int, y: Int, val color: Color.Value) extends Point(x, y) { 
                            override def hashCode = 41 * super.hashCode + color.hashCode 
                            override multidef equals(this, that: ColordePoint) = (x == that.x) && (y == that.y) && (this.color == that.color)
                          } 
                          


                          Which I think is a lot clearer and address points 1 and 3 that I raised above. With multiple dispatch you can also do a more general comparison (point 2 above), e.g. suppose you want Point to be a Black ColoredPoint, then you can:

                          class ColoredPoint(x: Int, y: Int, val color: Color.Value) extends Point(x, y) { 
                            override def hashCode = 41 * super.hashCode + color.hashCode 
                            override multidef equals(this, that: ColoredPoint) = (x == that.x) && (y == that.y) && (this.color == that.color)
                            override multidef equals(this, that: Point) = (x == that.x) && (y == that.y) && (this.color == Black)
                            override multidef equals(that: Point, this) = (x == that.x) && (y == that.y) && (this.color == Black)
                          } 
                          


                          So in summary, the suggested solution in chapter 28 is a hand coded form of limited multiple dispatch. Yes it does overcome some of the limitations of pattern matching, but not all (unlike true multiple dispatch) and it is extra tricky code to write.
                          • Howard
                             
                            Posts: 25 / Nickname: hlovatt / Registered: March 3, 2003 1:20 PM
                            Re: The Point of Pattern Matching in Scala
                            May 28, 2009 8:59 PM      
                            Oops typo

                            >
                            > class ColoredPoint(x: Int, y: Int, val color: Color.Value) extends Point(x, y) { 
                            >   override def hashCode = 41 * super.hashCode + color.hashCode 
                            >   override multidef equals(this, that: ColordePoint) = (x == that.x) && (y == that.y) && (this.color == that.color)
                            > } 
                            > 
                            


                            Should be

                            class ColoredPoint(x: Int, y: Int, val color: Color.Value) extends Point(x, y) { 
                              override def hashCode = 41 * super.hashCode + color.hashCode 
                              override multidef equals(this, that: ColordePoint) = (x == that.x) && (y == that.y) && (this.color == that.color)
                              override multidef equals(this, that: Point) = false
                              override multidef equals(that: Point, this) = false
                            } 
                            
                          • Raoul
                             
                            Posts: 20 / Nickname: raoulduke / Registered: April 14, 2006 11:48 AM
                            Re: The Point of Pattern Matching in Scala
                            June 2, 2009 3:00 PM      
                            Howard,

                            > Chapter 28 of the Scala Book is great, particularly since
                            > I am a fan of multiple dispatch and the suggested solution
                            > is to hand code multiple dispatch :).

                            So, just for kicks, any thoughts on how PEC could some day work with Scala?
                            • Howard
                               
                              Posts: 25 / Nickname: hlovatt / Registered: March 3, 2003 1:20 PM
                              Re: The Point of Pattern Matching in Scala
                              June 2, 2009 7:25 PM      
                              PEC could be rewritten to work with Scala, but it would be a lot of work. In particular I make extensive use of the Javassist library that unfortunately doesn't work with Scala.

                              Because Scala doesn't have a direct translation between a class definition and a class file (strictly Java doesn't have a direct translation for inner classes but it is close), it would probably by easier in Scala to break into the compiler and modify the AST rather than modify the class file byte codes (which is what Javassist does). This breaking into the compiler might be easy, you can do this in Java already using the annotation processor and casting the unmodifiable AST you get into a modifiable AST (I guess something similar will work with the Scala compiler).