A type-directed, dictionary-passing translation of method overloading and structural subtyping in Featherweight Generic Go

Abstract Featherweight Generic Go (FGG) is a minimal core calculus modeling the essential features of the programming language Go. It includes support for overloaded methods, interface types, structural subtyping, and generics. The most straightforward semantic description of the dynamic behavior of FGG programs is to resolve method calls based on runtime type information of the receiver. This article shows a different approach by defining a type-directed translation from 
${\textrm{FGG}^{-}}$
 to an untyped lambda-calculus. 
${\textrm{FGG}^{-}}$
 includes all features of FGG but type assertions. The translation of an 
${\textrm{FGG}^{-}}$
 program provides evidence for the availability of methods as additional dictionary parameters, similar to the dictionary-passing approach known from Haskell type classes. Then, method calls can be resolved by a simple lookup of the method definition in the dictionary. Every program in the image of the translation has the same dynamic semantics as its source 
${\textrm{FGG}^{-}}$
 program. The proof of this result is based on a syntactic, step-indexed logical relation. The step index ensures a well-founded definition of the relation in the presence of recursive interface types and recursive methods. Although being non-deterministic, the translation is coherent.


Introduction
Go (2022) is a statically typed programming language introduced by Google in 2009. It supports method overloading by allowing multiple declarations of the same method signature for different receivers. Receivers are structs, similar to structs in C. The language also supports interfaces; as in many object-oriented languages, an interface consists of a set of method signatures. But unlike in many object-oriented languages, subtyping in Go is structural not nominal.
Earlier work by Griesemer et al. (2020) introduces Featherweight Go (FG), a minimal core calculus that covers method overloading, structs, interfaces and structural subtyping. Their work specifies static typing rules and a dynamic semantics for FG based on runtime method lookup. However, the actual Go implementation appears to employ a different dynamic semantics. Quoting Griesemer and co-workers: "Go is designed to enable efficient implementation. Structures are laid out in memory as a sequence of fields, while an interface is a pair of a pointer to an underlying structure and a pointer to a dictionary of methods." In our own prior work Wehr, 2021, 2022), we formalize a type-directed dictionary-passing translation for FG and establish its semantic equivalence with FG's dynamic semantics. Griesemer and coworkers also introduce Featherweight Generic Go (FGG), an extension of FG with generics. In this work, we show how our translation approach can be extended to deal with generics. Our focus is on the integration of generics with method overloading and structural subtyping. Hence, we consider FGG − , which is equivalent to FGG but does not support type assertions. Our contributions are as follows: • We specify the translation of source FGG − programs to an untyped -calculus with recursive let-bindings, constructors and pattern matching. We employ a dictionarypassing translation schemeà la type classes (Hall et al., 1996) to statically resolve overloaded method calls. The translation is guided by the typing of the FGG − program. As the typing rules include a subsumption rule, the translation is inherently non-deterministic. • We establish the semantic correctness of the dictionary-passing translation. The result relies on a syntactic, step-indexed logical relation to ensure well-foundedness of definitions in the presence of recursive interface types and recursive methods. • We show that values produced by different translations of the same program are identical up to dictionaries embedded inside these values. • We report on an implementation of the translation.
The upcoming Section 2 presents an overview of our translation by example. Section 3 gives a recap of the source language FGG − , whereas Section 4 defines the target language and the translation itself. Next, Section 5 establishes the formal properties of the translation, rigorous proofs of our results can be found in the Appendix. Section 6 presents the implementation, Section 7 covers related work. Finally, Section 8 summarizes this work and points out directions for future work. types for integers and strings, with an operator + for string concatenation and a builtin function intToString, with definitions of local variables, and with function definitions. We will first consider FGG − without generics to highlight the idea behind our typedirected dictionary-passing translation scheme. Then, we show how the translation scheme can be adapted to deal with the addition of generics. All examples have been checked against our implementation 1 of the translation.

Starting Without Generics
The upper part of Figure 1 shows an (extended) FGG − program for formatting values as strings. The code does not use generics yet.
Structs in Go are similar to structs in C, a syntactic difference is the Go convention that field or variable names precede their types. Here, struct Num has a single field val of type int, so it simply acts as a wrapper for integers.
Interfaces in Go declare sets of method signatures sharing the same receiver where method names must be distinct and the receiver is left implicit. Interfaces are types and describe all receivers that implement the methods declared by the interface. In our example, interface Format declares a method format for rendering its receiver as a string. The second interface Pretty also declares format, but adds a second method pretty with the intention to produce a visually more attractive output.
Methods and functions are introduced via the keyword func. A method can be distinguished from a function as the receiver argument in parenthesis precedes the method name. Methods can be overloaded on the receiver type. In lines 5 and 6, we find methods format and pretty, respectively, for receiver type Num. In the body of format, we assume a builtin function intToString for converting integers to strings. Lines 8 and 10 define two functions.
An interface only names a set of method signatures, its definition is not required for a method to be valid. For example, the methods in lines 5 and 6 could be defined without the interfaces in lines 2 and 3, or the methods could be placed before the interfaces.
However, interfaces and method definitions imply structural subtype relations. Interface Format contains a subset of the methods declared by interface Pretty. Hence, Pretty is a structural subtype of Format, written (1) Pretty <: Format. Line 5 defines method format for receiver type Num, we say that Num implements method format. Hence, Num is a structural subtype of Format, written (2) Num <: Format. Receiver Num also implements the pretty method, see line 6. Hence, we also find that (3) Num <: Pretty. Structural subtype relations play a crucial role when type checking programs.
For example, consider the function call formatSome(Num{1}) in line 11. Here, Num{1} is a value of the Num struct with val set to 1. From above we find that (2) Num <: Format. That is, Num implements the Format interface and therefore the function call type checks. Consider the variable declaration and assignment in line 12. Value Num{2} is assigned to a variable of interface type Pretty. Based on the subtype relation (3) Num <: Pretty the assignment type checks. Consider the function call formatSome(pr) in line 13, where pr has type Pretty. Based on the subtype relation (1) Pretty <: Format the function call type checks.
In Griesemer et al. (2020), the dynamic behavior of programs is explained via runtime lookup of methods, where based on the receiver's runtime type the appropriate method definition is selected. The Go (and FGG/FGG − ) conditions demand that for each method name and receiver type there can be at most one definition. This guarantees that method calls can be resolved unambiguously.

Type-Directed Translation
We explain the meaning of extended FGG − programs by translation into an untyped -calculus with recursive top-level definitions, let-bindings, pattern matching, integers, strings, an operator ++ for string concatenation, and a builtin function intToString. We will use a Haskell-style notation.
Method definitions belonging to an interface are grouped together in a dictionary of methods. Thus, method calls can be turned into primitive function calls by simply looking up the appropriate method in the dictionary. Structural subtype relations are turned into coercion functions that transform, for example, a struct value into an interface value to make sure that the appropriate dictionaries are available. Where to insert dictionaries and coercions in the program is guided by the type checking rules. Hence, the translation is type-directed.
Our translation strategy can be summarized as follows: Struct. An FGG − value at the type of a struct with fields is represented by an -tuple holding the values of the fields. We call such an -tuple a struct value.
Interface. An FGG − value at the type of an interface is represented as a pair ( , D), where is a struct value and D is a method dictionary. Such a method dictionary is a tuple holding implementations of all interface methods for , in order of declaration in the interface. We call the pair ( , D) an interface value. Coercion. A structural subtype relation <: implies a coercion function to transform the target representation of an FGG − value at type into a representation at type .
The lower part of Figure 1 gives the translation of our running example. In this overview section, we identify a 1-tuple with the single value it holds.
For each field name, we assume a helper function to access the field component, see line 16. Method calls on interface values lookup the respective method definition in the dictionary and apply it to the struct value embedded inside the interface value. See lines 19-21. Method definitions translate to plain functions, see lines 24-25. Recall that for each method name and receiver type there can be at most one definition. Hence, the generated function names are all distinct.
Structural subtype relations translate to coercions, see lines 28-30. For example, (2) Num <: Format translates to the toFormat Num coercion. Input parameter x represents a target representation of a Num value. The output (x,format Num ) is an interface value holding the receiver and the corresponding method definition. Coercion toPretty Num corresponds to (3) Num <: Pretty and coercion toFormat Pretty to (1) Pretty <: Format.
The translation of the main function, starting at line 35, is guided by the type checking of the source program. Each application of a structural subtype relation leads to the insertion of the corresponding coercion function in the target program. For example, the function call formatSome(Num{1}) translates to formatSome (toFormat Num 1) because typing of the source requires (2) Num <: Format. The other coercions arise for similar reasons.

Adding Generics
We extend our running example by including pairs, see Figure 2. The struct type Pair[T Any, U Any] is generic in the type of the pair components, T and U are type variables. When introducing type variables we must also specify an upper type bound to constrain the set of concrete types that will replace type variables. The bounded type parameter T Any can therefore be interpreted as ∀T.T <: Any. Upper bounds are always interface types. The upper bound Any is satisfied by any type because the set of methods that need to be implemented is empty.
To format pairs, we need to format the left and right component that are of generic types T and U. Hence, the method definition for format in line 4 states the type bound Format for type variables T and U. In general, bounds of type parameters for the receiver struct of a method declaration must be in a covariant subtype relation relative to the  bounds in the struct declaration. This is guaranteed in our case as we find Format <: Any. Importantly, the type bounds in line 4 imply the subtype relations (4) T <: Format and (5) U <: Format. Thus, we can show that the method body type checks. For example, expression this.left is of type T. Based on (4), this expression is also of type Format and therefore the method call in line 5 this.left.format() type checks.
We consider type checking the main function. Instances for generic type variables must always be explicitly supplied. Hence, when constructing a pair that holds number values, see line 9, we find Pair [Num, Num].
Consider the method call p.format() in line 10. The receiver struct Pair[T Format, U Format] of the method definition in line 4 matches p's type Pair [Num, Num] by replacing T and U by Num. The type bounds in the receiver type are satisfied as we know from above that (2) Num <: Format. Hence, the method call type checks.
By generalizing the above argument we find that That is, under the assumptions T <: Format and U <: Format we can derive that Pair[T, U] <: Format. In particular, we find that Pair[Num, Num] <: Format. Hence, the function call formatSome(p) in line 11 type checks. Extending our type-directed translation scheme to deal with generics turns out to be fairly straightforward.
Bounded type parameter. A bounded type parameter T Ifce where T is a type variable and Ifce is an interface type becomes a coercion parameter toIfce T . At instantiation sites, coercions need to be inserted.
The lower part of Figure 2 shows the translated program. Starting at line 18 we find the translation of the definition of method format for pairs. Each bounded type parameter T Format and U Format is turned into a coercion parameter toFormat T and toFormat U . In the target, we use a curried function definition where coercion parameters are collected in a tuple.
A method call of format needs to supply concrete instances for these coercion parameters. See line 27 which is the translation of calling format on receiver type Pair [Num,Num]. Hence, we must pass as the first argument the tuple of coercions (toFormat Num , toFormat Num ) to format Pair .
Subtype relation (6) implies the (parameterized) coercion toFormat Pair in line 23. Given coercions toFormat T and toFormat U we can transform a pair p into an interface value for Format, where the method dictionary consists of the partially applied translated method definition format Pair .
We make use of toFormat Pair in the translation of the function call formatSome(p), see line 28. Based on the specific coercion toFormat Num , the call toFormat Pair transforms the pair value p into the interface value (p, format Pair (toFormat Num ,toFormat Num )). Then, we call formatSome on this interface value.

Bounded type parameters of methods
There may be bounded type parameters local to methods. Consider Figure 3 where we further extend our running example. Starting at line 1 we find a definition of method formatSep for pairs. This method takes an argument s that acts as a separator when formatting pairs. Argument s is of the generic type S constrained by the type bound Format. Type parameter S is local to the method and not connected to the receiver struct. Type arguments for S must also be explicitly specified in the program text, see method calls in lines 8 and 13.
In the translation, bounded type parameters of methods simply become additional coercion parameters. Consider the translation of formatSep defined on pairs starting at line 21. The translated method definition first expects the coercion parameters (toFormat T , toFormat U ) that result from the bounded type parameters T Format and U Format of the receiver. Then, we find the receiver argument this followed by the coercion parameter toFormat S resulting from S Format, and finally the method argument s. The translation of the method body follows the scheme we have seen so far, see lines 22-24. When calling method formatSep on a pair we need to provide the appropriate coercions, see line 37.
From the method definition of formatSep for pairs and from the definition of interface FormatSep, we find that the following subtype relation holds:   Subtype relation (7) implies the coercion toFormatSep Pair in line 28. Thus, the function call of formatSepSome from line 14 translates to the target code starting in line 38. The point to note is that a coercion parameter corresponding to a bounded type parameter of a method is not part of the dictionary; it is only supplied at the call site of the method. Consider the call x.formatSep[Format](s) in line 8. In the translation (line 33), we first partially apply the respective dictionary entry on the receiver. This is done via the target expression (formatSep FormatSep x). Type Format is a valid instantiation for type parameter S of formatSep because Format <: Format in FGG − . In the translation, this corresponds to the (identity) coercion toFormat Format . Hence, we supply the remaining arguments toFormat Format and s.

Bounded type parameters of structs and interfaces
Structs and interfaces may also carry bounded type parameters. In FGG − and FGG, these type parameters do not have a meaning at runtime as their purpose is only to rule out more  programs statically. Hence, in our translation approach they do not translate into additional dictionary parameters or coercions.
Let us explain with the example in Figure 4. Struct FPair (short for "formatted pairs") requires the type bound Format on its type parameters. The generic interface Factory defines a factory method returning formatted pairs. It requires a type bound T Format for the type FPair[T, T] in its method signature to be well-formed. The need for this type bound arises because FGG's type system does not allow to conclude from just an occurrence of FPair[T, T] that T is already a subtype of Format.
Struct MyFactory defines a concrete factory implementation for FPair [Num, Num], function doWork accepts a generic Factory[T] for arbitrary T. Again, doWork requires a type bound T Format for type Factory[T] to be well-formed. The main function may then call doWork with a MyFactory value because MyFactory is a subtype of Factory [Num].
The translated code (lower part of Figure 4) demonstrates that bounded type parameters of structs and interfaces have no representation at runtime, so the translation effectively ignores them. A struct value is still just a tuple with the fields of the struct (lines 17, 18), and an interface value just combines a struct value with a method dictionary (e.g. An important point to note is that there is a difference between generic interfaces and interfaces with generic methods. Interface Factory [T] in Figure 4 is generic in T, a subtype of Factory[U] must provide an implementation of the create method for some fixed type U. In contrast, interface FormatSep from Figure 3 is not generic but contains a method formatSep that is generic in S. A subtype of FormatSep must provide an implementation of formatSep that is also generic in S.

Outlook
Next, Section 3 formalizes FGG − following the description by Griesemer et al. (2020). Then, we give the details of our type-directed translation scheme in Section 4 and establish that the meaning of FGG − programs is preserved in Section 5. Griesemer et al., 2020) is a small subset of the full Go language (2022) supporting only essential features such as structs, interfaces, method overloading and structural subtyping. In the same article, the authors add generics to FG with the goal to scale the design to full Go. The resulting calculus is called Featherweight Generic Go (FGG). Since version 1.18, full Go includes generics as well, but with limited expressivity compared to the FGG proposal (see Section 7.1). For the translation presented in this article, we stick to the original FGG language with minor differences in presentation but excluding dynamic type assertions. We refer to this language as FGG − . The next two subsections introduce the syntax and the dynamic semantics of FGG − . We defer the definition of its static semantics until Section 4.2, where we specify it as part of the type-directed dictionary-passing translation. Figure 5 introduces the syntax of FGG − . We assume several countably infinite, pairwise disjoint sets for names, ranged over by N with some subscript (upper part of the figure). Meta variables and denote struct names, and interface names, and type variables, field names, method names, and , denote names for variables in expressions. Overbar notation is a shorthand for the sequence 1 . . . where is some syntactic construct. In some places, commas separate the sequence items. If irrelevant, we omit the and simply write . Using the index variable under an overbar marks the parts that vary from sequence item to sequence item; for example, ′ abbreviates ′ 1 . . . ′ and abbreviates 1 . . .

Syntax
. The middle part of Figure 5 shows the syntax of types in FGG − . A type name , is either a struct or interface name. Types , include types variables and instantiated types [ ]. For non-generic structs or interfaces, we often write just instead of []. Struct types , and interface types , denote syntactic subsets of the full type syntax.  The lower part of Figure 5 defines the syntax of FGG − expressions, declarations, and programs. Expressions, ranged over by and , include variables , method calls, struct literals, and field selections. A method call . [ ] ( ) invokes method on receiver with type arguments and arguments . If does not take type arguments, we often write just . ( ). A struct literals { } creates an instance of a struct with fields, the arguments become the values of the fields in order of appearance in the struct definition. A field selection . projects the value of some struct field from expression .
A method signature ::= [ ] ( ) consists of a name , bounded type parameters with interface type as upper bounds, parameters of type , and return type . It binds and . The scope of a type variable is , , and all upper bounds , so FGG − supports F-bounded quantification (Canning et al., 1989). For non-generic methods, we often write just ( {return } providing an implementation of method for struct . All three forms bind the type variables , a method implementation additionally binds the receiver parameter . The scope of a type variable includes all upper bounds , the body of the declaration enclosed in {. . .}, and for method declarations also the signature . We omit the [ ] part completely if is empty. Finally, a program consists of a sequence of declarations together with a main function. Method and function bodies only contain a single expression. We follow the usual convention and identify syntactic constructs up to renaming of bound variables or type variables. The syntax of FGG − as presented here differs slightly from its original form (Griesemer et al., 2020). The original article encloses type parameters in parenthesis, an additional type keyword starts a list of type parameters. Here, we follow the syntax of full Go and use square brackets without any keyword. Further, the original article prepends package main to each program, something we omit for succinctness. Finally, we reduce the number of syntactic meta-variables to improve readability.

Dynamic Semantics
Figure 6 defines a call-by-value dynamic semantics for FGG − using a small-step reduction semantics with evaluation contexts. The definition is largely taken from Griesemer et al. (2020). We use , , to denote values, where a value is a struct literal with all fields being values. A call-by-value evaluation context E is an expression with a hole □ such that the hole marks the point where the next evaluation step should happen. We write E [ ] to denote the replacement of the hole in E with expression . A value substitution is a finite mapping ⟨ ↦ → ⟩ from variables to values, whereas a type substitution is a finite mapping ⟨ ↦ → ⟩ from type variables to types. The (type) variables in the domain of a substitution must be distinct. Substitution application, written in prefix notation as or or , is defined in the usual, capture-avoiding way. When combining two sequences, we implicitly assume that both sequences have the same length. For example, combining variables and values to a substitution ⟨ ↦ → ⟩ implicitly assumes that there are as many variables as values.
The reduction relation −→ ′ denotes that expression reduces to expression ′ . To avoid clutter, the sequence of declarations of the underlying program is implicitly available in the rules defining this reduction relation. Rule FG-CONTEXT applies a reduction step inside an expression. Rule FG-FIELD  The first two restrictions ensures that the value for a field in rule FG-FIELD is unambiguous. The third restriction avoids multiple matching method definitions in rule FG-CALL.

Type-directed translation
This section defines a type-directed, dictionary-passing translation from FGG − to an untyped -calculus extended with recursive let-bindings, constructors and pattern matching. We first introduce the target language, then specify the translation itself, and last but not least give some examples. Formal properties of the translation are deferred until Section 5.  , and constructors are drawn from countably infinite, pairwise disjoint sets V Var and V Con , respectively. Expressions, ranged over by and , include variables , constructors , function applications ′ , -abstractions . , and pattern matching via case-expressions case of Pat → . Patterns Pat have the form , they do not nest. We assume that all constructors in Pat are distinct. To avoid some parentheses, we use the conventions that application binds to the left and that the body of a extends to the right as far as possible.

TL expression reductions
A program let = in consists of a sequence of (mutually recursive) definitions and a (main) expression, where we assume that the variables are distinct. In the translation from FGG − , the values are always functions resulting as translations of FGG − methods. We identify expressions, pattern clauses and programs up to renaming of bound variables. Variables are bound by expressions, patterns, and let-bindings of programs.
Some syntactic sugar simplifies the construction of patterns, expressions and programs. (a) We use nested patterns to abbreviate nested case-expressions. (b) We assume data constructors for tuples up to some fixed but arbitrary size. The syntax ( ) constructs an -tuple when used as an expression, and (Pat ) deconstructs it when used in a pattern context. (c) We use patterns in -expressions; that is, the notation Pat. stands for .case of Pat → where is fresh. Target values , , are either -expressions or constructors applied to values. A constructor value is short for (. . . ( 1 ) . . .) . A call-by-value evaluation context R is an expression with a hole □ such that the hole marks the point where the next evaluation step should happen. We write R [ ] to denote the replacement of the hole in R with expression .
A substitution , is a finite mapping ⟨ ↦ → ⟩ from variables to values. The variables in the domain must be distinct. Substitution application, written in prefix notation , is defined in the usual, capture-avoiding way. We use two different meta variables and for substitutions in the target language with the convention that the domain of contains only top-level variables bound by let. As top-level variables result from translating FGG − methods, we sometimes call a method substitution.
The reduction semantics for the target language is defined by two relations: −→ ′ reduces expression to ′ under method substitution , and Prog −→ Prog ′ reduces Prog to Prog ′ . The definition of the latter simply forms a method substitution from the toplevel bindings of Prog and then reduces the main expression of Prog under (rule TL-PROG). We defer the substitution of top-level-bound variables because they might be recursive.
The definition of the reduction relation for expressions extends over four rules. Rule TL-CONTEXT uses evaluation context R to reduce inside an expression, rule TL-LAMBDA reduces function application in the usual way. Pattern matching in rule TL-CASE assumes that the scrutinee is a constructor value ; the lookup of a pattern clause matching yields at most one result as we assume that clauses have distinct constructors. During a sequence of reduction steps, a variable bound by let at the top-level might become a redex, as only -bound variables are substituted right away. Thus, rule TL-METHOD finds the value for the variable in the method substitution .

Translation
Before we dive into the technical details, we summarize our translation strategy.
Struct. An FGG − value of some struct type is represented in the TL as a struct value; that is, a tuple ( ) where is the number of fields and represents the -th field of the struct.
Interface. An FGG − value of some interface type is represented in the TL as an interface value; that is a pair ( ,D), where is a struct value realizing the interface and D is a dictionary. Dictionary. A dictionary D for an interface with methods is a tuple ( ) such that is a dictionary entry for method . Dictionary entry. A dictionary entry for a method with signature = [ ] ( ) is a function accepting a triple: (1) receiver, (2) tuple with coercions corresponding to the bounded type parameters of the method, (3) tuple for parameters . Coercion. A structural subtype relation <: implies a coercion function to transform the target representation of an FGG − value at type into a representation at type . Bounded type parameter. A bounded type parameter becomes a coercion parameter transforming the type supplied for to its bound . At instantiation sites, coercions need to be inserted.

Method declaration. A method declaration func ( [ ]) [ ′ ′ ] ( ) {return } is represented as a top-level function
, accepting a quadruple: (1) tuple with coercions corresponding to the bounded type parameters of the receiver, (2) receiver , (3) tuple with coercions corresponding to bounded type parameters ′ ′ of the method, (4) tuple for parameters .
In essence, the above is a more detailed description of the translation scheme motivated in Section 2. The only difference is that dictionary entries and translations of methods are now represented as uncurried functions. For example, instead of the curried representation in Figure 3 formatSep Pair ( toFormat T , toFormat U ) this toFormat S x = ... toFormatSep Pair ( toFormat T , toFormat U ) p = (p , formatSep Pair ( toFormat T , toFormat U )) our actual translation scheme uses uncurried functions, as in the following code: --translation of method formatSep Pair (( toFormat T , toFormat U ) , this , toFormat S , x ) = ...
toFormatSep Pair ( toFormat T , toFormat U ) p = (p , \( this , locals , arg ) -> --dictionary entry formatSep Pair (( toFormat T , toFormat U ) , locals , arg )) Using an uncurried representation instead of a curried representation is just a matter taste. As we have carried out the semantic equivalence proof initially based on the uncurried representation, we stick to it from now on.

Conventions and Notations
The translation relies on three total, injective functions with pairwise disjoint ranges for mapping FGG − names to TL variables. The first function N var → V Var translates a FGG − variable to a TL variable . To avoid clutter, we do not spell out the translation function explicitly but use the abbreviation that a lowercase always translates into its uppercase counterpart . The second function N tyvar → V Var translates an FGG − type variable into a TL variable, abbreviated . The third function N method × N struct → V Var gives us the TL variable , representing the translation of a method for struct . Here is a summary of the shorthand notations for name translation functions, where methodName( ) denotes the name part of method signature .
The notation for translating names slightly differs from the approach used in the examples of Section 2. For instance, the coercion toFormat T from Figure 3 is now named T and method formatSep Pair becomes formatSep,Pair . The notation of the formal translation stresses that T and formatSep,Pair are variables of the target language.
An FGG − type environment Δ is a mapping { : } from type variables to their upper bounds . An FGG − value environment Γ is a mapping { : } from FGG − variables to their types . An environment may contain at most one binding for a type variable or variable. We write ∅ for the empty environment, dom(·) for the domain of an environment, and ∪ for the disjoint union of two environments. The notation distinct( ) asserts that is a sequence of disjoint items. We let [ ] denote the set {1, . . . , }.
In the following, we assume that the declarations of the FGG − program being translated are implicitly available in all rules. This avoids the need for threading the declarations through all translation rules.

Auxiliary Judgments
Figure 8 defines some auxiliary judgments. The judgment Δ ⊢ subst ↦ → : , defined by rule TYPE-INST-CHECKED, constructs a type substitution = ⟨ ↦ → ⟩ and checks that the conform to their upper bounds under type environment Δ. In the tuple ( ) ofabstractions each coerces the actual type argument to its upper bound. The relevant premise for checking upper bounds is Δ ⊢ coerce <: , which asserts that is a structural subtype of giving raise to a coercion function . The judgment will be defined and explained in the next subsection.
The lower part of Figure 8 defines two judgments for looking up methods defined for a struct or interface type. Judgment ⟨ , ⟩ ∈ methods(Δ, ) states that method signature is available for struct type under type environment Δ, see rule METHODS-STRUCT. The value is a tuple of coercion functions resulting from checking the bounds of the receiver's type parameters. Judgment methods( ) = { } states that the set of method signatures available for interface type is { }, see rule METHODS-IFACE. As stated before, this rule forms the substitution ⟨ ↦ → ⟩ by implicitly assuming that and have the same length. Translation of structural subtyping  Figure 9 defines the relation Δ ⊢ coerce <: for asserting that is a structural subtype of , yielding a coercion function to convert the target representations of to .

Translation of Structural Subtyping
Rule COERCE-TYVAR covers the case of a type variable . The premise states that type bound ( : ) exists in the environment. By convention, is the name of the corresponding coercion function. We further find that Δ ⊢ coerce <: . Hence, we obtain the coercion function for <: by composition of coercion functions and . Rule COERCE-STRUCT-IFACE covers structs. The premise ⟨ , ⟩ ∈ methods(Δ, ) asserts that each method with name methodName( ) of interface is defined for . Value ⟨Δ, Γ⟩ ⊢ exp : ⟨Δ, Γ⟩ ⊢ exp : is a tuple with coercion parameters corresponding to the bounds of the receiver's type parameters. Thus, = ( 3 ). , ( , 3 ) is the dictionary entry for the -th method: a function accepting receiver 1 , coercion parameters 2 corresponding to bounded type parameters of the method, and the argument tuple 3 . As written earlier, dictionary entries and top-level functions , are uncurried. Thus, we need to deconstruct the argument triple ( 3 ) and construct a new quadruple ( , 3 ) for calling , . Rule COERCE-IFACE-IFACE covers structural subtyping between interface types [ ] and [ ]. In this case, must declare all methods of , so we can build a dictionary for from the methods in the dictionary for . Thus, the premise of the rule requires the total function to be chosen in such a way that the -th method of has the same signature as the ( )-th method of . The translation uses pattern matching to deconstruct the dictionary of as ( ). Then the -th method in the dictionary of is ( ) , so we construct the wanted dictionary as ( (1) , . . . , ( ) ). states that under type environment Δ and value environment Γ the FGG − expression has type and translates to TL expression .

Translation of Expressions
Rule VAR retrieves the type of FGG − variable from the environment and translates to its TL counterpart . The context makes variable available, see the translation of method definitions in Section 4.2.6. Rule STRUCT type checks and translates a struct literal [ ] ( ). Premise Δ ⊢ ok [ ] checks that type [ ] is well-formed; the definition of the judgment Δ ⊢ ok is given in Figure 11 and will be explained in the next subsection. Each argument translates to , so the result is ( ). Rule ACCESS deals with field access . , where expression must have struct type [ ] such that defines field . Thus, translates to a tuple , from which we extract the -th component via pattern matching.
Rule CALL-STRUCT handles a method call .
[ ′ ] ( ), where receiver has struct type [ ] and translates to . The in the premise corresponds to a tuple of coercion functions that result from checking the bounds of the receiver's type parameters, whereas ′ is a tuple of coercion functions for the bounds of the type parameters of the method. Argument translates to . According to our translation strategy, a method declaration for and is represented as a top-level function , accepting a quadruple: coercions for the receiver's type parameters, receiver, coercions for the bounded type parameters local to the method, and method arguments. Thus, the result of the translation is , ( , , ′ ,( )). Rule CALL-IFACE handles a method call .
[ ′ ] ( ), where receiver has interface type and translates to . Similar to CALL-STRUCT, ′ is a tuple of coercion functions that result from checking the bounds of the type parameters local to the method. Expressions are the translation of the arguments . Following our translation strategy, receiver is a pair where the first component is a struct value and the second component is a dictionary for the interface. Thus, we use pattern matching to extract the struct as and the wanted method as . This is a function accepting a triple: receiver, coercions for bounded type parameters of the method, and method arguments. Hence, the translation result is ( , ′ ,( )). The difference to rule CALL-STRUCT is that there is no need to supply coercions for the bounded type parameters of the receiver. These coercions have already been supplied when building the dictionary, see rule COERCE-STRUCT-IFACE of Figure 9.
The last rule SUB is a subtyping rule allowing an expression with translation at type to be assigned some (structural) supertype . Premise Δ ⊢ coerce <: serves two purposes: it ensures that is a supertype of and it yields a coercion function from to . The translation of at type is then . In Griesemer et al. (2020), the subtype check is included for each form of expression. For clarity, we choose to have a separate subtyping rule as in our translation scheme each subtyping relation implies a coercion function. Figure 11 defines several well-formedness judgments. The judgments Δ ⊢ ok and Δ ⊢ ok assert that a single type and multiple types, respectively, are well-formed under type environment Δ. A type variable is well-formed if it is contained in Δ (rule OK-TYVAR). A named type [ ] is well-formed if its type arguments are well-formed and if they are subtypes of the upper bounds in the definition of . The latter is checked by the premise Δ ⊢ subst ↦ → : of rule OK-TYNAMED, thereby ignoring the type substitution and the coercion functions . We have already seen in Section 2.5 that these coercions are not represented in the translated program because type bounds of structs and interfaces have no operational meaning.

Well-formedness
Well-formedness of type parameters and method signatures contains one func-declaration for and methodName( ) Judgment Δ ⊢ ok asserts that bounded type parameters are well-formed under type environment Δ (rule OK-BOUNDED-TYPARAMS). Judgment Δ ⊢ ok ensures that a method signature is well-formed (rule OK-MSIG). To form the combined environment Δ ∪ { : } in the premise requires disjointness of the type variables in dom(Δ) and . This can always be achieved by -renaming the type variables bound by .
Judgment ⊢ ok validates declaration . A struct declaration is well-formed if it is defined only once (restriction FGG-UNIQUE-STRUCTS in Section 3.2), if all field names are distinct (restriction FGG-DISTINCT-FIELDS), and if the field types are well-formed. An interface declaration is well-formed if it is defined only once, if all its method signatures are well-formed, and if all methods have distinct names.
A method declaration for and is well-formed if there is no other declaration for and (restriction FGG-UNIQUE-METHOD-DEFS), if the method signature is well-formed, and if each bound of the method declaration is a structural subtype of the corresponding bound ′ in the declaration of . In FGG − , this boils down to checking that the methods of ′ are a subset of the methods of . The well-formedness conditions for method declarations do not type check the method body. We will deal with this in the upcoming translation rule for methods. The translation of such a declaration is the binding , = . According to our translation strategy, must be a function accepting a quadruple: coercions ( ) for the bounded type parameters of the receiver, receiver corresponding to , coercions ( ) for the bounded type parameters local to the method, and finally method arguments corresponding to . Binding all these variables with a makes them available in the translated body .

Translation of Methods and Programs
Judgment ⊢ prog Prog denotes the translation of an FGG − program to a TL program Prog. Rule PROG type checks the main expression under empty environments against some type to get its translation . Next, the rule requires all struct or interface declarations to be well-formed. Finally, it translates each method declaration to a binding = . The resulting TL program is then let = in .

Example
We now give an example of the translation. The FGG − code in the top part of Figure 13 defines equality for numbers Num and for generic boxes Box [ Any]. Interface Any defines no methods, it serves as an upper bound for otherwise unrestricted type variables. We take the liberty to assume a basic type int and an operator == for equality. Interface Eq[ ] requires a method eq for comparing the receiver with a value of type . We provide implementations of eq for Num and Box[ ]. Comparing the content of a box requires the F-bound Eq[ ] (Canning et al., 1989). The main function compares two boxes for equality.
The middle part of the figure shows the translation of the FGG − code, using abbreviations in the bottom part. Variable eq,Num holds the translation of the declaration of eq for Num; it simply compares 2 (translation of this.val) with 3 (translation of that.val). (2))) --translated body of eq for Box Remember that the translation of a method declaration takes a quadruple with coercions for the bounded type parameters of the receiver, the receiver itself, coercions for the bounded type parameters of the method, and the method arguments. Here, () is a tuple of size zero, corresponding to the non-existing type parameters, (That) denotes a tuple of size one, corresponding to the single argument that. The translation of eq for Box is more involved. Figure 14 shows its derivation. We omit "obvious" premises and some trivial details from the derivation trees. Rule CALL-IFACE translates the body of the method. It coerces the receiver to the interface type Eq[ ] and then extracts the method to be called via pattern matching, see 1 . The construction of the coercion is done via Δ ⊢ coerce <: Eq[ ] 1 , see subderivation 1 . Coercion 1 is slightly more complicated then necessary because the translation does not optimize the identity coercion 2 . Inside of 1 , we use . This variables denotes a coercion from to the representation of Eq[ ]; it is bound by the -expression in the definition of eq,Box .
The translation of the main expression calls eq,Box with appropriate arguments, see Figure 15 for the derivation. The values ((1)) and ( (2) (1)

Formal Properties
In this section, we establish that the type-directed translation from Section 4.2 preserves the static and dynamic semantics of FGG − programs. The translation as formalized is nondeterministic: for the same source program we may derive syntactically different target programs. Thus, we further show that all target programs resulting from the same source program behave equivalently. Detailed proofs for all lemmas and theorems are given in the appendix.

Preservation of Static Semantics
It is straightforward to verify that the type system originally defined for FGG is equivalent to the type system induced by the type-directed translation presented in Section 4.2, provided the FGG program does not contain type assertions. In the following, we write Δ ⊢ G <: for FGG's subtyping relation, Δ; Γ ⊢ G : for its typing relation on expressions, and ⊢ G ok for the FGG typing relation on programs. These three relations were specified by Griesemer et al. (2020). The original article on FGG also includes support for dynamic type assertions, something we do not consider for our translation. Hence, we assume that FGG expressions do not contain type assertions.
Lemma 5.1.1 (FGG typing equivalence). Typing in FGG is equivalent to the type system induced by the translation, provided there are no type assertions.
(a) If Δ ⊢ G <: then either Δ ⊢ coerce <: for some or = and is not an interface type.
Claims (a) and (b) state that structural subtyping in FGG is equivalent to the relation from Figure 9, except that the latter is not reflexive for type variables and struct types. Claims (c) and (d) establish that expression typing in FGG and our expression typing from Figure 10 are equivalent modulo subtyping. The exposition in Griesemer et al. (2020) includes a subtyping check for each form of expression whereas we choose to have a separate subtyping rule. Hence, the type computed by the original rules for FGG might be a subtype of the type deduced by our system. FGG enjoys type soundness (see Theorem 4.3 and 4.4 of Griesemer et al. 2020). The reduction rules for FGG and FGG − are obviously equivalent. Thus, Lemma 5.1.1 gives the following type soundness result for our type system: for some , , and . Then either reduces to some value of type or diverges.

Preservation of Dynamic Semantics
This section proves that evaluating a well-typed FGG − program yields the same behavior as evaluating one of its translations. Thereby, we consider all possible outcomes of evaluation: reduction to a value or divergence. Further, we show that different translations of the same program have equivalent behavior.
The proof of semantic equivalence is enabled by a syntactic, step-indexed logical relation that relates an FGG − expression and a TL expression at some FGG − type. We write −→ ′ if reduces to ′ in exactly ∈ N steps, where N denotes the natural numbers including zero. By convention, we write −→ 0 ′ to denote = ′ . The notation −→ * ′ 16. Relating FGG − to TL expressions states that −→ ′ for some ∈ N. We write diverge( ) to denote that does not terminate; that is, for all ∈ N there exists some ′ with −→ ′ . The same definitions apply analogously to reductions in the target language.

The Logical Relation
The definition of the logical relation spreads over two figures 16 and 17. In these figures, we assume that the declarations of the FGG − program being translated are implicitly available in all rules. Also, we assume that an arbitrary but fixed method substitution is implicitly available to all rules. This is used in the reduction rules of the target language to resolve let-bound variables (i.e. translations of methods). In our main theorem (Theorem 5.2.6), we will then require that results from translating the methods in . We now explain the logical relation on expressions, see Figure 16. The relation ≈ ∈ ⟦ ⟧ denotes that FGG − expression and TL expression are equivalent at type for at most reduction steps. We call the step index. Rule EQUIV-EXP has two implications as its premises. The first states that if reduces to a value in ′ < steps, then reduces to some value in an arbitrary number of steps and is equivalent to at type for the remaining − ′ steps. The second premise is for diverging expressions: if reduces in less than steps to some expression ′ and ′ diverges, then diverges as well.
The relation ≡ ∈ ⟦ ⟧ defines equivalence of FGG − value and TL value at type with step index . Rule EQUIV-STRUCT handles the case where is a struct type. Then must be a value of this struct type and must be a struct value such that all field values of and are equivalent. Rule EQUIV-IFACE deals with the case that is an interface type. Hence, must be an interface value ( ,( )) with two requirements. First, and are equivalent for all step indices 1 < at some struct type . Second, ( ) must be an appropriate dictionary for the methods of the interface with receiver type . To check this requirement, rule METHOD-LOOKUP defines the auxiliary methodLookup( , ) to retrieve a quadruple ⟨ , , , ⟩ from the declaration of for . This quadruple has to be equivalent to dictionary entry for all step indices 2 < at the signature of the method.
A dictionary entry is always a function value. We write ⟨ , , , ⟩ ≈ ( . ) ∈ ⟦ ⟧ to denote equivalence between a quadruple for a method declaration and some dictionary entry . . Rule EQUIV-METHOD-DICT-ENTRY defines this equivalence such that method body and . take related arguments to related outputs. Thus, the premise of the rule requires for all step indices ′ ≤ , all related type parameters and , all related receiver values and , and all related arguments and that and . yield related results when applied to the respective arguments.
The judgment ≈ ∈ ⟦ ⟧ denotes equivalence between concrete type arguments and their TL realization when checking the bounds of type parameters . The definition in rule EQUIV-BOUNDED-TYPARAMS relies on our translation strategy that bounded type parameters are represented by coercions.
Having explained all judgments from Figure 16, we verify that the recursive definitions of ≈ ∈ ⟦ ⟧ and ≡ ∈ ⟦ ⟧ are well-founded. Often, logical relations are defined by induction on the structure of types. In our case, this approach does not work because interface types in FGG − might be recursive, see our previous work (Sulzmann and Wehr, 2022) for an example. Thus, we use the step index as part of a decreasing measure M. Writing | | for the size of some target value , we define M ( ≈ ∈ ⟦ ⟧ ) = ( , 1, 0) and M ( ≡ ∈ ⟦ ⟧ ) = ( , 0, | |). In EQUIV-EXP, either decreases or stays constant but the second component of M decreases. In EQUIV-STRUCT, and the second component stay constant but | | decreases, and in EQUIV-IFACE together with EQUIV-METHOD-DICT-ENTRY and EQUIV-BOUNDED-TYPARAMS step index decreases. Note that EQUIV-METHOD-DICT-ENTRY and EQUIV-BOUNDED-TYPARAMS only require ′ ≤ . This is ok because we already have 2 < in EQUIV-IFACE. Figure 17 extends the logical relation to whole programs. Judgment ≈ ∈ ⟦Δ⟧ denotes how a FGG − type substitution intended to substitute the type variables from Δ is EQUIV-METHOD-DICT-ENTRY: method body and variable must yield related outputs when applied to related arguments. Thus, for all related type arguments , ′ and ( , ′ ), all related receiver values and , and all related arguments and , the expression and variable must be related when applied to the appropriate arguments. However, different than in EQUIV-METHOD-DICT-ENTRY, we only requires this to hold for all ′ < . Judgment ≈ defines equivalence between FGG − declarations and TL method substitution . The definition in rule EQUIV-DECLS is straightforward: each method declaration for some method and struct must be equivalent to variable , .

Equivalence Between Source and Translation
To establish the desired result of semantic equivalence between a source program and one of its translations, we implicitly make the following assumptions about the globally available declarations and method substitution .
Assumption 5.2.1. We assume that the globally available declarations are well-formed; that is, ⊢ ok for all ∈ and ⊢ meth ′ = for some and and all ′ = func . . . ∈ . Further, we assume that the globally available method substitution has only variables of the form , in its domain.
The lemmas for monotonicity and several other properties are stated in Appendix A.3, together with all proofs. We can then establish that an FGG − expression is semantically equivalent to its translation .
The proof of this lemma is by induction on , see Appendix A.3.2.2 (page 59) for the full proof. Finally, the following theorem states our desired result: semantic equivalence between an FGG − program and its translation.
Theorem 5.2.6 (Program equivalence). Let ⊢ prog func main(){ = } let = in with having type . Let = ⟨ ↦ → ⟩. Then both of the following holds: 1. If −→ * for some value then there exists a target language value such that −→ * and ≡ ∈ ⟦ ⟧ for any . 2. If diverges then so does .
The "with having type " part means that the last rule in the derivation of the program translation has ⟨∅, ∅⟩ ⊢ exp : as a premise. Obviously, and meet the requirements of Assumption 5.2.1. The theorem then follows from Lemma 5.2.4 and Lemma 5.2.5. See Appendix A.3.2.3 (page 61) for the full proof.

Equivalence Between Different Translations
Our translation is non-deterministic because different translations of the same expression may contain distinct sequences of applications of the subsumption rule SUB. Recall the example from Figure 1. There are (at least) two different ways to translate expression Num{1} at type Format.
1. Use rules COERCE-STRUCT-IFACE and SUB to go directly from Num to supertype Format. The translation is then toFormat Num 1. 2. First use COERCE-STRUCT-IFACE and SUB to go from Num to Pretty, then use COERCE-IFACE-IFACE and SUB to go from Pretty to Format. The translation is then toFormat Pretty (toPretty Num 1).
Each choice leads to a syntactically distinct target expression. In general, evaluating the target expressions might lead to syntactically different target values because target values might contain dictionaries (i.e. tuple of -expressions), and different translations might produce syntactically different dictionaries.
Another source of non-determinism is that rule PROG for typing programs is allowed to choose the type of the main expression. For example, instead of typing Num{1} at type Format, another translation might pick type Pretty or Num for the main expression. The choice for the type of the main expression might also lead to syntactically different target expressions.
To summarize, different translations of the same source program might lead to syntactically different target language programs, and the syntactic differences might persist in the final values after evaluation. But a slightly weaker property holds: if we remove all dictionaries in the final values, then the results are syntactically equal. Figure 18 defines a function erase that removes all dictionaries from a target-language value. The function comes in two variations: • erase( , ) removes all dictionaries from value when viewed at type . Its duty is to remove the topmost dictionary if is an interface type. In this case, the function is partial but this is not an issue: a value viewed at an interface type is always a pair of values. • erase( ) removes all dictionaries from by replacing the -expressions with a fixed, otherwise unused, nullary constructor K . This definition relies on then fact that the translation only produces -expressions for dictionary entries. We replaceexpressions with a dedicated constructor K instead of the nullary tuple () so as to avoid confusion between an erased and a struct value without fields.
The following theorem states that evaluating the outcomes of two translations of the same source program yields values that are identical up to removal of dictionaries (or both diverge). This holds even if the two translations assign different types to the main expression of the source program. That is, there are no semantic ambiguities and we can establish that our translation is coherent (Reynolds, 1991).

Getting the step index right
The logical relation in Figures 16 and 17 requires at some places the step index in the premise to be strictly smaller than in the conclusion (<), other places require only lessthan-or-equal (≤). In EQUIV-EXP, we have < to keep the definition of the LR well-founded. The < in rule EQUIV-METHOD-DECL is required for the inductive argument in the proof of Lemma 5.2.5. Rule EQUIV-IFACE also has <, but rule EQUIV-METHOD-DICT-ENTRY only requires ≤. For well-foundedness, it is crucial that one of these two rules decreases the step index. However, equally important is that the step index is not forced to decrease more than once, so we need < in one rule and ≤ in the other. If both rules had <, then the proof of Lemma 5.2.4 would not go through for case CALL-IFACE. Consider the following example in the context of Figure 13: For values 1 and 2 , we may assume (1) 1 ≡ 1 ∈ ⟦Eq[Num]⟧ and 2 ≈ 2 ∈ ⟦Num⟧ for some . To verify that the translation yields related expressions, we must show From (1), via inversion of rule EQUIV-IFACE, we can derive methodLookup(eq, Num) ≈ ∈ ⟦eq(that Num) bool⟧ −1 because the premise of the rule requires this to hold for all 2 < . Let be the body of the method declaration of eq for Num. Inverting rule EQUIV-METHOD-DICT-ENTRY for (3) yields ⟨this ↦ → 1 , that ↦ → 2 ⟩ ≈ ((1),(),((2))) ∈ ⟦bool⟧ ′ (4) for ′ = − 1 because rule EQUIV-METHOD-DICT-ENTRY has ≤ in its premise. Also, we have 1 .eq( 2 ) −→ 1 ⟨this ↦ → 1 , that ↦ → 2 ⟩ and −→ * ((1),(),((2))). Thus, with (4), Lemma 5.2.2, and Lemma 5.2.3 we get 1 .eq( 2 ) ≈ ∈ ⟦bool⟧ ′ +1 . For ′ = − 1, this is exactly (2), as required. But if rule EQUIV-METHOD-DICT-ENTRY required < in its premise, then (4) would only hold for ′ = − 2 and we could not derive (2).
Whether we have < in EQUIV-IFACE and ≤ in EQUIV-METHOD-DICT-ENTRY or vice versa is a matter of taste. In our previous work at MPC (Sulzmann and Wehr, 2022), we established a dictionary-passing translation for Featherweight Go without generics. The situation is slightly different there. With generics, we need two rules with respect to methods: EQUIV-METHOD-DECL for method declarations and EQUIV-METHOD-DICT-ENTRY for dictionary entries where the coercions for the bounds of the receiver's type parameters have already been supplied. Without generics, there are no type parameters, so a single rule suffices (rule RED-REL-METHOD in MPC). So in the article at MPC, we use < for rule RED-REL-METHOD and ≤ for rule RED-REL-IFACE, the pendant to rule EQUIV-IFACE of the current article.

Implementation
We provide an implementation of the translation 2 written in Haskell (2022). All examples in this article were checked against the implementation. Competitive runtime performance of the translated code was not our goal. Hence, we took a convenient route and used Racket (2022) as the target language. The implementation features all language concepts from Section 3, as well as type assertions, generic functions, and several base types (integers, characters, strings, and booleans).
Generic functions and base types are straightforward to support. Implementing the typing and translation rules from Figure 10 requires some care because the presence of subsumption rule SUB renders the translation non-deterministic (see Section 5.2.3). We solved this problem by "inlining" the subsumption step when checking the arguments of a method call against the parameter types (rules CALL-STRUCT and CALL-IFACE) and when checking the field values of a struct against the declared field types (rule STRUCT). On formal grounds, this is justified as Featherweight Go (2020) inlines the subsumption step in similar ways and typing in FGG − is equivalent the type system induced by our translation rules (Lemma 5.1.1). The realization of type assertions (dynamic type casts) uses type tags (Ohori and Ueno, 2021). At runtime, a type assertion .( ) checks compatibility between 's type tag and the type tag corresponding to .
Our implementation comes with a large test suite and contains in total 181 tests, covering all main features of the source language. See Figure 19  Some of the tests behave differently under our implementation when compared with the original implementations for OOPSLA 2020/2022: • The OOPSLA 2020 implementation compiles generics by monomorphization; that is, generic code is specialized for all type arguments appearing in the program. But monomorphization cannot deal with all programs, so their type checker rejects several programs based on some syntactic condition (see Section 7.1 for details). Our implementation type checks these programs successfully. • The OOPSLA 2020 implementation statically rejects type assertions .( ) where the type of is a struct type, even though evaluation might succeed at runtime. Our implementation is more liberal and only rejects type assertions statically that are guaranteed to fail at runtime. • The OOPSLA 2020 implementation rejects recursive definitions of structs. For simplicity, we omitted this check from our implementation.
• The OOPSLA 2020 implementation runs several tests only for a fixed number of reduction steps because these tests would diverge otherwise. Our implementation only type checks such tests.

Related Work
The related work section covers generics in Go, type classes in Haskell, logical relations, and a summary of our own prior work. 5 At the end, we give an overview of the existing translations with source language Featherweight Generic Go.

Generics in Go
The results of this work rest on the definition of Featherweight Generic Go (FGG) provided by Griesemer and colleagues (2020). FGG is a minimal core calculus modeling the essential features of the programming language Go (2022). It includes support for overloaded methods, interface types, structural subtyping, generics, and type assertions. Our formalization of FGG ignores dynamic type assertions but otherwise sticks to the original definition of FGG, apart from some minor cosmetic changes in presentation. We prove that the type system implied by our translation is equivalent to the original type system of FGG, and that translated programs behave the same way as under the original dynamic semantics.
The original dynamic semantics of FGG uses runtime method lookup, in the same way as we did in Section 3. The authors define an alternative semantics via monomorphisation; that is, they specialize generic code for all type arguments appearing in the program. This alternative semantics is equivalent to the one based on runtime method lookup, but there exist type-correct FGG programs that cannot be monomorphized. For instance, polymorphic recursion leads to infinitely many type instantiations, so programs with a polymorphic-recursive method cannot be monomorphized. 6 Further, monomorphization often leads to a blowup in code size. In contrast, our translation handles all type-correct FGG programs, and instantiations of generic code with different type arguments do not increase the code size. However, we expect that monomorphized code will offer better performance than code generated by our dictionary-passing translation, because method dictionaries imply several indirections not present in monomorphized programs.
The current implementation of generics (Taylor and Griesemer, 2021) in Go versions 1.18, 1.19, and 1.20 (2022) differs significantly from the formalization in FGG. For example, full Go requires a method declaration for a generic struct to have exactly the same type bounds as the struct. In FGG, bounds of the receiver struct in a method declaration might be stricter than the bounds in the corresponding struct declaration. In Figure 2, we used this feature to implement formatting for the fully generic Pair type, provided the type parameters can be formatted as well. Go cannot express this scenario without falling back to dynamic type assertions. Ellis et al. (2022) formalize a dictionary-passing translation from a restricted subset of FGG to FG. The restriction for FGG is the same as previously explained for full Go: a method declaration must have the same type bounds as its receiver struct. The translation utilizes this restriction to translate an FGG struct together with all its methods into a single FG struct (dictionary). This approach would scale to full Go even with separate compilation because a struct and all its methods must be part of the same package. Further, the translation of Ellis and coworkers replaces all types in method signatures with the top-type Any, relying on dynamic type assertions to enable type checking of the resulting FG program. The authors provide a working implementation and a benchmark suite to compare their translation against several other approaches, including the current implementation of generics in full Go. Our translation targets an extended -calculus and does not restrict the type bounds of the receiver struct in a method declaration. We also provide an implementation but no evaluation of its performance.
Method dictionaries bear some resemblance to virtual method tables (vtables) used to implement virtual method dispatch in object-oriented languages (Driesen and Hölzle, 1996). The main difference between vtables and dictionaries is that there is a fixed connection between an object and its vtable (via the class of the object), whereas the connection between a value and a dictionary may change at runtime, depending on the type the value is used at. Dictionaries allow access to a method at a fixed offset, whereas vtables in the presence of multiple inheritance require a more sophisticated lookup algorithm (Alpern et al., 2001).
Generics in class-based languages such as Java (Bracha et al., 1998;Igarashi et al., 2001), and C♯ (Kennedy and Syme, 2001;Emir et al., 2006) do not require a dictionarypassing translation because all methods are part of the virtual method table of an object. In Go, however, methods are not necessarily attached to the receiver struct, so additional evidence in form of dictionaries must be passed for such methods. Further, subtyping in Java and C♯ is nominal, whereas Go has structural subtyping.
A possible optimization to the dictionary-passing translation is selective code specialization (Dean et al., 1995). With this approach, the dictionary-passing translation generates code that runs for all type arguments. In addition, specialized code is generated for frequently used combinations of type arguments. This approach allows to trade code size against runtime performance. The GHC compiler for Haskell supports a SPECIALIZE pragma (GHC User's Guide, 2022, § 6.20.11.) that allows developers to specialize a polymorphic function to a particular type. The specialization also supports type-class dictionaries.

Type Classes in Haskell
The dictionary-passing translation is well-studied in the context of Haskell type classes (Wadler and Blott, 1989;Hall et al., 1996). A type-class constraint translates to an extra function parameter, constraint resolution provides a dictionary with the methods of the type class for this parameter. In FGG, structural subtyping relations imply coercions and bounded type parameters translate to coercion parameters. An interface value pairs a struct value with a dictionary for the methods of the interface. Thus, interface values can be viewed as representations of existential types (Mitchell and Plotkin, 1988;Läufer, 1996;Thiemann and Wehr, 2008).
Another important property in the type class context is coherence. Bottu and coworkers (2019) make use of logical relations to state equivalence among distinct target terms resulting from the same source type class program. In the setting of FGG-, we first establish semantic equivalence among source and target programs, see Theorem 5.2.6. From this property, we can derive the coherence property (Theorem 5.2.7) almost for free. We believe it is worthwhile to establish a property similar to this theorem for type classes. We could employ a simple denotational semantics for source type class programs similar as Thatte (1994) or Morris (2014), which would then be related to target programs obtained via the dictionary-passing translation.
Section 2.5 demonstrated that type bounds on generic structs and interfaces have no operational meaning. This situation is similar to contexts of data type definitions in Haskell 2010 (Marlow, 2010). A data type such as data Eq a = > Set a = NilSet | ConsSet a ( Set a ) may require the context Eq a. However, an occurrence of type Set a does not imply that Eq a holds but always requires the constraint to be justified elsewhere. The GHC manual states that "this is widely considered a misfeature" (GHC Team, 2021, Section 6.4.2).

Logical Relations
Logical relations have a long tradition of proving properties of typed programming languages. Such properties include termination (Tait, 1967;Statman, 1985), type safety (Skorstengaard, 2019), and program equivalence (Pierce, 2004, Chapters 6, 7). A logical relation (LR) is often defined inductively, indexed by type. If its definition is based on an operational semantics, the LR is called syntactic (Pitts, 1998;Crary and Harper, 2007). With recursive types, a step-index (Appel and McAllester, 2001;Ahmed, 2006) provides a decreasing measure to keep the definition well-founded. See Mitchell (1996, Chapter 8) and Skorstengaard (2019) for introductions to the topic.
LRs are often used to relate two terms of the same language. For our translation, the two terms are from different languages, related at a type from the source language. Benton and Hur (2009) prove correctness of compiler transformations. They use a step-indexed LR to relate a denotational semantics of the -calculus with recursion to configurations of a SECD-machine. Hur and Dreyer (2011) build on this idea to show equivalence between an expressive source language (polymorphic -calculus with references, existentials, and recursive types) and assembly language. Their biorthogonal, step-indexed Kripke LR does not directly relate the two languages but relies on abstract language specifications.
Our setting is different in that we consider a source language with support for overloading. Besides structured data and functions, we need to cover recursive interface values. This leads to some challenges to get the step index right (Sulzmann and Wehr, 2022).
Simulation or bisimulation (see e.g. Sumii and Pierce 2007) is another common technique for showing program equivalences. In our setting, using this technique amounts to proving that reduction and translation commutes: if source term reduces to ′ and translates to target term , then ′ translates to ′ such that reduces to ′′ (potentially in several steps) with ′ = ′′ . One challenge is that the two target terms ′ and ′′ are not necessarily syntactically equal but only semantically. In our setting, this might be the case if ′ and ′′ contain coercions for structural subtyping. Even if such coercions behave the same, their syntax might be different. With LR, we abstract away certain details of single step reductions, as we only compare values, not intermediate results. A downside of the LR is that getting the step index right is sometimes not trivial. Paraskevopoulou and Grover (2021) combine simulation and an untyped, step-indexed LR (Acar et al., 2008) to relate the translation of a reduced expression (the ′ from the preceding paragraph) with the reduction result of the translated expression (the ′′ ). They use this technique to prove correctness of CPS transformations using small-step and bigstep operational semantics. Resource invariants connect the number of steps a term and its translation might take, allowing them to prove that divergence and asymptotic runtime is preserved by the transformation. Our LR does not support resource invariants but includes a case for divergence directly.

Prior Work
Our own work published at APLAS (Sulzmann and Wehr, 2021) and MPC (Sulzmann and Wehr, 2022) laid the foundations for the dictionary-passing translation and its correctness proof of the present article. For the APLAS paper, we defined a dictionary-passing translation for Featherweight Go (FG, Griesemer et al., 2020), the non-generic variant of FGG. That translation is similar in spirit to the translation presented here, it supports type assertions but not generics. The APLAS paper includes a proof for the semantic equivalence between the source FG program and its translation. The result is, however, somewhat limited as semantic equivalence only holds for terminating programs whose translation is also known to terminate.
In the MPC paper, we addressed the aforementioned limitation by extending the proof of semantic equivalence to all possible outcomes of an FG program: termination, panic (failure of a dynamic type assertion), and divergence. The proof uses a logical relation similar to the one used here, but without support for generics. We have already shown more differences in Section 5.3.

Summary of Translations
The diagram in Figure 20 summarizes the existing translations by Griesemer et al. (OOPSLA 2020), by Ellis et al. (OOPSLA 2022), from our MPC 2022 paper (Sulzmann and Wehr, 2022), and from the article at hand. The three resulting target language programs TL , ′ TL , and ′′ TL are semantically equivalent because all translations preserve the dynamic semantics. Each translation with FGG ★ as its source has different restrictions. OOPSLA 2022 requires the receiver struct of some method declaration to have exactly the same type bounds as the struct declaration itself. OOPSLA 2020 requires FGG to be monomorphizable, checked by a simple syntactic condition. The translation of this article does not support type assertions. Program FGG ★ is subject to certain restrictions, depending on the translation being performed.

Conclusion and Future Work
This article defined a type-directed dictionary-passing translation from Featherweight Generic Go (FGG) without type assertions to an extension of the untyped -calculus. The translation represents a value at the type of an interface as a combination of a concrete struct value with a dictionary for all methods of the interface. Bounded type parameters become extra function arguments in the target. These extra arguments are coercions from the instantiation of a type variable to its upper bound. Every program in the image of the translation has the same dynamic semantics as its source program. Different translations of the same source program may result in syntactically different but equivalent target programs. The proof of semantic equivalence is based on a syntactic, step-indexed logical relation. The step-index ensures a well-founded definition of the relation in the presence of recursive interface types and recursive methods. We also reported on an implementation of the translation.
In this article, we relied on FGG as defined by Griesemer and coworkers (2020), without reconsidering design decisions. But our translation raises several questions with respect to the design of generics in FGG and more generally also in Go. For example, the translation clearly shows that type bounds in structs and interfaces have no operational meaning. Should we eliminate these type bounds? Or should we give them a meaning inspired by Haskell's type class mechanism? Further, a method declaration in full Go must reuse the type bounds of its struct and must be defined in the same package as the struct. Clearly, this limits extensibility and flexibility. Can we provide a more flexible design to solve the expression problem (Wadler, 1998) in Go, without resorting to unsafe type assertions? We would like to use the insights gained through this article to answer these and similar questions in future work.
A somewhat related point is performance. As explained earlier, generics in Go are compiled by monomorphization. This gives the best possible performance because the resulting code is specialized for each type argument. However, not all programs can be monomorphized and the increase in code size is often considered problematic. This raises another interesting question for future work. Could selective monomorphization or specialization offer a viable trade-off between performance, code size, and the ability to compile Go programs which are not monomorphizable?
A statically-typed target language typically offers more room for compiler optimization (Harper and Morrisett, 1995). Thus, another interesting direction for future work is a translation to a typed backend, for example System F (Girard, 1972;Reynolds, 1974).
The work presented here does not include type assertions (dynamic type casts), although FGG supports them. We omitted type assertions from our theory for two reasons: Firstly, type assertions are largely orthogonal to the dictionary-passing translation, so their inclusion would obscure the working of the translation. Secondly, type assertions would require some extra design choices to consider. In our implementation we construct the check for a type assertion at the same place as in the source program, relying on dynamic type-tagpassing for gathering all information necessary. But other approaches are possible. For example, one could construct downcast coercions at call-sites and pass these coercions around. The second option could make the treatment of type assertions more lightweight, but would require significant research in this direction. Proof. We first state and prove three sublemmas:

A.1 Deterministic Evaluation in FGG
The proof is by induction on E 1 . (b) If −→ ′ then there exists a derivation of −→ ′ that ends with at most one consecutive application of rule FG-CONTEXT. The proof is by induction on the derivation of −→ ′ . From the IH, we know that this derivation ends with at most two consecutive applications of rule FG-CONTEXT. If there are two such consecutive applications, (a) allow us to merge the two evaluation contexts involved, so that we need only one consecutive application of FG-CONTEXT. (c) We call an FGG − expression directly reducible if it reduces but not by rule FG-CONTEXT. If 1 and 2 are now directly reducible and E 1 [ 1 ] = E 2 [ 2 ] then E 1 = E 2 and 1 = 2 . For the proof, we first note that E 1 = □ iff E 2 = □. This holds because directly reducible expressions have no inner redexes. The rest of the proof is then a straightforward induction on E 1 . Now assume −→ ′ and −→ ′′ . By (b) we may assume that both derivations ends with at most one consecutive application of rule FG-CONTEXT. It is easy to see (as values do not reduce) that both derivations must end with the same rule. If this rule is FG-FIELD, then ′ = ′′ by restrictions FGG-UNIQUE-STRUCTS and FGG-DISTINCT-FIELDS. If this rule is FG-CALL, then ′ = ′′ by FGG-UNIQUE-METHOD-DEFS. If the rule is FG-CONTEXT, we have the following situation with 1 ≠ FG-CONTEXT and 2 ≠ FG-CONTEXT:

FG-CONTEXT
As neither 1 nor 2 are FG-CONTEXT, we know that 1 and 2 are directly reducible.
and (c) we get E 1 = E 2 and 1 = 2 . With 1 and 2 not being FG-CONTEXT, we have ′ 1 = ′ 2 , so ′ = ′′ as required. Proof. We first prove the first implication of the lemma There are three sublemmas, analogously to the proof of Lemma A.1.1.
If −→ ′ then there exists a derivation of −→ ′ that ends with at most one consecutive application of rule TL-CONTEXT. (c) We call a target-language expression directly reducible if it reduces but not by rule TL-CONTEXT. If 1 and 2 are now directly reducible and R 1 [ 1 ] = R 2 [ 2 ] then R 1 = R 2 and 1 = 2 . The proofs of these lemmas are similar to the proofs of the sublemmas in Lemma A.1.1. Then (1) follows with reasoning similar to the proof of Lemma A.1.1. If the derivations of −→ ′ and −→ ′′ both end with rule TL-CASE, then our assumption that the constructors of a case-expression are distinct ensures determinacy.
The second claim of the lemma ( −→ ′ and −→ ′′ imply ′ = ′′ ) then follows directly from (1). Our assumption that the variables of a top-level let-binding are distinct ensures that the substitution built from the top-level let-bindings is well-defined. ■

A.2 Preservation of Static Semantics
Proof of Lemma 5.1.1. We prove (a) and (b) by case distinctions on the last rule of the given derivations; (c) and (d) follow by induction on the derivations, using (a) and (b). Claim (e) then follows by examining the typing rules, using (c) and (d). ■
Proof. We have −→ 0 , so we get the first implication of rule EQUIV-EXP by setting = and by assumption ≡ ∈ ⟦ ⟧ .
) and assume for any ′ , , Obviously Φ ↦ → : To show that holds, we assume the left-hand side of the implication in the premise of rule EQUIV-METHOD-DICT-ENTRY for some ′′ , ′ , ′ , , , , : We then need to prove (9) to show the overall goal.
End case distinction. This finishes the proof of (18).
Definition A.3.12 (Free variables). We write fv(·) for the set of free term variables, and ftv(·) for the set of free type variables.
We prove Lemma A.3.13 together with the following two lemmas.
• Case CALL-STRUCT: From the IH applied to (23) and (26) From (24) we get by inverting rule METHODS-STRUCT: From the assumption ≈ and (35) We have ′ − ′′ − Σ < ′′′ + 1 by the following reasoning: With (38) and ′′′ < , we now want to use the implication from the premise of rule EQUIV-METHOD-DECL. We instantiate the universally quantified variables of the implication as follows: Next, we prove the left-hand side of the implication. But first assume (see (36), (25), (37)) and define -We start by showing the first two conjuncts of the implication's left-hand side.
Proof. Define a measure function if is a struct type (| |, 1) if is an interface type (| |, 2) if is a type variable and proceed by induction on M ( , ). We first note that the derivations of ≡ ∈ ⟦ ⟧ all end with the same rule, independent from ∈ N. Case distinction on the last rule in the derivations of ≡ ∈ ⟦ ⟧ .
• Case rule EQUIV-STRUCT: Then is a struct type, so the derivations of ≡ ′ ∈ ⟦ ⟧ also all end with EQUIV-STRUCT. Thus we have As this holds for any ∈ N and we have M ( , ) < M ( , ), we may apply the IH to (1) and (2) and get erase( ) = erase( ′ ) for all ∈ [ ]. Then erase( ) = erase( ′ ) follows by definition of erase.
Case distinction on the forms of and ′ .
• Case and ′ are both struct types: Then all derivations of (1) and (2)  • Case is an interface type and ′ is a struct type: Analogously to the preceding case.
We now start with the first claim. Assume −→ * for some . Then must reduce to some value because of Theorem 5.2.6. Again with Theorem 5.2.6 and with Lemma A.1.2: Applying Lemma A.3.21 yields erase( , ) = erase( ′ , ′ ) as required.
For the second claim, we assume that diverges. With Theorem 5.2.6, we know that must diverge as well. Again with Theorem 5.2.6 we get that ′ also diverges. ■