Traits and rename
(1/5)An introduction to Programmatic Refactoring
Traits allow us to programmatically rename methods and nested classes to other names.
Consider the following example code, defining trait
'A.k()] Concrete2 = {//equivalent to just writing the following directly: A = {interface, method S k()} B = {[A], method S k()=S"Hi" class method This()} C = { method m()=S" world" class method This()} D = { method S callBoth()=B().k()++C().m() class method This()} } Main2 = Debug(Concrete2.D().callBoth()) //still prints "Hi World" ]]>As you can see above, the mapping
'A.k(); 'B.#apply() =>'B.of(); 'C=>'D; 'D=>'C; ] Concrete3 = {//equivalent to just writing the following directly: A = {interface, method S k()} B = {[A], method S k()=S"Hi" class method This of()} C = { method S callBoth()=B.of().k()++D().m() class method This()} D = { method m()=S" world" class method This()} } Main3 = Debug(Concrete3.C().callBoth()) //still prints "Hi World" ]]>Note how the call
(2/5) Programmatic Refactoring: all kinds of operations
Single
Programmatic refactoring of nested classes is transitive by default. All the nested classes are going to be renamed together with the renamed root. The code below shows how to specify a single rename instead:
'K; 'A=>'D.H ] MultiConcrete = {//equivalent to just writing the following directly: D = { H = { class method S hi() = S"hi" B = {class method S world() = S"world"} } } C = { D = { class method S space() = S" " } } K = { class method Void print() = Debug(D.H.hi()++C.D.space()++D.H.B.world()) } } Main4 = MultiConcrete.K.print() //still prints "hi world" ]]>As you can see, we did a single rename
We can also rename
'OriginalTop;single='NewTop=>'This] ]]>
On the other side, self rename, for example
Hide
The code below shows how to hide a method or a class:
Private members in 42 are obtained by relying on unique unguessable numbers: These names do not show up in the outline and can not be invoked by user code; moreover those numbers are automatically renamed to avoid clashing during code composition; overall, those numbers are completely invisible for the user of the code. While it is possible to manually use unique numbers, it is so much more convenient to write open code and then seal it later.Clear
Hidden code is still part of the result, but it is no more accessible. Symmetrically, cleared code is not part of the result, but its entry point is still accessible, but abstract; clearing code allows us to override already defined behaviour, as shown below:
Of course,Soft rename
Clearing code allows us to override code by removing code.
This is different with respect to what happens with overriding in most languages, where the former code still exists and can be invoked, for example with
'SuperA; 'C.D.space()->'C.D.superSpace() ]:{ SuperA = { class method S hi() B={class method S world()}} A = { class method S hi() = S"[%SuperA.hi()]" B = {class method S world() = S"[%SuperA.B.world()]"} } C={ D={ class method S superSpace() class method S space()=S"[%this.superSpace()]" } } } Main7 = MultiConcrete4.C.print() //now prints "[hi][ ][world]" ]]>Note how in this case we explicitly declare
Redirect
Finally, programmatic refactoring allows us to rename a nested class into an externally declared class. We call this kind of rename
Num] Main = ( myBox = NumBox(3Num) Debug(myBox) Num n = myBox.that() Debug(n) ) ]]>Note how we wrote
We can redirect multiple nested classes at the same time, and we can put arbitrary constraints on the structural type of the destination types simply by specifying abstract methods and implemented interfaces. Consider the following example:
e2.myIndex().eval(e1) if res return e1 return e2 } } Adventurer = Data:{S name, Num attack, Level level} Level = Data:{ Num exp S profession method Num eval(Adventurer that) = {..} } DuelOperation = Class:Operation ['Elem.myIndex()=>'Elem.level()] ['Elem=>Adventurer;'Index=>Level] Main= /*..*/ DuelOperation.best(e1=luke e2=gandalf) /*..*/ ]]>Here we can define a generic
(3/5) Different ways to supply missing dependencies
As we have seen, in 42 it is convenient to write self contained code, where the dependencies can be specified as nested classes with abstract methods. In 42 there are three different ways to satisfy those dependencies:
-
Sum:
We can compose two traits with the operators
« » or« » to provide some of the missing method implementations.
- Redirect: We can rename a class to an external one
Foo] ]]>
- Rename: We can rename a member to another member in the same unit of code:
'A] ]]>This last solution works like the sum, but is happening inside of the a single unit of code. If this inner sum is successful, it behaves as trait composition would. There are a few corner cases where this inner sum will fail; they involve details of composing classes with interfaces and adding methods to interfaces.
(4/5) Introspection and Info
It is also possible to programmatically query the code structure and make decisions about it. For example
t2.info().methods().size() return t1 return t2 }} MyClass = Class:Larger(t1=ATrait, t2=AnotherTrait) ]]>The method
A class that is watched can not be cleared. Indeed, all the possible errors of programmatic refactoring can be predicted by relying on the methods of
(5/5)Programmatic refactoring summary
- Many kinds of operations can be performed on code
-
Rename, as for
« 'B]]>» or« 'A.bar()]]>» , is used to rename all the occurrences of a member into another name or form, for the sake of the final user. -
Soft Rename, as for
« 'B]]>» or« 'A.bar()]]>» , only moves the declaration. It leaves in place all the usages and an abstract version of the original signature. -
Clear, as for
« » or« » , removes the implementation and all the private details of a member. It leaves in places all the usages and an abstract version of the original signature. -
Hide, as for
« » or« » , renames all the occurrences of a member into an uniquely named one. This new name is now completely invisible from outside the code unit. -
Redirect, as for
« Num]]>» , redirects all the usages of a nested class into an externally declared one. The internal declaration is simply trashed.