DCI in Haxe

60 views
Skip to first unread message

Andreas

unread,
May 13, 2013, 10:18:18 PM5/13/13
to object-co...@googlegroups.com
I've had success with a DCI implementation in Haxe 3 (http://haxe.org). It's a nice language with great type inference and macros that can rewrite the AST, enabling a fully-functional DCI implementation. Proof of concept:

package contexts;

typedef SourceAccountInterface = 
{
    function Withdraw(amount : Float) : Void;
}

typedef DestinationAccountInterface = 
{
    function Deposit(amount : Float) : Void;
}

typedef MoneyTransferRoles =
{
    private var sourceAccount : SourceAccount;
    private var destinationAccount : DestinationAccount;
    private var amount : Amount;
}

@:build(Dci.context())
class MoneyTransfer
{
    private var sourceAccount : SourceAccount;
    private var destinationAccount : DestinationAccount;
    private var amount : Amount;

    public function new(source, destination, amount)
    {
        bindRoles(source, destination, amount);
    }

    private function bindRoles(source, destination, amt)
    {
        sourceAccount = new SourceAccount(source);
        destinationAccount = new DestinationAccount(destination);
        amount = new Amount(amt);
    }
    
    // Interaction
    public function Execute()
    {
        sourceAccount.Transfer();
    }
}

@:build(Dci.role(MoneyTransfer))
private abstract Amount(Float) from Float to Float
{
    public inline function new(amount)
    {
        this = amount;
    }    
}

@:build(Dci.role(MoneyTransfer))
private abstract SourceAccount(SourceAccountInterface)
{
    public inline function new(account)
    {
        this = account;
    }
    
    public function Transfer()
    {
        this.Withdraw(context.amount);
        context.destinationAccount.Deposit(context.amount);
    }
}

@:build(Dci.role(MoneyTransfer))
private abstract DestinationAccount(DestinationAccountInterface)
{
    public inline function new(account)
    {
        this = account;
    }
    
    public function Deposit(amount : Float)
    {
        this.Deposit(amount);
    }
}


Some notes for this very early version:

- The abstract types are not wrappers! They are compiled into a part of the underlying type, but the private accessibility makes them only usable in the Context class.
- Using the @:build macro, AST rewritings are made for setting the current Context in the Interactions, and accessing it through "context" in the RoleMethods.
- The MoneyTransferRoles typedef is a little DRY violation, but it enables the Roles in the Context to be private and still accessible through the RoleMethods. Reflection could be used instead.

Everything I've made so far can surely be made in better ways, but I just wanted to show that the road is open for DCI in a language that compiles to javascript, flash, neko, php, C++, C# and java. I'll put the code up on github later in case someone is interested.

Raoul Duke

unread,
May 14, 2013, 5:33:55 PM5/14/13
to object-co...@googlegroups.com
($0.02 haxe is cool! though in my experience at least the java port is
quite incomplete.)

Matthew Browne

unread,
May 16, 2013, 11:12:18 AM5/16/13
to object-co...@googlegroups.com
This is very cool! Great work.

The only thing I noticed as potentially confusing is your bindRoles method since it makes it look like the roles are wrappers when in fact they aren't. Would a syntax like this be feasible:

    private function bindRoles(source, destination, amt)
    {
        sourceAccount = SourceAccount.bindData(source);
        destinationAccount = DestinationAccount.bindData(destination);
        amount = Amount.bindData(amt);
    }


This is just a thought and there's probably a counter-argument. In any case this is a great contribution.

Matthew Browne

unread,
May 16, 2013, 11:17:44 AM5/16/13
to object-co...@googlegroups.com
It would be even better of course if you could do this:

    private function bindRoles(source, destination, amt)
    {
        sourceAccount = source.bindRole(SourceAccount);
        ...
    }


But I'm guessing that's not possible...
--
You received this message because you are subscribed to a topic in the Google Groups "object-composition" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/object-composition/CVRDkHK_gW8/unsubscribe?hl=en.
To unsubscribe from this group and all its topics, send an email to object-composit...@googlegroups.com.
To post to this group, send email to object-co...@googlegroups.com.
Visit this group at http://groups.google.com/group/object-composition?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Andreas

unread,
May 16, 2013, 11:29:39 AM5/16/13
to object-co...@googlegroups.com
Thanks Matthew, glad you like it! I have worked on it some more now, and have simplifed some things. The Context typedef is gone now, among other things. The currently working example looks like this:

package dci.examples.moneytransfer.contexts;

typedef ISourceAccount = {
function withdraw(amount : Float) : Void;
}

typedef IDestinationAccount = {
function deposit(amount : Float) : Void;
}

typedef IAmount = Float;

class MoneyTransfer implements dci.Context {
@role var sourceAccount : SourceAccount;
@role var destinationAccount : DestinationAccount;
@role var amount : Amount;

public function new(source : ISourceAccount, destination : IDestinationAccount, amount : IAmount) {
bindRoles(source, destination, amount);
}

function bindRoles(source, destination, amt) {
sourceAccount = new SourceAccount(source);
destinationAccount = new DestinationAccount(destination);
amount = new Amount(amt);
}
public function Execute() {
sourceAccount.Transfer();
}
}

@:build(Dci.role(MoneyTransfer))
private abstract Amount(IAmount) from IAmount to IAmount
public inline function new(amount) {
this = amount;
}
}

@:build(Dci.role(MoneyTransfer))
private abstract SourceAccount(ISourceAccount) {
public inline function new(account) {
this = account;
}
public function Transfer() {
this.withdraw(context.amount);
context.destinationAccount.deposit(context.amount);
}
public function withdraw(amount : Float) {
this.withdraw(amount);
}
}

@:build(Dci.role(MoneyTransfer))
private abstract DestinationAccount(IDestinationAccount) {
public inline function new(account) {
this = account;
}
public function deposit(amount : Float) {
this.deposit(amount);
}
}


A little bit simpler. But I will soon try to remove the abstract type constructors too, it can probably be autogenerated with macros (maybe, since they are only abstract types, not existing at runtime). And I'll definitely apply your role binding suggestions, it's possible that the whole bindRoles method can be created with macros. The macro system is extremely powerful and easy to use after a little hurdle in the beginning, so anything is possible, perhaps auto-generating the RoleInterfaces based on empty function definitions in the Roles... Please bring on the suggestions if you have more. :)


2013/5/16 Matthew Browne <mbro...@gmail.com>

--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To unsubscribe from this group and stop receiving emails from it, send an email to object-composit...@googlegroups.com.

Matthew Browne

unread,
May 16, 2013, 11:40:12 AM5/16/13
to object-co...@googlegroups.com
Nice improvements. I've only glanced at Haxe so I'm not familiar with all its syntax...what does this mean:

private abstract TypeName

as in:

private abstract SourceAccount

I notice the class keyword is absent, which makes sense since a role isn't a class. If we were talking about extensions to a different class-oriented language (say Java) I would be inclined to suggest a role keyword like in Marvin, but that seems not to be necessary here.

Andreas

unread,
May 16, 2013, 11:49:18 AM5/16/13
to object-co...@googlegroups.com
It's the definition of an abstract type, which has plenty of features but it does what a role should do, attaches methods to a type, and by making it private it can only be used in the current namespace. Here's the official explanation: http://haxe.org/manual/abstracts

One thing I didn't mention is that everything is typesafe so any errors occurs at compilation, since Haxes type inference is so good.


2013/5/16 Matthew Browne <mbro...@gmail.com>

--

Axel Huizinga

unread,
Jul 26, 2013, 2:13:19 PM7/26/13
to object-co...@googlegroups.com


Am Donnerstag, 16. Mai 2013 17:49:18 UTC+2 schrieb Andreas Söderlund:
It's the definition of an abstract type, which has plenty of features but it does what a role should do, attaches methods to a type, and by making it private it can only be used in the current namespace. Here's the official explanation: http://haxe.org/manual/abstracts

One thing I didn't mention is that everything is typesafe so any errors occurs at compilation, since Haxes type inference is so good.


2013/5/16 Matthew Browne <mbro...@gmail.com>
Nice improvements. I've only glanced at Haxe so I'm not familiar with all its syntax...what does this mean:

private abstract TypeName

as in:

private abstract SourceAccount

I notice the class keyword is absent, which makes sense since a role isn't a class. If we were talking about extensions to a different class-oriented language (say Java) I would be inclined to suggest a role keyword like in Marvin, but that seems not to be necessary here.

--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To unsubscribe from this group and stop receiving emails from it, send an email to object-composition+unsub...@googlegroups.com.

To post to this group, send email to object-co...@googlegroups.com.
Visit this group at http://groups.google.com/group/object-composition?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Looks very interesting but I don't get it :)
public inline function new(account) {
this = account;
}
this instance assignment looks weird to me - a complete example with entry point to compile and try would be great
 

Andreas

unread,
Jul 27, 2013, 7:10:26 AM7/27/13
to object-co...@googlegroups.com
2013/7/26 Axel Huizinga <paradisep...@gmail.com>

Looks very interesting but I don't get it :)
public inline function new(account) {
this = account;
}
this instance assignment looks weird to me - a complete example with entry point to compile and try would be great

It looks a bit weird and similar to a wrapper, but when compiling it attaches all methods of the abstract type on the object specified in the constructor parameter. And when making the abstract type private, it can be kept within a Context class and then works like a DCI Role. Here's a working example with explanation: https://github.com/ciscoheat/haxedci-example

The documentation for abstract types in Haxe: http://haxe.org/manual/abstracts#opaque-types


/Andreas

Reply all
Reply to author
Forward
0 new messages