Automatic Properties, In-Line Classes, Delegate Properties, and caseNext
For the last couple of weeks I’ve been trying to come up with a good semi-automatic resource-loading framework for Plasmacore. For each revision I added a new feature or two to Slag; I ended up not truly needing most of them but they’re good features nonetheless. Here’s a quick overview of the new features – mostly by example.
Automatic Properties
First we have automatic properties that can be used when you have a single init method that stores each of its arguments into a property you’ve just defined. Here’s an example of old code:
class Person PROPERTIES name : String age : Int32 METHODS method init( name, age ): endClass
Here’s the same code with automatic properties:
class Person( String name, Int32 age ) endClass
or even just:
class Person( String name, Int32 age );
You can add additional properties and methods as you like.
In-Line Classes
Slag now supports in-line classes that work pretty much just like Java’s anonymous classes. In addition, “METHODS” is now the default member category (so you can omit it if you start a class definition with a method), which makes in-line method definitions easier. An example:
class Operator method init: method apply( Real64 lhs, Real64 rhs ).Real64: abstract endClass ... method test( Real64 left, Operator op, Real64 right ): println( op.apply(left,right) ) ... local Operator plus = Operator() with method apply( Real64 lhs, Real64 rhs ).Real64: return lhs + rhs endWith local Operator minus = Operator() with method apply( Real64 lhs, Real64 rhs ).Real64: return lhs - rhs endWith test( 5, plus, 3 ) # prints: 8 test( 5, minus, 3 ) # prints: 2 test( 5, Operator() with method apply( Real64 lhs, Real64 rhs ).Real64: return lhs * rhs endWith, 3 ) # prints: 15
Delegate Properties
Delegate properties allow you to specify that any unresolved method calls or property accesses be forwarded to one property in particular. This is of particular use when you want to make one object that is essentially a proxy for another. Example:
class String PROPERTIES data : delegate Array<<Char>> hash_code : Int32 ... endClass ... local String mesg = "Delegate *this*!" println( mesg.count ) # same as saying "mesg.data.count" - prints: 16
Only one property can be defined as a delegate. Underneath the mechanism is very simple: at compile time failed member accesses are reevaluated with the delegate property.
caseNext
caseNext is handy in a which where you want to do something different every time based on a counter but you don’t really care what the case values are. It just takes the previous case value (literal integer required), adds one to it, and makes that the new case. For example, to load a different graphic (my plan is revealed!) each time a method is called:
method load_next.Logical: load_index++ which (load_index) caseNext: img_alpha = Image("alpha.png") caseNext: img_beta = Image("beta.png") caseNext: img_gamma = Image("gamma.png") caseNext: return false endWhich return true
If you started with “case 5:” then the first caseNext would be a shorthand for “case 6:”. If you start with caseNext (as above) then it resolves to “case 1:”.
For empty base classes like Operator, could you do something like:
class Operator()
method apply( Real64 lhs, Real64 rhs ).Real64: abstract
endClass
Also, that “Operator” is the example for inline classes screams language support for lambda functions.
Can you override the failed member access function yourself like Ruby? Well, I guess not since it’s compile time, but it would be rather cool…
The whole delegate property thing feels a lot like inheritance minus that pesky Liskov Substitution Principle. That is, a delegate property behaves exactly like an inherited base class, except the type checker won’t let you pass the derived class to methods that expect the base class.
Saying Operator() instead of putting an empty init() method in there: in theory yes; good thing you mentioned that ’cause it crashed my compiler when I tried it. 🙂 It’s fixed now though!
I started out wanting to *just* add lambda functions (or inline function definitions I called ’em). I was attempting to use a syntax that was totally new to Slag:
calculate( x, Fn(Real64 a,Real64 b).Real64 { return a+b }, y );
method calculate( Real64 a, Fn(Real64,Real64).Real64 op, Real64 b ): println( op.call(a,b) )
It ended up being really hard to implement (especially trying to correctly substitute template placeholder types at the right time) and much less versatile than the inline classes, so I scrapped it and went with inline classes instead!