Multimethods ============ In this chapter, we show two important techniques. First, we define methods for built-in generic functions — in this case, for the functions ``+``, ``<``, and ``=``. Second, we define multimethods. We describe how method dispatch works for multimethods. .. _multi-methods-for-plus-gf: Methods for the ``+`` generic function -------------------------------------- We need to make it possible to add one time to another. We could define a method with a name such as ``add`` or ``plus``. However, the concept of adding times is the same as the concept of adding numbers. Dylan already provides the + generic function for adding numbers. Instead of inventing a new name for the addition operation, we define new methods on the built-in generic function ``+``. We can extend ``+`` by defining new methods for it. In certain languages, this technique is called *operator overloading*. .. topic:: Comparison with C++ and Java: In C++, operator overloading means customizing the action of any built-in operator for classes that you define. In Dylan, operators are just generic functions, and you can add methods to those generic functions for your classes. In C++, the meaning of an overloaded operator is resolved at compile time — the types of the operands must be known at compile time. Because Dylan operators are generic functions, the method is chosen dynamically according to the argument types —at run time, if the types may vary at run time. Java does not allow operator overloading. The Java designers believe that overloading of operators results in inscrutable code (because the meaning of the operator can vary). Dylan and C++ designers believe that, judiciously used, operator overloading permits clearer, more concise code. Method for adding two time offsets ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ We now define a method for ``+``. The method adds two time offsets and returns the sum, which is also a time offset: .. code-block:: dylan :linenos: // Method on , define method \+ (offset1 :: , offset2 :: ) => (sum :: ) let sum = offset1.total-seconds + offset2.total-seconds; make(, total-seconds: sum); end method \+; On line 2, notice that the method is defined on ``\+``, rather than simply on ``+``. When we define a method on ``+`` or on another infix function, we need to use a backslash before the function name. The backslash clarifies that we mean the value of the variable + (which is a generic function), and that we are not trying to call the function. On line 4, we add the values stored in the ``total-seconds`` slots of the two instances. On line 5, we make and return a new instance of ````. We initialize the ``total-seconds`` slot to contain the sum calculated in line 4. To test the method, we need to create two instances of ````: .. code-block:: dylan define variable *minus-2-hours* = make(, total-seconds: - encode-total-seconds (2, 0, 0)); define variable *plus-15-20-45* = make(, total-seconds: encode-total-seconds (15, 20, 45)); We can add the time offsets: .. code-block:: dylan-console ? *minus-2-hours* + *plus-15-20-45*; => {instance } The result is a new instance of ````. We did not save the value returned. (Many environments offer a way to access values returned by the listener.) We can add the time offsets again, and view the ``total-seconds`` slot of the result: .. code-block:: dylan-console ? decode-total-seconds(*minus-2-hours* + *plus-15-20-45*); => 13 => 20 => 45 Methods for adding a time of day to a time offset ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These methods implement addition between a time offset and a time of day: .. code-block:: dylan // Method on , define method \+ (offset :: , time-of-day :: ) => (sum :: ) make(, total-seconds: offset.total-seconds + time-of-day.total-seconds); end method \+; The method on ````, ```` is invoked when the first argument is a time offset and the second argument is a time of day. It does the work of creating a new ```` instance with the ``total-seconds`` slot initialized to the sum of the ``total-seconds`` slots of the two arguments. .. code-block:: dylan // Method on , define method \+ (time-of-day :: , offset :: ) => (sum :: ) offset + time-of-day; end method \+; The method on ````, ```` is invoked when the first argument is a time of day and the second argument is a time offset. It simply calls ``+`` with the order of the arguments switched — this call invokes the method on ````, ````. To test these methods, we can use one of the time offsets created in `Method for adding two time offsets`_, and define the ``*8-30-59*`` variable, which contains a ```` instance, which we define as follows: .. code-block:: dylan define variable *8-30-59* = make(, total-seconds: encode-total-seconds(8, 30, 59)); We add the time offset and the time of day: .. code-block:: dylan-console ? decode-total-seconds(*minus-2-hours* + *8-30-59*); => 6 => 30 => 59 We add the time of day and the time offset: .. code-block:: dylan-console ? decode-total-seconds(*8-30-59* + *minus-2-hours*); => 6 => 30 => 59 .. _multi-adding-other-times: Method for adding other kinds of times ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ We have already defined methods for adding the kinds of time that it makes sense to add together. It is not logical to add one time of day to another time of day — what would three o’clock plus two o’clock mean? Someone could create another concrete subclass of ``