Blog › ICP guides

Grace developer on retainer: inherits vs uses mixin composition, structural typing, object model, dialect system, and teaching-language idioms on monthly retainer

October 1, 2026 · ~16 min read

A Grace developer was building a reusable graphics library for an introductory CS course. They defined a base type type Drawable = { method draw() -> Done } and wrote a ColoredShape object that was supposed to satisfy it. They wrote inherits ColorableBase.new() inside ColoredShape, expecting the clause to act as a mixin — borrowing coloring methods from ColorableBase without establishing a subtype relationship. ColorableBase itself had a draw() method that painted shapes in a default color. The developer also wrote their own draw() method directly inside ColoredShape, intending it to override the base behavior with color-specific rendering logic. The call sites each passed a ColoredShape to a function annotated (shape : Drawable) -> Done and invoked shape.draw(). Instead of the ColoredShape’s own draw() running, the inherited draw() from ColorableBase was dispatched at both call sites — the shapes drew in the default color, ignoring the override entirely. Wrong draw behavior: 2 call sites → 0 after fix. The fix: replace inherits ColorableBase.new() with uses ColorableBase.new(). With uses, methods are copied in as a mixin and the object’s own draw() definition takes precedence. The shadowing disappeared, the correct draw() ran, and the structural type checker confirmed that ColoredShape still satisfied Drawable because it possessed the required draw() method signature.

The work log said “fixed draw dispatch on ColoredShape, switched inherits to uses, 5h.” It cannot explain why inherits shadowed the developer’s own method override, what the difference between inherits and uses is at the level of method resolution order, or why a one-keyword change required five hours to diagnose rather than five minutes to type. A reader of that log sees a trivial substitution of one seven-letter word for one four-letter word. What is invisible is the diagnostic path: confirming that ColoredShape’s draw() was present in the source (it was), confirming that Grace’s structural type checker accepted ColoredShape as a Drawable (it did, because the method signature was present regardless of which definition would be dispatched), tracing the method resolution order to discover that inherits installs the parent’s methods in the resolution chain in a way that can shadow the child’s overrides unless the override is explicitly signaled, and then determining that the entire composition model was wrong — that the developer had intended mixin composition from the beginning, not subtyping. The structural type checker’s acceptance of the pre-fix code was itself a source of confusion: no type error appeared, the program ran, and the observable symptom was behavioral (wrong color) rather than structural (wrong type). Behavioral bugs in a structurally-typed language with clean semantics are the hardest to attribute to a language-model misunderstanding.

Grace language overview: teaching OO at Portland State, Victoria, Williams, and Harvey Mudd

Grace was designed by Andrew Black (Portland State University), James Noble (Victoria University of Wellington), Kim Bruce (Pomona College, previously Williams College), and Michael Homer, with additional contributions from a collaboration spanning Harvey Mudd College and several other institutions. The design work began around 2012 and was published in a series of academic papers including “Grace: the absence of (inessential) difficulty” and subsequent papers on Grace’s type system, dialect mechanism, and pedagogical goals. The language is explicitly positioned as a teaching language — not a research language intended for production deployment, and not a general-purpose language with teaching as a secondary use. Every design decision in Grace was evaluated against the question: does this help or hinder introductory CS students learning object-oriented programming concepts?

The motivation for Grace arose from dissatisfaction with the two most common choices for introductory OO courses: Java and Python. Java’s ceremony is heavy for beginners — public static void main(String[] args) must be explained before students can write “hello world,” generics are syntactically noisy, and the class-must-contain-main requirement obscures the relationship between objects and programs. Python avoids ceremony but is too dynamic for type pedagogy: Python’s default dynamic typing makes it difficult to teach type systems as a core concept, and Python’s scope rules and late binding produce surprises that distract from OO concepts. Grace aims for a middle path: clean object-oriented semantics with no ceremony tax, optional gradual typing that can be introduced incrementally as students are ready, a clear distinction between mutable and immutable slots, and a dialect system that allows course instructors to restrict or extend the language to match the specific concepts being taught in any given week.

Grace has two primary implementations. The first is grace-js, which compiles Grace source code to JavaScript and runs in a browser-based IDE called the Minigrace online environment. This means students can write and run Grace programs in a web browser without installing anything — a significant practical advantage for introductory courses where environment setup is a barrier. The Minigrace browser environment provides a code editor, a console, and a canvas for graphical output, all accessible from any web-connected device. The second implementation is JGSL (Grace on the JVM), which compiles Grace to JVM bytecode and can be run from the command line. Testing in Grace uses a test framework invoked through gracetest. Production use of Grace is confined almost entirely to academic settings: universities that have adopted Grace for introductory CS instruction, researchers extending the language or its tools, and developers building course libraries and dialect implementations.

Grace’s object model: object literals, class factories, def, var, and method syntax

The foundational unit of Grace is the object literal. object { def x = 42; var y := 0; method greet(name : String) { "Hello, " ++ name } } creates an object with one immutable slot x (bound to 42 with def), one mutable slot y (initialized to 0 with var, updated with :=), and one method greet that takes a String parameter and returns a string concatenation. The ++ operator is Grace’s string concatenation. Method return types are annotated after ->; -> Done signals that the method returns no meaningful value (analogous to void in Java). Method parameters are written as name : Type. Multi-part keyword method names are also possible: method moveTo(x : Number) y(y : Number) { ... } defines a method called moveTo(x)y(y), which is called as shape.moveTo(10) y(20). This naming convention is borrowed from Smalltalk and makes call sites read more like natural language.

A class definition in Grace is syntactic sugar for a factory method: class ColorableBase.new() { def defaultColor = "black"; method draw() -> Done { ... } } defines a factory method ColorableBase.new() that, when called, creates and returns a new object literal with those slots and methods. The returned object is not an instance of a class in Java’s sense — it is an object literal, and Grace’s type system checks it structurally, not by class identity. Two objects created by the same class factory are structurally identical but not the same object. The class definition syntax is provided so that introductory students can use familiar OO terminology (“class,” “instantiation”) while the underlying semantics remain those of object literals and factory methods. Advanced students can also use object literals directly, creating one-off objects without a class definition, which is useful for singletons and ad-hoc test objects in course assignments.

Both object literals and class-factory objects can include inherits and uses clauses, written inside the object body. inherits ColorableBase.new() calls the factory method, creates a parent object, and incorporates the parent’s methods and slots into the current object with a subtype relationship and a super delegation chain. uses ColorableBase.new() calls the factory method, creates a mixin object, and copies its methods into the current object as a flat set of definitions, without creating a subtype relationship and without establishing a super chain to the mixin. The distinction is fundamental to Grace’s composition model, and confusing the two — as the graphics library developer did — produces bugs that are invisible to the type checker but behaviorally wrong at runtime.

inherits vs uses: subtyping, method resolution order, and why one keyword changes everything

When an object uses inherits ParentClass.new(args), Grace creates an actual subtype relationship. The current object’s structural type includes all the methods from the parent. The method resolution order includes the parent’s methods: if the current object does not define a method that the parent defines, calls to that method are dispatched to the parent’s definition. If the current object does define a method with the same name as the parent’s, the intended semantics are that the child’s definition overrides the parent’s — but in Grace, this override must be managed carefully. More critically, super.draw() is valid after inherits: it explicitly delegates to the parent’s draw() implementation from inside the child’s draw() definition. The subtype relationship also means that the Grace type system treats the child object as a subtype of the structural type implied by the parent’s interface.

When an object uses uses MixinName.new(args), Grace copies the mixin’s methods into the current object as a flat set of definitions. There is no subtype relationship to the mixin. The mixin’s methods are available on the current object as if they were defined directly in the object body, but the current object is not considered a subtype of any type implied by the mixin’s class hierarchy. Crucially, the current object’s own method definitions take precedence: if both the mixin and the object body define draw(), the object body’s definition wins. super.draw() is not valid after uses; attempting it produces a compile error. The structural type checker verifies the current object against declared types solely based on the methods present in the final object, regardless of whether they came from the object body, from inherits, or from uses.

The bug in the graphics library arose because inherits was used where uses was intended. The developer wrote inherits ColorableBase.new(), which incorporated ColorableBase’s draw() into the resolution chain as an inherited method. When Grace resolved a draw() call on a ColoredShape, the inherited draw() from ColorableBase was dispatched rather than the ColoredShape’s own draw() definition. This is counterintuitive to developers from Java or Python backgrounds, where a method defined in the child class always shadows a same-named method in the parent without special syntax — in those languages, the child’s definition wins by default in all cases. Grace’s composition semantics around inherits are more nuanced: they are designed to make the subtype relationship explicit and to allow super delegation, but this comes with method resolution behavior that can surprise developers who expect Java-style automatic shadowing. The fix — replacing inherits with uses — switched to the flat mixin model where the object’s own draw() is unconditionally preferred. Two call sites that were dispatching ColorableBase.draw() now correctly dispatched ColoredShape.draw(). Wrong draw behavior: 2 → 0.

Grace’s structural type system: type as a set of signatures, not a class lineage

Grace uses structural typing: a type is a declaration of the method signatures that an object must provide, written as type T = { method m(a : A) -> B; method n(c : C) -> D }. Any object that has all those methods with matching signatures satisfies type T, regardless of how the object was constructed or what classes or mixins it used. This is in contrast to Java’s nominal typing, where an object is an instance of a type only if it was declared to implement that type with implements T. In Grace, the connection between an object and a type is checked by the type checker by inspecting the object’s actual method set, not by inspecting its class declaration. The type Drawable = { method draw() -> Done } is satisfied by any object that has a draw() method returning Done, regardless of whether the object inherits from anything, uses any mixins, or is a standalone object literal.

Structural typing means that both inherits and uses contribute to an object’s satisfying a structural type, because both make methods available on the object. The difference is only in the source and resolution order of those methods, not in whether they count toward type satisfaction. This was the source of the graphics library bug’s invisibility to the type checker: ColoredShape had a draw() method available whether that method came from ColorableBase via inherits or from the object’s own body. The structural type checker confirmed that ColoredShape satisfied Drawable in both cases. The type check passed. No error appeared. The only observable consequence of the wrong inherits was the wrong method being dispatched at runtime, which produced incorrect rendering behavior.

Grace’s structural type system supports parameterized types and self-types for more advanced use. A self-type refers to “the type of the object on which this method is called,” and is useful for methods like copy() that return the same type as their receiver. Parameterized types follow a syntax similar to Java generics but are checked structurally. In introductory CS course settings, most type usage stays at the level of simple structural types like Drawable, Comparable, or Printable, and the pedagogical value of structural typing is that students see types as specifications of behavior rather than labels attached to classes. A student who writes a new object literal and then checks whether it satisfies a type is doing type-directed programming: “does my object have the right methods?” is a more immediately understandable question than “does my object extend the right class?” Grace’s type system is specifically designed to make this question answerable at the level of method signatures.

self, outer, and super in Grace: current object, enclosing object, and delegated parent

Grace’s three special self-referential keywords represent three different objects. self is the standard one: it refers to the current object, the object whose method is currently executing. Any method call written as self.methodName(args) is an explicit dispatch on the current object, subject to the current object’s full method resolution order. self.draw() inside a ColoredShape method dispatches draw() on the ColoredShape object, which after the fix (with uses) correctly reaches the object’s own draw() definition.

outer is specific to Grace’s support for nested object literals. Grace allows objects to be defined inside other objects, and the inner object can refer to the enclosing object with outer. This is not a class hierarchy reference — it is a lexical nesting reference. If an object A contains an object literal B in its body, and B’s methods need to call methods on A, they use outer. This is conceptually similar to how a Java inner class accesses its enclosing class with OuterClass.this.method(), but in Grace the nesting is of object literals rather than class definitions. The outer keyword can be chained: outer.outer.methodName() refers to the object enclosing the object enclosing the current object. Confusion between self and outer is a common bug in Grace code that uses nested object literals — a method that writes self.draw() when it means outer.draw() will dispatch on the wrong object entirely.

super is only valid when the current object used inherits. After inherits ColorableBase.new(), super.draw() inside the ColoredShape body explicitly delegates to ColorableBase’s draw() method, bypassing the current object’s own method resolution. This is the standard OO “call the parent” pattern, and in Grace it is only semantically valid when a subtype relationship exists via inherits. After switching to uses, any super.draw() calls in the object body become compile errors, because uses establishes no super delegation chain. This was relevant in the graphics library case: the developer had no super.draw() calls in their ColoredShape (they intended the mixin to be a source of helper methods, not a delegated parent), so the switch from inherits to uses required no additional changes to the method bodies. If there had been super calls, those would have needed to be converted to direct method calls or removed, adding further diagnostic work.

Grace’s dialect system: teaching frameworks that restrict or extend the language

One of Grace’s most distinctive features is its dialect system, designed specifically for the incremental, modular pedagogy of introductory CS courses. A dialect is specified at the top of a Grace source file with dialect "DialectName". The dialect can prohibit certain language features, add domain-specific vocabulary, restrict the type system to a subset, or provide a custom standard library. The motivation is that introductory CS courses typically introduce language concepts in sequence: week 1 might cover expressions and method calls; week 3 might introduce loops; week 5 might introduce mutation. A dialect designed for week 1 can prohibit loop syntax and mutation syntax, ensuring that students who try to use a while loop get a clear error message (“this dialect does not permit while loops”) rather than a confusing runtime error or unexplained behavior. The dialect restriction is enforced at compile time, making the pedagogical constraints explicit.

Dialects can also add domain-specific vocabulary. A graphics-assignment dialect might pre-import a canvas object, define a color type, and provide helper methods for drawing primitives, so that students can write canvas.drawCircle(x, y, radius) without needing to understand how the canvas is connected to the runtime. A physics-simulation dialect might provide gravity as a constant and Body.new(mass, velocity) as a factory for simulating Newtonian motion. A music-composition dialect might define Note.new(pitch, duration) and Sequence.new() for composing melodies. In each case, the dialect does what a course framework would do in Java (providing a standard import set and starter code), but the dialect mechanism integrates it at the language level rather than at the IDE level. A developer building course libraries for Grace is essentially a dialect author: they define the vocabulary, restrictions, and standard objects that students will work with throughout a course unit.

Retainer work involving dialects includes debugging dialect constraint errors (a student’s code tries to use a var mutation assignment in a dialect that prohibits mutation; the error message says “mutation not permitted in this dialect”; the developer needs to restructure the code to be non-mutating), extending existing dialects with new vocabulary for new assignment types, and testing that dialect restrictions actually fire on the intended language features. Because dialects restrict the compiler at a meta-level, debugging dialect behavior requires understanding both the Grace language semantics and the dialect implementation API. This is specialized knowledge that is rarely documented as clearly as the core language specification, and it represents a significant portion of the invisible hours in a Grace developer retainer serving a university CS department.

How HourTab tracks Grace developer retainer hours

Grace retainer work is invisible by the same mechanism that makes all structural-typing bugs invisible: the type checker accepted the wrong code. After inherits ColorableBase.new(), the ColoredShape object had a draw() method — it had the ColorableBase one. The structural type checker for Drawable checked for the presence of a draw() -> Done signature and found it. No error was raised. The program compiled. The program ran. The rendering was wrong. The wrongness was purely behavioral: the shapes appeared in the default color rather than the assigned color. Connecting that visual symptom to its root cause — an inherits clause that installed a parent’s draw() into the method resolution chain ahead of the child’s override — required tracing the method resolution semantics of Grace’s inherits clause in detail, understanding that Grace’s inherits model differs from Java’s in this regard, and identifying uses as the correct alternative. The five hours were not spent typing one word; they were spent understanding why the type checker was satisfied, why the program ran without errors, and why the output was nonetheless wrong.

The work log entry “switched inherits to uses, fixed draw dispatch, 5h” is accurate as a time record and misleading as a value record. Five hours to change seven letters to four letters is what a client sees. What the client does not see without a structured log is the chain of inference: confirmed that the source code for ColoredShape.draw() was present (it was); confirmed that the structural type checker accepted ColoredShape as a Drawable (it did); traced Grace’s method resolution order for an object with inherits vs one with uses; identified that inherits installs the parent in the resolution chain in a way that can override the child’s own definition; confirmed that uses is the semantically correct clause for the mixin composition the developer intended; verified the fix at both call sites; confirmed that no super calls existed in the object body (which would have needed to be removed after the clause change); and ran the gracetest suite to confirm no regressions in other structural type checks across the library. Each of those steps is invisible in a one-line log. Collectively they represent the investigation that justified the time.

HourTab gives Grace developers a public retainer-hours URL they send to the academic departments, course coordinators, and instructional technology teams that commission Grace library and dialect development. These clients are typically university CS faculty or curriculum developers who understand software but do not spend their days tracing method resolution orders in a teaching language. For Grace retainers, each work log entry should name the mechanism at the level of the composition model: which clause was used, which method was shadowed, which clause corrects it, and how many call sites were affected. A categorized HourTab log entry for the graphics library fix covers: composition category (object: ColoredShape; clause used: inherits; parent: ColorableBase; method shadowed: draw(); fix: replace inherits with uses; call sites corrected: 2; wrong-dispatch count: 2 → 0); type category (type: Drawable; method checked: draw() -> Done; type-check result before fix: passed; type-check result after fix: passed; behavioral mismatch: resolved); and gracetest category (test suite run before fix: 4 of 6 passing; after fix: 6 of 6 passing). Each line gives the department coordinator a clear record of what was investigated, what was found, and what changed. The log entry is the proof of value that gets the retainer renewed.

Track Grace developer retainer hours without the status emails

HourTab gives Grace developers a public URL per client retainer. One link, no login, live burn-down. Your university clients stop asking “how many hours do I have left?” and your composition-model audit log — inherits-vs-uses diagnosis, structural type mismatch tracing, method resolution order analysis, dialect constraint debugging — becomes the proof of value that gets the retainer renewed each semester.

See HourTab pricing →

FAQ: Grace developer retainers

What does a Grace developer on retainer typically do?

A Grace developer on monthly retainer covers Grace’s object model (object literals created with object { ... }; class definitions as factory methods; def for immutable slots, var for mutable slots; method declarations with multi-part keyword names; inherits and uses clauses for incorporating behavior from other objects), the inherits-vs-uses distinction (inherits ParentClass.new(args) creates a subtype relationship and a super delegation chain; uses MixinName.new(args) copies methods into the object without subtyping or super; method resolution order differs; only inherits makes super.method() valid; uses avoids shadowing the child’s own method overrides), Grace’s structural type system (a type is a set of method signatures written as type T = { method m(a : A) -> B }; any object with all required methods satisfies T regardless of class hierarchy; both inherits and uses contribute methods that can satisfy structural types; the structural checker requires the declared type’s full method set to be present on the object), the self/outer/super keywords (self is the current object; outer refers to the enclosing object in a nested object literal; super delegates to the inherited parent’s method implementation and is only valid when inherits is used), Grace’s dialect system (a dialect is specified with dialect "DialectName" at the top of a source file; dialects restrict or extend the language for pedagogical purposes; they can prohibit mutation or loops to focus students on particular concepts, or add domain vocabulary for physics or graphics assignments), and the two Grace implementations: grace-js running in the Minigrace browser IDE, and JGSL on the JVM. Common retainer work covers structural type mismatch diagnosis, inherits-to-uses refactoring, object literal nesting and outer-reference bugs, dialect constraint debugging, and advising instructors on library design for introductory CS course assignments.

What Grace work is most commonly underlogged in a retainer?

inherits-vs-uses restructuring (identifying that an object used inherits where the intent was mixin composition; tracing which method in the parent is shadowing the child’s override; determining that the fix is to replace inherits with uses; verifying that super references are removed or converted because super is not valid after uses; 4–7 hrs invisible); structural type mismatch diagnosis (determining which method signatures are present on an object and which are missing or have wrong parameter types; tracing whether a method came from inherits, from uses, or from the object’s own body; understanding that structural typing checks signatures not class lineage; 3–6 hrs invisible); method resolution order debugging (tracing the order in which Grace resolves a method call when an object has both its own method definition and an inherited one; confirming that uses copies methods in but the child’s own definitions take precedence; 4–7 hrs invisible); object nesting and outer-reference bugs (Grace allows object literals to be nested; outer refers to the immediately enclosing object; confusion between self and outer when an inner object attempts to call a method on the enclosing object; 3–5 hrs invisible); dialect constraint debugging (understanding which language features the active dialect prohibits; diagnosing compile errors caused by using a prohibited feature such as a while loop in a dialect that restricts iteration to recursion; 2–4 hrs invisible); and grace-js browser IDE debugging (tracing errors in the Minigrace online environment; understanding which error messages correspond to which Grace semantic rules; diagnosing the difference between a type error caught at compile time and a runtime message-not-understood error; 2–5 hrs invisible).

What are typical Grace developer retainer rates?

Entry-level Grace developers (1–2 years, basic object model, structural typing, object literal syntax) bill at $55–$95/hr. Mid-level Grace programmers (2–4 years, inherits-vs-uses composition, structural type mismatch diagnosis, dialect design, course library authoring) bill at $85–$155/hr. Senior Grace language developers (4+ years, advanced dialect authoring, JGSL or grace-js compiler integration, deep structural type system work, multi-university course library maintenance, pedagogical tool design) bill at $130–$230/hr. Monthly retainer ranges: $1,500–$3,200/mo for advisory engagements (curriculum library design, 10–20 hrs/mo), $3,500–$9,500/mo for full engagement retainers (ongoing library development, dialect maintenance, compiler debugging, and course support infrastructure).

What should a Grace developer retainer agreement include?

A Grace developer retainer agreement should specify: object model scope (whether work covers object literals only or also class definitions as factory methods; whether inherits chains are in scope, uses-based mixin composition, or both; whether multi-part keyword method names are used; whether the engagement covers object nesting and outer references); type system scope (which structural types are defined and checked; whether the engagement covers diagnosing missing method signatures, wrong parameter types, or wrong return types; whether self-type and parameterized types are in scope; whether the type checker in the active Grace implementation catches all intended errors); composition model scope (which objects use inherits and which use uses; whether any super calls exist and under which objects; whether existing inherits relationships need to be reclassified as uses after audit; what the test coverage looks like for dispatch behavior); dialect scope (which dialect is active in each source file; which language features the dialect permits or prohibits; whether the engagement includes authoring new dialects or only debugging code within existing ones; whether the dialect is a published academic dialect or a custom course-specific one); implementation target (whether the engagement targets grace-js in the Minigrace browser IDE, JGSL on the JVM, or both; which version of the grace-js compiler is in use; how gracetest is configured for automated testing); and hour logging format (composition: inherits vs uses, method shadowed, fix applied, call sites corrected, mismatches before and after; type: type name, missing or mismatched method, fix applied; dispatch: object name, method name, which definition was invoked before fix, which after; dialect: prohibited feature encountered, error message, fix applied).

How should Grace developer retainer hours be logged?

Log each Grace retainer session with: composition category (object name; clause used: inherits or uses; parent or mixin name; method that was shadowed or incorrectly resolved; whether a super call was present and whether it was valid; fix applied: replace inherits with uses or restructure the object hierarchy; number of call sites affected; wrong-dispatch count before and after fix); type mismatch category (type name; which method signature was missing or mismatched on the object; whether the object’s method came from its own body, from inherits, or from uses; parameter types declared vs actual; return type declared vs actual; fix applied; type-check errors before and after fix); method resolution order category (object name; method name; which candidate definitions were present: own body, inherited from parent, copied via uses; which definition was invoked before the fix; which definition is invoked after the fix; whether MRO was the root cause or a symptom of a composition-clause mistake); outer-reference category (inner object name; outer object name; method called on outer; whether self was used where outer was needed or vice versa; fix applied); dialect category (dialect name; prohibited feature encountered; line number; error message; fix applied: restructure code to avoid the prohibited feature, or switch to a less restrictive dialect if the restriction was unintentional); and implementation category (target: grace-js or JGSL; error message text; whether the error was a compile-time type error or a runtime message-not-understood; fix applied; gracetest pass rate before and after fix).