6.11 Classes

You can define new types using class. For example:

Pasta_plain = class {  
  lasagne = "large sheets";  
  fusilli = "sort of twisty";  
  radiatori = "lots of ridges";  
}

This defines a new class called Pasta_plain. The class has three members (lasagne, fusilli and radiatori), each of which has a list of char as its value. By convention, we’ve named classes with an initial capital letter, but of course you can do what you like.

You can refer to the members of a class using the class project (.) operator. For example:

Pasta_plain.lasagne == "large sheets"

You can use an expression to the right of . if you enclose it in brackets. For example:

Pasta_plain.("las" ++ "agne") ==  
  "large sheets"

Classes can contain any objects as members, including functions and sub-classes. Functions may define local classes, classes may define local functions, and all may refer to each other using the usual scope rules. For example:

Pasta_all = class {  
  filled = class {  
    tortelloni = "venus' navel";  
    ravioli = "square guys";  
  }  
  plain = Pasta_plain;  
}

When you define a class, nip2 adds a few extra members for you. name is a list of char giving the name of the class. this and super are the most-enclosing class instance and the class instance this class is derived from (see §6.11.2). nip2 also adds a default constructor: a member with the same name as the class, pointing back to the class constructor.

For efficiency reasons nip2 does not allow mutual recursion at the top level. If two functions depend on each other, neither will ever be calculated. For example:

a = 1 : b;  
b = 2 : a;

Neither a nor b will have a value.

You can have mutual recursion between class members. For example:

Fred = class {  
  a = 1 : b;  
  b = 2 : a;  
}

Now Fred.a will have the value [1, 2, 1, 2, 1, …].

6.11.1 Parameterised classes

Classes can have parameters. Parameters behave like class members initialised from arguments to the class constructor. For example:

My_pasta pasta_name cooked = class {  
  is_ready t = "your " ++  
    pasta_name ++ " is " ++ state  
  {  
    state  
      = "underdone!", t < cooked  
      = "perfect", t == cooked  
      = "yuk!";  
  }  
}

This defines a class called My_pasta which takes a pasta name and a cooking time as parameters. Once you have made an instance of My_pasta, you can test if it’s been cooked at a certain time with the is_ready member. For example:

tele = My_pasta "telephoni" 10;  
tele.is_ready 5 ==  
  "your telephoni is underdone!"

6.11.2 Inheritance

Classes can inherit from a super-class. For example:

Pasta_more = class Pasta_plain {  
  macaroni = "tubes";  
  spaghetti = "long and thin";  
  lasagne = "fairly large sheets";  
}

Here the new class Pasta_more inherits members from the previous class Pasta_plain. It also overrides the definition of lasagne from Pasta_plain with a new value. For example:

Pasta_more.macaroni == "tubes"  
Pasta_more.fusilli == "sort of twisty"  
Pasta_more.lasagne == "fairly large sheets"

You can use this and super to refer to other members up and down the class hierarchy. super is the class instance that the current class inherits from (if there’s no super-class, super has the value []), and this is the most-enclosing class instance.

Pasta_more.super == Pasta_plain  
Pasta_more.this == Pasta_more  
Pasta_more.super.this == Pasta_more

therefore:

Pasta_more.lasagne == "fairly large sheets"  
Pasta_more.super.lasagne == "large sheets"  
Pasta_more.super.this.lasagne ==  
  "fairly large sheets"

There’s a special symbol root which encloses all symbols. For example:

fred = 12;  
 
Freddage = class {  
  fred = 42;  
  mystery = root.fred;  
}

Now Fred.mystery will have the value 12.

There’s another special symbol called scope which encloses all symbols in the file this definition was loaded from. If you want to refer to another definition in the same file which is being masked somehow, use scope.

You can use the built in function is_instanceof to test whether an instance is or inherits from a class. For example:

is_instanceof "Pasta_more" Pasta_more == true  
is_instanceof "Pasta_plain" Pasta_more == true  
is_instanceof "Pasta_more" Pasta_plain ==  
  false

The super-class constructor can take arguments, and these arguments can refer to class members. For example:

Fresh_pasta pasta_name = class  
  My_pasta pasta_name cooked {  
  cooked = 2;  
}

Defines a class for fresh pasta, which always cooks in 2 minutes. You need to be careful not to make loops: if cooked did tried to refer to something in the super-class, this class would never construct properly. nip2 unfortunately does not check for this error.

Finally, the superclass can be a fully constructed class. In this case, the superclass is cloned and the new class members wrapped around it. You can use this to write a class which can wrap any other class and add members to it. Many of the toolkit menu items use this trick to enable them to work for any object type.

6.11.3 Minor class features

There are a couple of other things you can do with classes. You can define a special member called _check. If this member is defined, then when a class instance is created, the check member is returned instead of the class itself. You can use this to implement class argument type checks, for example:

Fred a b = class {  
  _check  
    = this, is_real a && is_real b  
    = error "args to Fred must " ++  
      "both be real"  
}

Defines a class called Fred which has to have two real numbers as arguments.

You can define members called oo_binary, oo_binary’ and oo_unary and do operator overloading. When nip2 sees one of the standard operators being used on an instance of your class, it will look up one of these members and pass in the name of the operator and the argument. The two forms of the binary operator member are called for the class-on-left and the class-on-rights cases. So:

x = Fred 1 2  
x + 12 == x.oo_binary "add" 12  
12 + x == x.oo_binary' "add" 12  
!x == x.oo_unary "negate"

These two features are very primitive. The _Object class in the _types toolkit builds on these to provide a fairly high-level system for checking class arguments and defining the meaning of operators. See §6.13.