 The three cornerstones of object-oriented programming are encapsulation, inheritance, and polymorphism, but closure somewhat heretically forsakes the first two. The types we create in closure do not inherit from other types, and all instance fields are directly accessible. Closure types, however, do embrace polymorphism, and so method calls dispatch to methods of a particular type based on the type of the first argument. To have such polymorphism in closure, we use protocols, closures equivalent of Java interfaces. A protocol names a set of method signatures and types which implement the protocol must implement all of the protocol's methods. Unlike Java classes and interfaces, however, every method of a closure type must be part of a protocol implemented by that type. So if you need an operation that isn't polymorphic across types and so is specific to just one type, you don't create a method but rather just a regular function that simply takes a type as argument. So while closure does allow us to define types that are polymorphically substitutable, it otherwise leans towards a procedural style. To create data types in closure, we use def type and def record. The distinction is twofold. First, a type allows for mutable fields, but a record does not. And second, the standard libraries map functions work on a record, such that each field is a key value pair. The intention in closure is that we use def record for representing application domain data but use def type for representing data structures. For example, if our application requires an employee entity, we would define an employee with def record. A collection type of some sort, however, we would define with def type. In most application code, it's of course much more common to define application domain data than data structures, so you will probably use def record more frequently than def type. Before we use either def type or def record, let's first look at creating protocols with def protocol. The general form is this. In the def protocol macro, you supply a name. The protocol name is a symbol, and by convention it should begin with a capital letter. The protocol doc string is an optional string describing the protocol, and each method signature has this form. Each of these methods must have at least one parameter list, and each parameter list is expressed as a vector of symbols. The method doc string is an optional string describing the method. So here's what a use of def protocol looks like. This defs the protocol Roger and its two methods foo and bar into the current namespace. The foo method is polymorphically overloaded to take either one argument or three arguments, but the bar method takes just one argument. Notice that the first parameter of both methods is by convention called this, because the object which implements this protocol will be passed as the first argument. When we then invoke a method, the method dispatches the call to the method implementation of that type. Now we can use def record, which has this general form. The name is a symbol which by convention should begin with a capital letter, and the fields are expressed as a vector of symbols. Each spec is a list starting with specification of some protocol, followed by implementations of that protocol's methods. The order in which we write the specs does not matter. Each method in a spec has the form method name parameter list body, and the order in which we write the methods within a spec also does not matter. So putting this all together, here's an example def record. This record Nadine has three fields x, y, and z, and it implements one protocol Roger. Notice a few things here. First, we have two implementations of foo because that is what the Roger protocol requires. Second, parameter names needn't match between the protocol methods and their implementations. All that matters is that the number of parameters matches. Third, notice in the methods we can reference the fields by name. Of course, though, if you create a local the same name as a field, the field will be inaccessible in that local scope. One subtlety not illustrated here is that the method bodies do not see any variables of enclosing local scope. So if, say, this def record were inside a function, the methods defined inside could not use the parameters of that enclosing scope. Be clear what is getting created here by def record. A new Java class is created and mapped to the symbol Nadine in the current namespace, but def record does not add the implemented methods to the current namespace. Rather, def record simply updates the existing methods of the protocol with new implementations. Also be clear about how we specify the protocols. The def record macro expects the protocol to be specified by a symbol rather than just any arbitrary expression, but the symbol is resolved by the normal evaluation rules. Here, if the Roger protocol were defined in another namespace, we'd have to fully qualify its name or refer it into the current namespace. In contrast, the def record macro checks the method names against the signatures in the protocol, so the symbols foo and bar here are not resolved like ordinary symbols. Closure types have no formal notion of constructors. To instantiate a record, we simply use the new special form passing values for each field. So this here, for example, returns a new instance of Nadine, where the field x has a value 3, the field y has the value of the string high, and the field z has the value false. To invoke the methods of Nadine, we invoke the protocol's methods like normal functions, but pass the instance as the first argument. So say, assuming foo here resolves to the method created by def protocol, this invokes that method with the Nadine instance as its first argument, and so invokes Nadine's implementation of foo. To access the fields of an Nadine instance, we can use the dot special form as we would with any other Java instance. This, for example, returns the value of the field x of the instance. As mentioned, records also support the map interface to access their fields, so say we can use the get function to return the same field, though note that this way we specify the field as a keyword. Used on a record, the map functions will normally return a new record of the same type, rather than a regular map. For example, this first call to a soak returns a new Nadine instance, not a new map. However, if the desoke function removes one of the fields that make up that record type, then the return to value is a regular map, and not an instance of the record. The first desoke here removes a field, so it returns a map, but the second desoke here removes a key that isn't a field, so it returns a Nadine record. Now let's use the def type macro, which has the same basic syntax as def record. The only new thing here is that the k field is designated as mutable by giving the k symbol a metadata map with a key, volatile mutable, having the value true. We could use the key unsynchronized mutable instead. We won't discuss the distinction here, but it relates to multithreading. Because the k field is mutable, we can use the set special form to assign to it, as we do here in the second implementation of foo, where we assign the sum of the parameters a and b to the k field. Mutable fields are actually private to their class, so we cannot access them with the dot special form. Also remember that, unlike records, types do not support the map or sequence interfaces, and so we cannot use the map and sequence functions on instances of a type. Again, the intended purpose of def type is to implement data structures. With data structures, we might need mutable fields for efficiency reasons, and it generally just doesn't make sense to treat their fields as keys of a map. The reify macro returns a one-off instance of an anonymous type with no fields. The syntax for reify is like def type and def record, except without the names or fields. So this here returns a single instance of an anonymous type with no fields, that implements the Roger protocol. Unlike a def type or def record, the methods defined in reify may actually access the local bindings of the enclosing context. Here where we use reify inside a let, the methods defined in reify can use the x bound in the let form. When working with types and records defined in libraries, we may wish to supplement their methods without having to touch the original type definition, either because we'd rather not edit the library source code or perhaps because we just don't have access to its source. The extend type macro adds additional protocol specs to an existing type or record. Here we implement methods for the protocols Albert and Philippa on the already existing record type Nadine. Hereafter we can invoke these methods on all Nadine instances, including those previously created. A limitation of extend type is that we cannot directly refer to the fields of the type we're extending in the new methods. We can, however, simply use the dot operator on the this parameter, though remember that the dot operator cannot retrieve a type's mutable fields. Notice that we implemented a method foo for the Philippa protocol even though our Nadine record already had foo methods implemented for the Roger protocol. This isn't a problem because the protocols Roger and Philippa create separate foo method objects. These objects just happen to be mapped in their respective namespaces to the same name, but we can deal with this name conflict just as we would for any other kinds of objects. As a matter of style and convenience, we may sometimes prefer to use the misleadingly named extend protocol macro. This macro does not modify an existing protocol, but rather implements the methods of that protocol for one or more previously defined types or records. Here we add method implementations of the Albert protocol to the previously defined record type Nadine and the previously defined type Karen.