type Cat = {
    getName->String
    color->String
    changeNameTo(_:String)->String
    speak->Void
    asString->String}

type Cheetah = Cat & { // object of type Cheetah must have all
    speed->Number      // of the methods specified in Cat type
    race(_:Cat)->Cat}


// Constants for use as default arguments
   
def defaultName:String = "Kitty"
def defaultColor:String = "tabby"
def defaultSpeed:Number = 70


// Constructor methods with varying parameters

method cat->Cat is public{CatClass.new(defaultColor, defaultName)}

method catOfColor(aColor:String)->Cat is public{CatClass.new(aColor, defaultName)}

method catOfColor (aColor:String) named (aName:String)->Cat is public{
    CatClass.new(aColor, aName)}

method cheetah->Cheetah is public{CheetahClass.new(defaultName, defaultSpeed)}

method cheetahNamed(aName:String)->Cheetah is public{
    CheetahClass.new(aName, defaultSpeed)}

method cheetahNamed (aName:String) withSpeed (aSpeed:Number)->Cheetah is public{
    CheetahClass.new(aName, aSpeed)}



class CatClass.new(aColor:String, aName:String){

    // a cat's color is immutable; treated as a method that returns String
    def color:String is public, readable= aColor

    // variable is private by default; don't want users to be able
    // to change the cat's name with :=
    var name:String := aName

    method getName->String is public{name}

    method changeNameTo(newName:String)->String is public{
        var oldName:String := name
        name := newName
        return oldName}

    method speak->Void is public{print "{name} says: Meow!"}

    method asString->String is public{""}
}


//alternate approach: aCat.new(c, n) equivalent to CatClass.new(c, n)

def aCat = object{

    method new(aColor:String, aName:String)-> Cat is public{

        object{

            def color:String = aColor

            var name:String := aName

            method getName->String is public{name}

            method changeNameTo(newName:String)->String is public{
                var oldName:String := name
                name := newName
                return oldName}

            method speak->Void is public{print "{name} says: Meow!"}

            method asString->String is public{""}
        }
    }
}


//an example of inheritance

class CheetahClass.new(aName:String, aSpeed:Number){
    
    //class gains all methods of an instance of CatClass
    // with color spotted and name aName
    inherits CatClass.new("spotted", aName)

    def speed:Number is public, readable = aSpeed

    // if the competitor is a cheetah, returns cat with higher speed
    // (competitor wins if the speeds are equal); otherwise, this cheetah
    // wins by default 
    method race(other:Cat)->Cat is public{
        match(other)
            case{ct:Cheetah->
                if(speed > ct.speed) then {self}
                else{ct}
            }
            case{ct:Cat-> self}
    }

   // overriding the speak and asString methods in CatClass to be more
   // apppropriate for cheetahs

    method speak->Void is public, overriding{print "{name} says: Grr."}

    method asString->String is public, overriding{""}
}