Lecture 22 Slides - Methods (Composing Data and Code)

1 of 19

Lecture 22

Composing Code and Data with Methods

2 of 19

Programming as com/position

  • Function composition
    • Linking outputs to inputs
  • Data composition
    • Primitives
    • Lists and records (tuples)
    • Inductive structures
  • Imperative composition
    • Sequencing changes in time
  • Composing code with data
    • Object-oriented programming

3 of 19

Programming as com/position

  • Function composition
    • Linking outputs to inputs
  • Data composition
    • Primitives
    • Lists and records (tuples)
    • Inductive structures
  • Imperative composition
    • Sequencing changes in time
  • Composing code with data
    • Object-oriented programming

4 of 19

Subtyping helped us streamline our program

(define (zoo-weight zoo)

(cond [(empty? zoo) 0]

[else (+ (local [(define a (first zoo))]

(cond [(snake? a) (snake-weight a)]

[(dillo? a) (dillo-weight a)]

[(ant? a) (ant-weight a)]))

(zoo-weight (rest zoo)))]))

VS

(define (zoo-weight zoo)

(cond [(empty? zoo) 0]

[else (+ (animal-weight (first zoo))

(zoo-weight (rest zoo)))]))

5 of 19

But what if this was more about an action?

  • Say we want to feed all the animals in the zoo:

(define (feed-animal! a)

(set-animal-weight! a (+ 2 (animal-weight a))))

(define (feed-zoo! loa)

(for-each (λ (a) (feed-animal! a))

loa))

  • But should we really be feeding every animal the same amount of food?

6 of 19

This isn't an inheritance issue

  • Before the problem was that cats, dogs, and mice were unnecessarily different
    • They all had different weight fields
    • But there was no good reason for them to be different
  • Now we have a situation where the types are necessarily different
    • We really do want to run different programs for different types (aka type dispatch)
    • And what we don't want is a complicated stack of conds that checks to see what each thing is before feeding it.
    • We want a cooler more lazy way

7 of 19

Methods

  • Methods are an implementation of a function for a specific type of data

8 of 19

Specifying methods in ASL structs

  • ASL lets you specify different methods for different types by
    • Placing them inside the define-struct expression
    • Prefacing them with the keyword #:methods

(define-struct (cat animal) (sleeping-spot)

#:methods

(define (feed! c) ...))

(define-struct (dog animal) (best-friend)

#:methods

(define (feed! d) ...))

(define-struct (mouse animal) (hiding-spot)

#:methods

(define (feed! m) ...))

9 of 19

Specifying methods in ASL structs

  • Each time a method is called…
    • ASL chooses which method to run based on the type of the first argument to the function

(define-struct (cat animal) (sleeping-spot)

#:methods

(define (feed! c) ...))

(define-struct (dog animal) (best-friend)

#:methods

(define (feed! d) ...))

(define-struct (mouse animal) (hiding-spot)

#:methods

(define (feed! m) ...))

10 of 19

Specifying methods in ASL structs

  • This lets you call feed!
    • Without having to worry about the type of object you're feeding!
    • ASL will take care of determining which method to run (type dispatch)

(define-struct (cat animal) (sleeping-spot)

#:methods

(define (feed! c) ...))

(define-struct (dog animal) (best-friend)

#:methods

(define (feed! d) ...))

(define-struct (mouse animal) (hiding-spot)

#:methods

(define (feed! m) ...))

11 of 19

Method inheritance

  • What if there is no method for that type?
  • Types are allowed to inherit methods from their parent type
    • If the subtype doesn't define a method of a particular name
    • It automatically inherits the method from its parent type
    • Otherwise, the type's method overrides the parent's method

12 of 19

Specifying methods in ASL structs

  • For example, we can have a method for feed! in animal that does nothing

  • This is often called the default (or generic) method

  • It gets used when a subtype doesn't need any special feeding (in this example, mouse)

(define-struct animal (name weight age)

#:methods

(define (feed! a) ...))

(define-struct (cat animal) (sleeping-spot)

#:methods

(define (feed! c) ...))

(define-struct (dog animal) (best-friend)

#:methods

(define (feed! d) ...))

(define-struct (mouse animal) (hiding-spot))

13 of 19

Methods with multiple arguments

  • Methods in Racket can have as many arguments as you want
  • But Racket will always choose the method based on the type of the first argument
  • There are fancier systems that allow dispatching types based on multiple arguments, but we don't have time to cover those this quarter

14 of 19

Missing methods

  • What if we call feed! on a plain animal?
    • Something we created using (make-animal …)
    • Rather than a subtype like dog or cat

  • We'll get an exception!
    • It will say that no method has been defined

  • But wait…isn't this a bug?

(define-struct animal (name weight age)

(define-struct (cat animal) (sleeping-spot)

#:methods

(define (feed! c) ...))

(define-struct (dog animal) (best-friend)

#:methods

(define (feed! d) ...))

15 of 19

Program Design

  • Not really. It depends on how we've designed our data type

  • The way we've defined animals…it doesn't make a ton of sense to create a new generic or abstract animal

  • We should be making specific subtypes for each animal type

(define-struct animal (name weight age)

(define-struct (cat animal) (sleeping-spot)

#:methods

(define (feed! c) ...))

(define-struct (dog animal) (best-friend)

#:methods

(define (feed! d) ...))

16 of 19

Abstract Types

  • In our design, you can think of animal as the base type that all animals are based on
  • But it's not intended to be instantiated directly
    • Instantiated is a fancy word for "made one"
    • We call objects of a given type instances of that type
  • It contains all the features common across all animals
  • Without having the specific details of any specific kind of animal

(define-struct animal (name weight age)

(define-struct (cat animal) (sleeping-spot)

#:methods

(define (feed! c) ...))

(define-struct (dog animal) (best-friend)

#:methods

(define (feed! d) ...))

17 of 19

Abstract Types

  • Some programming languages have a specific ability to define a type as abstract
    • It can have fields and methods
    • But you can't make an instance of it
    • You can only make an instance of its subtypes

(Note: while some languages call these abstract types, some others use this same term for a totally different concept. Just like everything else in life…there's never anything everyone agrees on).

18 of 19

A very rough analogy

  • There are a lot of different types of cats (meow)
    • They're all somewhat different
    • But you could "draw" any of them roughly
  • But you can't draw just a generic animal
    • Does it have feathers? Fur? Skin? Scales?
    • Legs? Flippers? Eyes?
  • And then you definitely can't draw a generic living thing

animal

fish

cat

bird

living-thing

plant

19 of 19

This is a whole new approach to program design

  • It's focused on seeing the world as objects which contain both data and code
    • Data in the form of fields
    • Code in the form of methods
  • It's commonly referred to as Object-Oriented Programming (OOP)
    • It's worth noting that historically this term has been used to describe a number of different things
      • Specific programming features
      • A programming methodology
      • Specific languages
  • Not all OOP languages implement the same features
    • Java and C# have interfaces
    • C++ has multiple inheritance
    • JavaScript has prototype-based inheritance