<!DOCTYPE article PUBLIC "-//NLM//DTD JATS (Z39.96) Journal Archiving and Interchange DTD v1.0 20120330//EN" "JATS-archivearticle1.dtd">
<article xmlns:xlink="http://www.w3.org/1999/xlink">
  <front>
    <journal-meta />
    <article-meta>
      <title-group>
        <article-title>Runtime type collecting and transpilation to a static language</article-title>
      </title-group>
      <contrib-group>
        <contrib contrib-type="author">
          <string-name>Pavel Krivanek</string-name>
          <xref ref-type="aff" rid="aff0">0</xref>
        </contrib>
        <contrib contrib-type="author">
          <string-name>Richard Uttner</string-name>
          <xref ref-type="aff" rid="aff1">1</xref>
        </contrib>
        <aff id="aff0">
          <label>0</label>
          <institution>Nidea s.r.o.</institution>
          ,
          <addr-line>Smejkalova 1033/136, 616 00, Brno Zabovresky, Czechia</addr-line>
        </aff>
        <aff id="aff1">
          <label>1</label>
          <institution>Projector Software GmbH</institution>
          ,
          <addr-line>Mathildenstr. 34, 72072 Tübingen</addr-line>
          ,
          <country country="DE">Germany</country>
        </aff>
      </contrib-group>
      <abstract>
        <p>This study delves into incorporating static typing into Pharo, a dynamic language derived from Smalltalk. Utilizing Pharo's pragmas, it introduces type annotations at both method and class levels, drawing from Strongtalk's approach but without altering Pharo's grammar. A novel experiment detailed here involves annotating Pharo code using these annotations and runtime type collection, then transpiling it to C#, highlighting the syntactic and conceptual adaptations required. Despite the complexities, the experiment successfully translates Pharo code into compilable C# code, underscoring Pharo's potential for optional static typing. The exploration also suggests future directions, including improved type inference, while reassessing static typing's role in error detection.</p>
      </abstract>
      <kwd-group>
        <kwd>eol&gt;Pharo</kwd>
        <kwd>static typing</kwd>
        <kwd>type annotations</kwd>
        <kwd>runtime type collection</kwd>
        <kwd>transpilation</kwd>
        <kwd>IWST*</kwd>
      </kwd-group>
    </article-meta>
  </front>
  <body>
    <sec id="sec-1">
      <title>1. Introduction</title>
    </sec>
    <sec id="sec-2">
      <title>2. Type Annotations</title>
      <p>Type annotations are categorized into two types: method-level and class-level.</p>
      <sec id="sec-2-1">
        <title>2.1. Method-level Type Annotations</title>
        <sec id="sec-2-1-1">
          <title>Method-level type annotations describe the types of:</title>
          <p>•
•
•
•</p>
        </sec>
        <sec id="sec-2-1-2">
          <title>Method return value</title>
          <p>Arguments
Temporary variables
Block arguments
•</p>
        </sec>
        <sec id="sec-2-1-3">
          <title>Block temporary variables</title>
          <p>occurrencesOf: o &lt;Object&gt; ^&lt;Int&gt;
| c &lt;Int&gt; |
c := 0.
self do: [ :e &lt;E&gt; | e = o ifTrue:[ c := c + 1 ]].</p>
          <p>^c
&lt;arg: #o type: Object&gt;
&lt;returns: #Integer&gt;
&lt;var: #c type: #Integer&gt;
&lt;blockArg: #e type: #Object generated: true&gt;
| c |
c := 0.
self do: [ :e | e = o ifTrue:[ c := c + 1 ]].</p>
          <p>^c</p>
          <p>Besides the type information, the method-level annotations include:
•</p>
          <p>Information on whether the return value of a given block is supposed to be used
Figure 2 shows the alternative to the Strongtalk code written using type annotating pragmas.</p>
          <p>Method-level type annotations for block arguments and block temporary variables have one
problem - the names of these entities may be the same in multiple blocks. A typical example is a
method with several blocks where each of them uses the block argument named each. We used two
strategies to solve this problem. The first requires each block argument and temporary variable in
the scope of a method</p>
          <p>to have a distinct name, which means that before adding type annotations to the code, every
method that does not pass this rule needs to be refactored. The second strategy involves adding a
block identification into the annotating pragma. We used a prefix that denotes the block order in
the method:
&lt;blockArg: #_1_each type: #Integer&gt;
&lt;blockArg: #_2_each type: #Point&gt;</p>
          <p>The latter approach avoids refactoring but complicates readability and further code
modifications. Therefore, we preferred using unique names within the method’s lexical scope.</p>
        </sec>
      </sec>
      <sec id="sec-2-2">
        <title>2.2. Class-level Type Annotations</title>
        <p>Class-level type annotations are used for type descriptions of instance variables (more generally,
slots) and other class elements, and we placed them into special methods named _slotTypes. Like
the method-level type annotations, they include pragmas.
_slotTypes
&lt;slot: #commandContext type: #CommandContext&gt;
&lt;slot: #labelString type: #String&gt;</p>
        <p>Our type annotations allow to describe a single simple type (class) associated with a variable,
multiple distinct types, or more complex types like Collections, Dictionaries, Associations, or
Blocks.</p>
        <p>Notice that the part of the pragma that describes the type is composed of a symbol or a literal
array of symbols. If the variable can hold only one class, a single symbol with the class name is
used. If it can hold more distinct classes, a literal array describes it.
&lt;var: #temp type: #Symbol&gt;
&lt;var: #temp type: #(Symbol Number)&gt;
&lt;var: #temp type: #(Symbol UndefinedObject)&gt;</p>
        <p>Given the commonality of UndefinedObject as an alternative type, we offer a shortcut for it
expressed as two colons. Here we would rather have preferred a question mark, but were limited
by Pharo’s symbol parsing.
&lt;var: #temp type: #Symbol::&gt;</p>
        <p>Complex types use literal arrays comprising the type class name, additional symbols such as of,
key or value describing the purpose of the subsequent type description, and embedded type
descriptions.
#(Array of Symbol)
#(Dictionary of Symbol keys Object)
#(Association key Symbol value Number)
#(Array of (Number String))
#(Array of (Array of String))</p>
        <p>Type descriptions for block closures are the most complex. They describe the types of all
arguments and the return type of the block. Blocks need not have arguments or return values, so
these parts can be omitted.
#FullBlockClosure
#(FullBlockClosure returning Integer)
#(FullBlockClosure:: returning Integer)
#(FullBlockClosure arguments #(String Object) returning Integer)</p>
        <p>These block type annotations are useful when describing argument types or variables holding
the block. However, Pharo code containing block literals does not indicate if the return value (the
result of the last expression) is to be used, as in select:, or ignored, as in do:. Thus, the method with
such a block can contain a pragma specifying this information, which is important in some cases
like code translation.
&lt;block: 1 returnsValue: false&gt;</p>
        <p>An example of such a method is described in the following chapter.</p>
        <p>We developed a simple system to regenerate type annotations while allowing programmers to
manually modify them without losing these changes. Each automatically generated type
annotation pragma includes an additional argument named generated: with true as the default
value.
&lt;arg: #anObject type: Object generated: true&gt;
generated: true.</p>
        <p>
          When generating type annotations, the system creates only new pragmas or those with
Other popular dynamically typed languages often implement type annotations by extending the
language syntax and standardizing existing language features such as function annotations (Python
3 [
          <xref ref-type="bibr" rid="ref3">3</xref>
          ]), using existing grammar constructs like special forms (Common Lisp [
          <xref ref-type="bibr" rid="ref4">4</xref>
          ]), or creating a new
language derived from the original one (TypeScript [
          <xref ref-type="bibr" rid="ref5">5</xref>
          ] derived from ECMAScript).
        </p>
      </sec>
    </sec>
    <sec id="sec-3">
      <title>3. Runtime Type Collecting</title>
      <p>
        Annotating existing Pharo code with types is a laborious task. Previous attempts, such as
RoelTyper [
        <xref ref-type="bibr" rid="ref6">6</xref>
        ], resolved a relatively low number of types. Thus, we explored an alternative
approach leveraging advances in Pharo’s reflectivity infrastructure.
      </p>
      <p>We collect runtime type information automatically during program execution by inserting
watchpoints and generate type annotations in post-processing. This method requires most of the
existing code to be executed in a manner providing relevant type information. Fortunately, in
Pharo, the test-driven style of development is very popular, so projects with a high test coverage
ratio are not unlikely. Ideally, runtime type collecting requires 100% code coverage with relevant
tests.</p>
      <p>
        Our technical solution involves installing various Metalinks [
        <xref ref-type="bibr" rid="ref7">7</xref>
        ] into each method to resolve
types. Metalinks in Pharo allow developers to attach additional behavior and metadata to methods
dynamically at runtime, enabling fine-grained control and non-intrusive modification of method
execution. For instance, to detect method argument types, a Metalink is installed at the method’s
beginning. For temporary variable types, a Metalink is installed at all assignments to the variable
within the method. Method return types are detected by wrapping the entire method execution
with a Metalink.
      </p>
      <p>Each Metalink has an associated object describing the collected types. Each invocation of the
Metalink updates this collection with the current types of values written into variables. After
executing all relevant code, the collected type information is post-processed and written as type
pragmas described above.</p>
      <p>This straightforward process is hindered by technical limitations in Pharo’s Metalinks
implementation, such as incorrect handling of some Metalink combinations. For example,
Metalinks for method return values need to be installed as standalone and require additional code
execution.</p>
      <p>Block closure literals require special treatment. Resolving their arguments is not significantly
different from resolving the method argument types; however, handling return values poses a
significant challenge. As mentioned above, in Pharo, the return value of a block evaluation is the
result of the last expression. The block itself does not provide any information about whether this
resulting value is actually meaningful and will be used. If the block is not constructed with the
intent to use its return value, the type provided by runtime collecting can produce an arbitrary
value, which does not help in describing the block type information because the actual expected
return type is void.</p>
      <p>Consider the following code:
self critical: [
logFileStream ifNotNil: [
logFileStream close.</p>
      <p>logFileStream := nil</p>
      <p>For the outermost block closure, the argument of the method critical:, we cannot be sure at
the first glance how the methodcritical: actually handles the block. We may assume it evaluates
the block, but we do not know if it only evaluates it and throws the result away or processes it
further. This information is required when we, for example, try to translate this code into a
different language that must explicitly specify a return statement for anonymous functions
replacing the block. Always returning the result type of the last statement is not possible because
the value may sometimes vary significantly. Moreover, in Pharo, the usage of a block return value
is sometimes depending on the actual context.</p>
      <p>We addressed this issue by returning a special proxy object instead of the actual block result
value. When we detect during type collecting that the variable or argument type is a block, we
construct a custom block closure of a custom class inheriting from the standard FullBlockClosure
class, set it up based on the original closure, and swap their identities using standard become:. Thus,
we ensure that we can handle standard evaluation messages of this block like value, value: etc.
These custom methods, when called, perform the standard block evaluation and wrap the returned
result in a custom proxy object. When a message is sent to the proxy or if it is assigned to a
variable – which needs to be resolved by some installed Metalink – the block is marked as a block
returning a value of the given result type. The proxy is then replaced by the actual result object
(using become:, again), so this mechanism is performed at most once.</p>
      <p>The type collecting process can be executed several times on a given code: As the type collector
always starts reading all available type annotations, it will merge the new collected annotations
with the existing ones if they are marked for regeneration.</p>
    </sec>
    <sec id="sec-4">
      <title>4. Translation into a Static Language</title>
      <p>Our experiment aimed to explore the possibility of transpiling parts of an existing business
application from Pharo to C# for architectural reasons, leveraging existing C# libraries and code
while benefiting from Pharo’s unique features. C# has severalproperties that facilitate the transpiling
of Pharo code, notably being a class-based object-oriented language with automatic memory
management based on garbage collection. Additionally, C#'s optional named arguments make the
translation of Pharo keywords easier than it would normally be for C-syntax based languages, thus
keeping the output code closer to its original source. Modern versions of C# support anonymous
functions and some other language properties similar to Pharo.</p>
      <p>However, C# has significant lexical, syntactical, and semantic differences from Pharo,
particularly in expressions and control structures. Its class metamodel also differs notably.
The C# grammar, being quite complicated, suggests that the direct transpiling of grammatically
simpler Pharo code to C# should be straightforward, at least from a syntactic perspective.
Unfortunately, this is only partially true. While in Pharo, practically everything is a message call or
an assignment, constructs that are easy to transpile, some C# language features introduce
difficulties, such as:
•
•
•
•
•
•
•
•
•</p>
      <sec id="sec-4-1">
        <title>The absence of non-local returns</title>
        <p>A notable difference between statements and expressions
Different object construction mechanisms
The absence of metaclasses and cascade
Missing polymorphism of constructors and static methods
Only stateless interfaces
The presence of primitive non-object types
Limited extension methods</p>
        <p>A wide set of reserved words</p>
        <p>Each of these issues presents its own challenges and potential solutions. In some cases, we
decided to limit constructs allowed in the transpiled Pharo code, so we must admit that we are able
to transpile only a subset of the Pharo language.</p>
        <p>We aimed to generate readable, maintainable C# code with a direct relation to the original
Pharo code. On the other hand, we wanted the Pharo code to be the main source of information.
The C# code was repeatedly generated from it while the Pharo code was still being modified and
improved.</p>
        <p>The transpilation process itself is not innovative. We start with Pharo code AST. Using a
transpiling visitor, we generate a new abstract syntax tree for a C# subset (because we do not need
all C# features) and finally, using another visitor, we generate C# code by visiting these nodes. The
generated C# code uses a small supporting library providing equivalents to Pharo standard library
along with other utility functions.
4.1. Messages
self next.</p>
        <p>becomes
Next;
Pharo unary messages are straightforward to translate into C#. They simply follow standard dot
notation and skip the this keyword if possible. All message names are modified to use the regular
C# customary to start with uppercase letter. So</p>
        <p>Binary message sends are interpreted as operators for common messages like +, &lt; and so on.
Some binary messages need to be reinterpreted, such as = as Equals().</p>
        <p>Keyword messages are generally hard to translate into other languages if readability shall be
preserved. Fortunately, with C# optional named arguments, this task is manageable with only small
limitations.</p>
        <p>As an example, let us choose a keyword message chooseFrom:title: with two keyword parts.
The method header with argument names looks like:
chooseFrom: aList title: aString</p>
        <p>When transpiling to C#, we can keep the first argument name (aList) unchanged. We rename
every other argument to match the corresponding keyword part name, aString to title in our case.
The original argument name is mentioned as a comment and in the beginning of the method. Then,
we define the new variable with the same name as it was in Pharo and assign it from the the
argument name we created. Thus the original argument name can be used inside of the method
without further change, as shown in the following example:
public long ChooseFrom(object aList, string title /* aString */ )
{
}
var aString = title;
…</p>
        <p>This approach proves its utility when we perform a method call of this message with some
arguments, as it closely resembles the message calls as done in the original Pharo code:
ChooseFrom(someList, title: actualTitle);</p>
        <p>However, this approach has some small limitations. Keyword part names need to be distinct so
it may require renaming of some methods (like with:with:) before transpiling. The reason behind is
that C# uses named arguments primarily to support different order of arguments. We created a
simple non-GUI tool in form of specialized class-side methods to detect such cases in advance.
4.2. Non-local returns
In Pharo, when a return statement is used inside a block closure, it causes program flow to exit
from the whole method, not only from a given closure. In C#, anonymous methods, also called
lambda expressions, are the closest corresponding construct to Pharo blocks. The main difference
in behaviour is that a return statement in C# only exits the execution of the current anonymous
function, not the method where it is defined.</p>
        <p>The difference in usage of non-local returns in Pharo and in C# stems from the fact that Pharo
uses combination of message sends and block closures for constructs that are expressed using
special grammar control structures in C#, like if-statements.</p>
        <p>Well-known Pharo methods such as ifTrue:, ifFalse:, ifNil:, ifEmpty:, whileTrue:, and do: are
translated directly into corresponding C# statements which use regular code blocks {…} instead of
lambdas. So the statement
aBoolean ifTrue: [
self doSomeAction.
^ 0 ]
is translated into
if (aBoolean)
{</p>
        <p>DoSomeAction;
return 0;</p>
        <p>Non-local returns that cannot be translated into statements are currently forbidden by our
transpiler. We have a small non-GUI tool to detect such cases in the code. The other alternative,
which generates slower code but is more general and does not require so many code modifications,
is to use exceptions.
4.3. Expressions
Pharo does not have any limits on structure of expressions. So instead of aBoolean in the example
above, another complex expression containing statement-like message can be used. This is not true
for C#. Some simple C# statements like if-else statement or null checks have an alternative
expression syntax. For example, the ternary expressions (?:) or the null-coalescing operators (??).
The transpiling visitor marks the AST subtrees that need to be expressions and tries to use these
alternatives. If this is not possible, it reports an error. In that case, the Pharo code needs to be
rewritten.
4.4. Object construction mechanisms
Pharo usually instantiates a class by sending a message to it. Such message then directly or
indirectly invokes a VM primitive that creates an object. C# does not have the concept of
metaclasses. For objects construction, the operator new is used. During building of the object, its
constructors are called.</p>
        <p>When the C# object is created, it sometimes requires generic type information, for example:
new Dictionary&lt;string, int&gt;()&gt;;</p>
        <p>Pharo does not provide such information; the equivalent expression is simply Dictionary new.
Our transpiler needs this information so it expects that the created object is assigned to some
variable, which is usually the correct assumption. If the new message is on the right side of an
assignment, the transpiler can derive generic types from the type annotations belonging to the
variable the new object is assigned to.</p>
        <p>If this mechanism cannot be used, an error is reported. In such case, the solution is to create an
additional temporary variable with type annotation and assign the new object during creation to it.</p>
        <p>The base C# class that is used as the root class for classes generated by the transpiler always
calls the Initialize method from its constructor to automatically mimic default Pharo behavior.
4.5. Cascade
Cascade is a heavily used Pharo construct that does not have a corresponding equivalent in C#. To
translate a cascade, we need to first create a temporary variable with a unique name and assign the
base cascade object to it. This variable needs to be typed in C#, but fortunately, automatic type
inference is sufficient in most cases. Because this assignment is a statement in C# but in Pharo, the
cascade is a general expression, there are additional complications in deciding where to and how to
evaluate the generated statement. The resultant code then looks, for example, like this:
var cascade = new Dictionary&lt;string&gt;&lt;string&gt;();
cascade.At("uid", put: uid);
cascade.At("label", put: label);
return cascade;</p>
        <p>Moreover, cascades may be embedded. We pay special attention to their evaluation order.
4.6. Metaclasses and polymorphism
The metaclasses concept in Pharo is very powerful and heavily used, but does not have a direct
mapping to C#. In Pharo, class-side methods often play a role of object constructing methods, and
they are polymorphic. We translate them into static methods in C# calling specially generated
constructors. We do not allow overrides because C# does not support static methods
polymorphism. Moreover, C# does not support polymorphism of constructors.</p>
        <p>
          The only more regular solution that could address this issue and allow to transpile more Pharo
code without need to adopt the code-base in advance, is to create a real object playing the role of
the class. This solution would break the direct relation between original transpiled code and, as a
consequence, lead to new issues. However, we plan to explore this approach in future
development, as we expect it to be the best way to avoid large refactorings.
4.7. Other complications
While traits in Pharo and interfaces in C# are comparable, traits in Pharo are stateful [
          <xref ref-type="bibr" rid="ref8">8</xref>
          ] such that
they can contain slots. As such slots cannot be translated into C#, we do not support stateful traits
in our transpiler.
        </p>
        <p>A surprising complication stems from the way how C# handles the nullability of primitive value
types like int. For such types, the nullability means that they are wrapped in an additional
structure that keeps information whether the value is set or not. If not, the actual value is set to the
default value, not null. This makes general implementation of some trivial Pharo collection
messages generally impossible (messages of type Dictionary &gt;&gt; atOrNil:).</p>
        <p>We tried to mimic many Pharo standard library methods using extension methods in C#. When
it was not possible because of C# limitations, like in the case of some Object extensions, we used
static methods in ECMAScript style (PharoObject.IsInteger(anObject)).</p>
        <p>The difference between Pharo and C# in zero array indexing proved to be not a real problem
because it was handled by extension methods we created to follow the Pharo collections API.</p>
        <p>Unlike Pharo, C# has a wide set of reserved keywords that can cause name collisions with the
existing Pharo code. C# has a way how to handle special names (using the @ sign). However, as we
encountered some problems with them, we rather changed the Pharo code in advance manually or
using refactorings to avoid names that are C# keywords, which was trivial.</p>
        <p>For Associations, we first tried to use C# pairs, but later switched to an own C# class which was
easier to handle in all use cases we needed.</p>
        <p>In relatively rare cases when the generated C# code required type casting, we introduced the
Pharo message castAs: #TypeName which does nothing in Pharo but adds a casting operator to the
C# code. In cases of several messages, we created own application specific hooks performing
certain casting operations, mainly for casting to return types of called methods. Thus we could get
around some complications, and even more important, avoid that C# performs unnecessary single
element casts for big collections, resulting in significant runtime overhead.</p>
      </sec>
    </sec>
    <sec id="sec-5">
      <title>5. Results and Further Development</title>
      <p>In our limited experiment, we were able to convert several packages of a real-life business
application into about 20,000 lines of compilable and working C# code. While the Pharo code
required some modifications to make it translatable to the target language, the amount of required
changes was relatively low. Because the automatically generated code was compilable by the C#
compiler, it means that the same task could theoretically be done without any translation to C#.
This proves that Pharo can easily play the role of a language with optional static typing.</p>
      <p>
        Our experiences during this experiment suggested several directions for further improvements.
Combining runtime type collecting with type inference in the style of RoelTyper [
        <xref ref-type="bibr" rid="ref6">6</xref>
        ] would be a
significant advantage for transpiling code with low code coverage. It showed us that, at least in the
case of C#, it would make sense not to maintain such a tight correspondence between the input and
output code and to handle constructs like metaclasses and non-local returns in more a verbose and
less readable but more general way. We would like to try translation into other languages, such as
TypeScript, as well.
      </p>
      <p>One interesting outcome of this experiment was our curiosity about whether static annotation
would reveal any serious type errors in the existing code. At least in this case, the result was
negative. We have encountered, of course, many reported type errors during compilation, but all of
them were related to the C# narrow interpretation of types, even if the Pharo code had valid
semantics.</p>
      <p>Even though we did not detect bugs in the Pharo code, runtime errors were still common even
for the code that was successfully compiled. Most of them, however, were related to some
limitations of the translation process or to bugs therein. This suggests that while static typing
brings some advantages, its role in error detection for well-designed dynamically typed code
should not be overestimated.</p>
      <p>The transpiler is publicly available at https://github.com/pavel-krivanek/Pharo-CSharp under
MIT lincense.</p>
    </sec>
  </body>
  <back>
    <ref-list>
      <ref id="ref1">
        <mixed-citation>
          [1]
          <string-name>
            <given-names>G.</given-names>
            <surname>Bracha</surname>
          </string-name>
          ,
          <string-name>
            <given-names>D.</given-names>
            <surname>Griswold</surname>
          </string-name>
          ,
          <article-title>Strongtalk: Typechecking Smalltalk in a production environment</article-title>
          ,
          <source>in: Proceedings of the 8th International Conference on Object-Oriented Programming Systems, Languages, and Applications (OOPSLA '93)</source>
          , ACM Press, New York, NY, USA,
          <year>1993</year>
          , pp.
          <fpage>215</fpage>
          -
          <lpage>230</lpage>
          . doi:
          <volume>10</volume>
          .1145/165854.165893.
        </mixed-citation>
      </ref>
      <ref id="ref2">
        <mixed-citation>
          [2]
          <string-name>
            <given-names>S.</given-names>
            <surname>Ducasse</surname>
          </string-name>
          ,
          <string-name>
            <given-names>E.</given-names>
            <surname>Miranda</surname>
          </string-name>
          ,
          <string-name>
            <given-names>A.</given-names>
            <surname>Plantec</surname>
          </string-name>
          ,
          <article-title>Pragmas: Literal Messages as Powerful Method Annotations</article-title>
          ,
          <source>in: Proceedings of the 11th International Workshop on Smalltalk Technologies (IWST</source>
          <year>2016</year>
          ), ACM Press, New York, NY, USA,
          <year>2016</year>
          . doi:
          <volume>10</volume>
          .1145/2991041.2991050.
        </mixed-citation>
      </ref>
      <ref id="ref3">
        <mixed-citation>
          [3]
          <string-name>
            <given-names>L.</given-names>
            <surname>Di Grazia</surname>
          </string-name>
          ,
          <string-name>
            <given-names>M.</given-names>
            <surname>Pradel</surname>
          </string-name>
          ,
          <article-title>The evolution of type annotations in Python: An empirical study</article-title>
          ,
          <source>in: Proceedings of the 30th ACM Joint European Software Engineering Conference and Symposium on the Foundations of Software Engineering (ESEC/FSE</source>
          <year>2022</year>
          ), ACM Press, New York, NY, USA,
          <year>2022</year>
          , pp.
          <fpage>209</fpage>
          -
          <lpage>220</lpage>
          . doi:
          <volume>10</volume>
          .1145/3540250.3549114
        </mixed-citation>
      </ref>
      <ref id="ref4">
        <mixed-citation>
          [4]
          <string-name>
            <given-names>G. L. Steele</given-names>
            <surname>Jr.</surname>
          </string-name>
          ,
          <string-name>
            <surname>Common Lisp: The Language</surname>
          </string-name>
          (2nd ed.), Digital Press,
          <year>1990</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref5">
        <mixed-citation>
          [5]
          <string-name>
            <given-names>B.</given-names>
            <surname>Cherny</surname>
          </string-name>
          ,
          <string-name>
            <surname>Programming TypeScript: Making Your JavaScript Applications Scale</surname>
            ,
            <given-names>O</given-names>
          </string-name>
          <string-name>
            <surname>'Reilly Media</surname>
          </string-name>
          ,
          <year>2019</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref6">
        <mixed-citation>
          [6]
          <string-name>
            <given-names>F.</given-names>
            <surname>Pluquet</surname>
          </string-name>
          ,
          <string-name>
            <given-names>A.</given-names>
            <surname>Marot</surname>
          </string-name>
          ,
          <string-name>
            <given-names>R.</given-names>
            <surname>Wuyts</surname>
          </string-name>
          ,
          <article-title>Fast type reconstruction for dynamically typed programming languages</article-title>
          ,
          <source>in: Proceedings of the 5th Symposium on Dynamic Languages (DLS '09)</source>
          , ACM Press, New York, NY, USA,
          <year>2009</year>
          , pp.
          <fpage>69</fpage>
          -
          <lpage>78</lpage>
          . doi:
          <volume>10</volume>
          .1145/1640134.1640145
        </mixed-citation>
      </ref>
      <ref id="ref7">
        <mixed-citation>
          [7]
          <string-name>
            <given-names>S.</given-names>
            <surname>Costiou</surname>
          </string-name>
          ,
          <string-name>
            <given-names>V.</given-names>
            <surname>Aranega</surname>
          </string-name>
          ,
          <string-name>
            <given-names>M.</given-names>
            <surname>Denker</surname>
          </string-name>
          ,
          <article-title>Sub-method, partial behavioral reflection with Reflectivity: Looking back on 10 years of use, The Art</article-title>
          , Science, and
          <article-title>Engineering of Programming 4 (</article-title>
          <year>2020</year>
          ). doi:
          <volume>10</volume>
          .1145/3567512.3567517
        </mixed-citation>
      </ref>
      <ref id="ref8">
        <mixed-citation>
          [8]
          <string-name>
            <given-names>P.</given-names>
            <surname>Tesone</surname>
          </string-name>
          ,
          <string-name>
            <given-names>S.</given-names>
            <surname>Ducasse</surname>
          </string-name>
          ,
          <string-name>
            <given-names>G.</given-names>
            <surname>Polito</surname>
          </string-name>
          ,
          <string-name>
            <given-names>L.</given-names>
            <surname>Fabresse</surname>
          </string-name>
          ,
          <string-name>
            <given-names>N.</given-names>
            <surname>Bouraqadi</surname>
          </string-name>
          ,
          <article-title>A new modular implementation for stateful traits</article-title>
          ,
          <source>Science of Computer Programming</source>
          <volume>195</volume>
          (
          <year>2020</year>
          ). doi:https://doi.org/10.1145/3167132.3167244
        </mixed-citation>
      </ref>
      <ref id="ref9">
        <mixed-citation>
          [9]
          <string-name>
            <given-names>N.</given-names>
            <surname>Bouraqadi</surname>
          </string-name>
          , D. Mason,
          <article-title>PharoJS: Transpiling Pharo classes to JS-ECMAScript 5 versus ECMAScript 6</article-title>
          , presented at the International Workshop on Smalltalk Technologies (IWST
          <year>2023</year>
          ),
          <year>2023</year>
          .
        </mixed-citation>
      </ref>
    </ref-list>
  </back>
</article>