Injectionless DCI in Scala (Marvin-inspired)

504 views
Skip to first unread message

Risto Välimäki

unread,
May 12, 2012, 2:49:07 AM5/12/12
to object-co...@googlegroups.com
Hi,

Thanks to Rune, we have a marvellous C#/.NET (or Mono) based, DCI-enabled language Marvin. Programming DCI in Marvin is as natural as breathing fresh air. 

However, I don't like Mono that much, since it's just less loved half brother of restricted, MS-controlled and non-crossplatform .NET. I have a few years of working experience on C#/.NET, and while C# is a lovely Java-clone (and in many ways better than the original), I like JVM much more. Furthermore, I'm taking my baby steps using Scala, which is a wonderful Java-compatible language often touted as "Java done right"(*).

Scala supports several paradigms, including ~procedural, class-oriented and (which is nice and rare) functional programming. Couple of years ago Cope suggested that Scala looks like it was designed for DCI. Unfortunately, it only looks like that. Even if there are Traits which as first glimpse look like a perfect solution for Roles, they suit only for book examples and not for real-life code (**). Reason for this is that Scala is utterly static language, just as Java. It's often said that Scala is "a statically typed dynamic language", but that's just bullshit. In fact Scala is a statically typed language with a syntax that resembles dynamic languages. Syntactically Scala is a very nice language that (using Stetson-Harrison method here:) easily removes 30-50% of boiler plate code needed in Java applications.

Injectionless DCI for the rescue

Fortunately, injectionless Roles (by Rune & Trygve) comes for the rescue here. Roles have several attributes that make it possible to use injectionless technique:
-Roles work as name and identifier (such as "Source" and "Destination")
-Roles are static (== no need for storing/saving variables)
-Roles are defined only inside one particular Context

Using injectionless DCI it's easy to avoid numerous extremely dangerous pitfalls there are with method injection such as: 
-Accidental Role-Data polymorphism (intented to call Data method from another Data method, Role method called instead)
-Accidental Data-Role polymorphism (same as above but reversed)
-Accidental Role-Role polymorphism (when Context are stacking and several Roles are effecting same time)
-Accidental removing of methods when un-injecting (eg. in Ruby undefining a Role method would remove Data method with identical name)
-Forgetting to remove injected methods after context is executed
-Impossible to remove injected methods after context is executed 

All above are easily avoided also by using wrappers (although accidental Role-Role polymorphism may be an issue depending the implementation), but as a bonus, there is no object schizophrenia.

(*) If Java is a toy language as Cope suggests and if Scala is "Java done right", Scala must be a roller-coaster...
(**) Traits CAN be used for Roles IFF roleplayers are created for example from database when Context starts, so I have to admit that in some environments (mainly the Web) it may be sufficient to use Traits for Roles.

*********************************************************************************'

OK, enough talking, let's just get into the code. Surprisingly I'm using money transfer example here. I'm also quite strictly following Rune's MoneyTransfer example on Marvin, just with Scala this time and there is no Ledgers yet but just basic "balance" variables.

Let's start from the usage, nothing fancy here (except that parenthesis for methods with 0 parameters are not needed in Scala, hence "transfer.trans" instead of "transfe.trans()":

******************************************************
var source = new Account(50.0)
var destination = new Account(20.0)
      
// Initialize context:
var transfer = new MoneyTransfer(source, destination, 20.0)
// Execute:
transfer.trans
      
// Just displaying that source and destination objects have really changed:
println("Source balance has changed and is now: " + source.Balance)      
println("Destination balance has changed and is now: " + destination.Balance)

Rune's Marvin example on the usage:

******************************************************

Then a simple Data class "Account":

******************************************************

// a basic Data class
class Account(initialBalance:BigDecimal) {
  private var balance:BigDecimal = initialBalance
  
  def Balance = balance  
  def DecreaseBalance(amount:BigDecimal) = balance -= amount  
  def IncreaseBalance(amount:BigDecimal) = balance += amount
}

Marvin counterpart:

******************************************************

And then is the interesting part. I have not yet looked that how to do a Scala compiler plugin for custom Marvin-like Role syntax. Anyhow, it should be possible. And what's more than great, it should be possible to add that compiler plugin for each Scala compiler version without modifying and re-compiling the original compiler. 

This is currently non-working code:

******************************************************

/**
 * This code is not valid Scala, but it should be possible to do a Scala compiler plugin 
 * to turn this code as above  
 *
 * the Context MoneyTransfer with roles Source, Destination & Amount. 
 * Also RoleContracts for RolePlayers are defined was Account, Account and BigDecimal
 */
class MoneyTransfer(Source:Account, Destination:Account, Amount:BigDecimal) {
  // -------------
  // Entry points:
  // -------------
  
  def trans() {
    Source.transfer(Amount)    
  }
  
  // --------------------
  // Roles & RoleMethods:
  // --------------------  
  role Source {
    def withdraw(amount:BigDecimal) {
 Source.DecreaseBalance(amount)
    }     
    def transfer(amount:BigDecimal) {
 Console.println("Source balance is: " + Source.Balance)  
 Console.println("Destination balance is: " + Source.Balance)
 
 Destination.deposit(amount)
 Source.withdraw(amount)
 
 Console.println("Source balance is now: " + Source.Balance)
 Console.println("Destination balance is now: " + Destination.Balance)
    }
  }
  
  role Destination{
    def deposit(amount:BigDecimal) {
      Destination.IncreaseBalance(amount)
    }
  }
}

Rune's Marvin original is found here:

There is not much differences between Marvin and "Scarvin" here, except that weird implicit access to values set by constructor that's a Scala feature.



However, this code is what really works in Scala and exactly what above code should produce with a custom (pre-)compiler plugin:

*************************************************************************************

 /**
 * DCI without method injection in Scala (This is what custom pre-compiler should produce using above code)
 * 
 * the Context MoneyTransfer with roles Source, Destination & Amount. 
 * Also RoleContracts for RolePlayers are defined was Account, Account and BigDecimal
 */
class MoneyTransfer(Source:Account, Destination:Account, Amount:BigDecimal) {
  // -------------
  // Entry points:
  // -------------  
  def trans() {
    Source_transfer(Amount)    
  }
  
  // ------------------------
  // Role methods for Source:
  // ------------------------  
  private def Source_withdraw(amount:BigDecimal) {
 Source.DecreaseBalance(amount)
  } 
  private def Source_transfer(amount:BigDecimal) {
 println("Source balance is: " + Source.Balance)  
 println("Destination balance is: " + Destination.Balance)
 
 Destination_deposit(amount)
 Source_withdraw(amount)
 
 println("Source balance is now: " + Source.Balance)
 println("Destination balance is now: " + Destination.Balance)
  }
  
  // -----------------------------
  // Role methods for Destination:
  // -----------------------------
  private def Destination_deposit(amount:BigDecimal) {
    Destination.IncreaseBalance(amount)
  }  
}

As you can see, Role methods are turned into private (and ~static, there is no static keyword in Scala) methods of the Context, and all Role.method() calls are translated into Role_method().

Next steps are:
1. create a compiler plugin that does the Role -> private role methods conversion (bad thing is that we are losing IDE support then)
(1b. add IDE support for Roles in Eclipse Scala plugin)
2. try to somehow mimic C# dynamic variable and dynamic method invocation for fully dynamic Role contracts
3. add "contract" keyword support and functionality from Marvin


Any questions, comments or rants?

-Risto

rune funch

unread,
May 12, 2012, 9:53:50 AM5/12/12
to object-co...@googlegroups.com
Den 12/05/2012 kl. 08.49 skrev "Risto Välimäki" <risto.v...@gmail.com>:

> 2. try to somehow mimic C# dynamic variable and dynamic method invocation for fully dynamic Role contracts

You probably don't need to. Simple use Object and cast/convert to the
argument type one the role method when invoked.
The biggest struggles I had with the rewrite was on the call site.
Changing the method overload resolution was a challenge

Risto Välimäki

unread,
May 12, 2012, 4:33:04 PM5/12/12
to object-co...@googlegroups.com
I have been working on Scala whole day; At morning I struggled to get my MoneyTransfer code compile and run (I'm nearly illiterate with Scala), and then about ten hours of constructing a wall for a sliding door made by Scala (Finnish door manufacturer Skaala)..

Rune: 
Thanks for your input. True, I can use Object, or in fact, class named "Any" in Scala. In Scala everything is an object including "primitive" variables such as Int. Even functions are objects in Scala. Anyhow, "Any" is the right superclass for any object in Scala.

I have never really used dynamic method invocation in Java (or Scala, which currently uses same methods for that, although native Scala reflection libraries are in development), but I hope it's just possible to iterate through all possible classes for a given object and then iterate through methods for that class until right one is found (or not...).

I think I'll not support 100% dynamic typing, but only strict and duck/contract typing.

So either use following standard implicit constructor for statically typed RolePlayers (Source type of Account etc.):
class MoneyTransfer(Source:Account, Destination:Account, Amount:BigDecimal) {

...or then do not set the type so that needed methods are checked from the role contract:
class MoneyTransfer(Source, Destination, Amount:BigDecimal) {
  role Source {
    contract {
       // following method is what should be found on "Any"one that wants to play role MoneyTransfer::Source:
      def decreaseAmount(amount: BigDecimal)  
    }
    ...
  }
 
Again, syntax is faithfully copied from Marvin.

Btw. If context can exist only in a stack (referring discussion on dci evolution list), it's probably wise to add "context" keyword that replaces "class" for contexts. Then, it would be easier to automate the context stack. (I have an argument against restricting context to exist only stack and having only one context active (even thought it wasn't a use case, but a just some background process done using DCI), but now I'm just too tired to elaborate.

-Risto



2012/5/12 rune funch <funchs...@gmail.com>

--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To post to this group, send email to object-co...@googlegroups.com.
To unsubscribe from this group, send email to object-composit...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/object-composition?hl=en.


Risto Välimäki

unread,
May 13, 2012, 4:16:41 AM5/13/12
to object-co...@googlegroups.com
Now, this IS heavy stuff! (+ some 111s here)

There is build-in support for duck typing in Scala which perfectly fits for Role contracts.

Following code is legal Scala, compiles and works well (as long as given object fullfills Role contracts. If not, compiling fails, which is nice):

class MoneyTransfer(
  // ---------------
  // Role contracts:
  // ---------------
  Source:{
    def decreaseBalance(amount:BigDecimal) 
    def balance:BigDecimal
  }, 
  Destination:{
    def increaseBalance(amount:BigDecimal) 
    def balance:BigDecimal
  }, 
  Amount:BigDecimal) {
  
  // -------------
  // Entry points:
  // -------------  
  def trans() {
    Source_transfer(Amount)    
  }
  // ------------------------
  // Role methods for Source:
  // ------------------------  
  private def Source_withdraw(amount:BigDecimal) {
 Source.decreaseBalance(amount)
  } 
  private def Source_transfer(amount:BigDecimal) {
 println("Source balance is: " + Source.balance)  
 println("Destination balance is: " + Destination.balance)
 
 Destination_deposit(amount)
 Source_withdraw(amount)
 
 println("Source balance is now: " + Source.balance)
 println("Destination balance is now: " + Destination.balance)
  }
  
  // -----------------------------
  // Role methods for Destination:
  // -----------------------------
  private def Destination_deposit(amount:BigDecimal) {
    Destination.increaseBalance(amount)
  }  
}

(I also changed method names from CamelCased to mixedCase, that's a Java/Scala convention. Last half year I have corrupted my mind programming on IEC 61131-3 ST language (used in PLC automation programming) where we have used CamelCase convention.

One would argue that there is also a convention to write variables as mixedCase and therefore Roles should also be mixed case. However, I think the exception for the rule is well justified here.)

I wrote that it's bs that "Scala is a statically typed dynamic language". Actually it seems that "Scala is a duck typed, duck typed language".

Well, that duck typed nature of Scala pretty much totally removes the need of dynamic typing and for example C#.NET-like "dynamic" type. Bonus is that in Dcala duck typing enables static type check in compile time. 

-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>

Tom B.

unread,
May 13, 2012, 7:48:10 AM5/13/12
to object-co...@googlegroups.com
I have absolutely no experience in Scala. But I'm wondering why did you prefer private methods instead a of private inline class?


Op zondag 13 mei 2012 10:16:41 UTC+2 schreef Risto Välimäki het volgende:
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To post to this group, send email to object-composition@googlegroups.com.
To unsubscribe from this group, send email to object-composition+unsub...@googlegroups.com.

For more options, visit this group at http://groups.google.com/group/object-composition?hl=en.




Op zondag 13 mei 2012 10:16:41 UTC+2 schreef Risto Välimäki het volgende:
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To post to this group, send email to object-composition@googlegroups.com.
To unsubscribe from this group, send email to object-composition+unsub...@googlegroups.com.

For more options, visit this group at http://groups.google.com/group/object-composition?hl=en.




Op zondag 13 mei 2012 10:16:41 UTC+2 schreef Risto Välimäki het volgende:
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To post to this group, send email to object-composition@googlegroups.com.
To unsubscribe from this group, send email to object-composition+unsub...@googlegroups.com.

rune funch

unread,
May 13, 2012, 7:57:47 AM5/13/12
to object-co...@googlegroups.com
Den 13/05/2012 kl. 13.48 skrev "Tom B." <t.b...@gmail.com>:

I have absolutely no experience in Scala. But I'm wondering why did you prefer private methods instead a of private inline class?
Not being Risto I can only argue why that's the case in Marvin. A role is not a type nor an object. I could have made all the role methods static methods of a private class but that complicates access to the other RolePlayers (I'd have to pass the context for each call and have a special case for member look up which in turn leads to having to extent the algorithm testing whether two members have the same name. It was a lot easier to simply make the RoleMethods private methods. 


-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To post to this group, send email to object-co...@googlegroups.com.
To unsubscribe from this group, send email to object-composit...@googlegroups.com.
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To post to this group, send email to object-co...@googlegroups.com.
To unsubscribe from this group, send email to object-composit...@googlegroups.com.
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To post to this group, send email to object-co...@googlegroups.com.
To unsubscribe from this group, send email to object-composit...@googlegroups.com.

For more options, visit this group at http://groups.google.com/group/object-composition?hl=en.

--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To view this discussion on the web visit https://groups.google.com/d/msg/object-composition/-/9M-Dcepre94J.
To post to this group, send email to object-co...@googlegroups.com.
To unsubscribe from this group, send email to object-composit...@googlegroups.com.

Tom B.

unread,
May 13, 2012, 9:07:24 AM5/13/12
to object-co...@googlegroups.com
Oh yes you're right overlooked the way Marvin does that. Although my major question comes from the interaction taking place inside the roles like with Marvin. I might have missed the discussion on that so I'll look it up and join the debate there.

Although I don't see why one would want to pass the context I would rather argue that you only need to pass the role it interacts with. From my point contexts knows its roles and visa-versa isn't required. Specially since Roles only exist in their own context and thus can't be accessed from the outside.

Besides that, I find it relieving that there are getting more and more examples of DCI. So kudos to you Risto.

Op zondag 13 mei 2012 13:57:47 UTC+2 schreef Rune het volgende:
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To post to this group, send email to object-composition@googlegroups.com.
To unsubscribe from this group, send email to object-composition+unsub...@googlegroups.com.
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To post to this group, send email to object-composition@googlegroups.com.
To unsubscribe from this group, send email to object-composition+unsub...@googlegroups.com.
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To post to this group, send email to object-composition@googlegroups.com.
To unsubscribe from this group, send email to object-composition+unsub...@googlegroups.com.

For more options, visit this group at http://groups.google.com/group/object-composition?hl=en.

--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To view this discussion on the web visit https://groups.google.com/d/msg/object-composition/-/9M-Dcepre94J.
To post to this group, send email to object-composition@googlegroups.com.
To unsubscribe from this group, send email to object-composition+unsub...@googlegroups.com.

For more options, visit this group at http://groups.google.com/group/object-composition?hl=en.

Op zondag 13 mei 2012 13:57:47 UTC+2 schreef Rune het volgende:
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To post to this group, send email to object-composition@googlegroups.com.
To unsubscribe from this group, send email to object-composition+unsub...@googlegroups.com.
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To post to this group, send email to object-composition@googlegroups.com.
To unsubscribe from this group, send email to object-composition+unsub...@googlegroups.com.
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To post to this group, send email to object-composition@googlegroups.com.
To unsubscribe from this group, send email to object-composition+unsub...@googlegroups.com.

For more options, visit this group at http://groups.google.com/group/object-composition?hl=en.

--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To view this discussion on the web visit https://groups.google.com/d/msg/object-composition/-/9M-Dcepre94J.
To post to this group, send email to object-composition@googlegroups.com.
To unsubscribe from this group, send email to object-composition+unsub...@googlegroups.com.

rune funch

unread,
May 13, 2012, 9:32:30 AM5/13/12
to object-co...@googlegroups.com
Den 13/05/2012 kl. 15.07 skrev "Tom B." <t.b...@gmail.com>:

Oh yes you're right overlooked the way Marvin does that. Although my major question comes from the interaction taking place inside the roles like with Marvin. I might have missed the discussion on that so I'll look it up and join the debate there.

Although I don't see why one would want to pass the context I would rather argue that you only need to pass the role it interacts with.
Roles it interacts with (plural)


From my point contexts knows its roles and visa-versa isn't required. Specially since Roles only exist in their own context and thus can't be accessed from the outside.

You could determine each role thats access from the method body and pass them but I think that's even more work than passing the context which as state I find to be more work than required to ensure that a role method can access other roles


-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To post to this group, send email to object-co...@googlegroups.com.
To unsubscribe from this group, send email to object-composit...@googlegroups.com.
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To post to this group, send email to object-co...@googlegroups.com.
To unsubscribe from this group, send email to object-composit...@googlegroups.com.
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To post to this group, send email to object-co...@googlegroups.com.
To unsubscribe from this group, send email to object-composit...@googlegroups.com.

For more options, visit this group at http://groups.google.com/group/object-composition?hl=en.

--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To view this discussion on the web visit https://groups.google.com/d/msg/object-composition/-/9M-Dcepre94J.
To post to this group, send email to object-co...@googlegroups.com.
To unsubscribe from this group, send email to object-composit...@googlegroups.com.
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To post to this group, send email to object-co...@googlegroups.com.
To unsubscribe from this group, send email to object-composit...@googlegroups.com.
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To post to this group, send email to object-co...@googlegroups.com.
To unsubscribe from this group, send email to object-composit...@googlegroups.com.
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To post to this group, send email to object-co...@googlegroups.com.
To unsubscribe from this group, send email to object-composit...@googlegroups.com.

For more options, visit this group at http://groups.google.com/group/object-composition?hl=en.

--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To view this discussion on the web visit https://groups.google.com/d/msg/object-composition/-/9M-Dcepre94J.
To post to this group, send email to object-co...@googlegroups.com.
To unsubscribe from this group, send email to object-composit...@googlegroups.com.

For more options, visit this group at http://groups.google.com/group/object-composition?hl=en.

--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To view this discussion on the web visit https://groups.google.com/d/msg/object-composition/-/dVF84-MuYVwJ.
To post to this group, send email to object-co...@googlegroups.com.
To unsubscribe from this group, send email to object-composit...@googlegroups.com.

Tom B.

unread,
May 13, 2012, 10:07:16 AM5/13/12
to object-co...@googlegroups.com
Yes you're completely right on that. Although I'm not questioning the way it is implemented. I'm questioning why it is implemented the way it is. It basically comes back to the way to the way I see the context and the way you see it. Rather than continuing here. I'll combine this one with the one Trygve posted over here ( https://groups.google.com/d/topic/object-composition/8Qe00Vt3MPc/discussion ) on the DCI-evolution list, cause I feel it is rather the same discussion. Although I'm going to read the document Trygve posted on code execution again to avoid any flaws in my way of thinking.

Op zondag 13 mei 2012 15:32:30 UTC+2 schreef Rune het volgende:
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To post to this group, send email to object-composition@googlegroups.com.
To unsubscribe from this group, send email to object-composition+unsub...@googlegroups.com.
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To post to this group, send email to object-composition@googlegroups.com.
To unsubscribe from this group, send email to object-composition+unsub...@googlegroups.com.
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To post to this group, send email to object-composition@googlegroups.com.
To unsubscribe from this group, send email to object-composition+unsub...@googlegroups.com.

For more options, visit this group at http://groups.google.com/group/object-composition?hl=en.

--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To view this discussion on the web visit https://groups.google.com/d/msg/object-composition/-/9M-Dcepre94J.
To post to this group, send email to object-composition@googlegroups.com.
To unsubscribe from this group, send email to object-composition+unsub...@googlegroups.com.
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To post to this group, send email to object-composition@googlegroups.com.
To unsubscribe from this group, send email to object-composition+unsub...@googlegroups.com.
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To post to this group, send email to object-composition@googlegroups.com.
To unsubscribe from this group, send email to object-composition+unsub...@googlegroups.com.
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To post to this group, send email to object-composition@googlegroups.com.
To unsubscribe from this group, send email to object-composition+unsub...@googlegroups.com.

For more options, visit this group at http://groups.google.com/group/object-composition?hl=en.

--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To view this discussion on the web visit https://groups.google.com/d/msg/object-composition/-/9M-Dcepre94J.
To post to this group, send email to object-composition@googlegroups.com.
To unsubscribe from this group, send email to object-composition+unsub...@googlegroups.com.

For more options, visit this group at http://groups.google.com/group/object-composition?hl=en.

--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To view this discussion on the web visit https://groups.google.com/d/msg/object-composition/-/dVF84-MuYVwJ.
To post to this group, send email to object-composition@googlegroups.com.
To unsubscribe from this group, send email to object-composition+unsub...@googlegroups.com.

Risto Välimäki

unread,
May 13, 2012, 4:13:26 PM5/13/12
to object-co...@googlegroups.com
Tom B: 
"I have absolutely no experience in Scala. But I'm wondering why did you prefer private methods instead a of private inline class?"

As Rune already explained, reason for that is mostly practical. Also it kind of violates DCI concepts to implement Roles as classes implicating there would be Role objects, which is not the case. There are no Role objects in DCI. Role is just an identifier ~= name, and oftentimes Roles also add additional functionality (RoleMethods) to RolePlayers, which are objects.

Essentially, closest to Role in Scala would be Trait, not Class. But as I said earlier, Traits cannot be (at least easily) just-in-time injected to RolePlayers. Hence, private methods in Context is by far the easiest and the least error prone way to implement RoleMethods in Scala (and in almost every language out there IMHO).

Tom B: 
"Although I'm not questioning the way it is implemented. I'm questioning why it is implemented the way it is."

If you are referring that use of injectionless technique instead of method injection, there are multiple reasons. To summarize, I have yet to see any (and I mean any) method injection technique that really works for other than as an schoolbook example. Numerous horrifying monsters are waiting for anyone who tries to do real world DCI with method injection including (quoting my first message on this thread):

-Accidental Role-Data polymorphism (intented to call Data method from another Data method, Role method called instead)
-Accidental Data-Role polymorphism (same as above but reversed)
-Accidental Role-Role polymorphism (when Context are stacking and several Roles are effecting same time)
-Accidental removing of methods when un-injecting (eg. in Ruby undefining a Role method would remove Data method with identical name)
-Forgetting to remove injected methods after context is executed
-Impossible to remove injected methods after context is executed 

Although not every problem might be happening simultaneously (eg. in pretty much every technique there _will_ be accidental Role-Data polymorphism but not vice versa), I'll bet that every existing method injection technique will have at least two or three of above pitfalls waiting for innocent coders.

Wrappers are not that problematic, but they are plagued with identity problems.

***

When it comes to Roles knowing about each others and about Context, I was also opposing the idea at first, but no more. I think there is a consensus on this that indeed, Roles know about the Context so there is no need to pass RolePlayers as arguments of RoleMethods.

***

Tom B:  
"It basically comes back to the way to the way I see the context and the way you see it. Rather than continuing here. I'll combine this one with the one Trygve posted over here ( https://groups.google.com/d/topic/object-composition/8Qe00Vt3MPc/discussion ) on the DCI-evolution list, cause I feel it is rather the same discussion."

AFAIK, at that post Trygve was talking about using DCI Context ("distributed") instead of a Mediator ("centralized"). But that's a different topic. In short: No, injectionless RoleMethods in DCI doesn't turn the Context into a Mediator.

Context with injectionless RoleMethods may be technically, under-the-hood, almost exactly like a Mediator. But that's not interesting. If you look at Marvin code example, you just can't know that technically it's like a Mediator (and won't know from my Scala code if I manage to get my own Scala compiler plugin with role support done). It doesn't look like Mediator in any way. It's not semantically a Mediator. It's semantically and code-wise distributed (Context  is not defined as a single entity, but different roles of that context are defined separately), not centralized as Mediator solution would be. 

This quote from Cope explains quite well the relationship between Role and Context and between Role and other Roles in that particular context:

"To me a Role is always defined in terms of its relationships to other roles. ... the definition of Role cannot be separated from the Context. By the same token, a Context is defined largely by its roles and interactions."

Distributed nature of DCI that Trygve talked about doesn't mean that Roles should be defined elsewhere than Context.

As Trygve put it in 21st of January (emphasis mine):
"Yes. role is internal to its context and has no meaning outside it
BUT, some people feel that this may be too strict. I suspect there might be something interesting hidden here, but I do not now what it is. We must look elsewhere, because simply sharing roles between different contexts makes no sense."

-Risto

2012/5/13 Tom B. <t.b...@gmail.com>

To post to this group, send email to object-co...@googlegroups.com.
To unsubscribe from this group, send email to object-composit...@googlegroups.com.

Tom B.

unread,
May 13, 2012, 6:12:22 PM5/13/12
to object-co...@googlegroups.com
Yes I was more of referring to the wrapper implementation. Making a role a private static ( for sake of completeness ) class places it inside the scope of the context. Hence my reason for asking. I've heard the argument of identity problems on several occasions. I fail to see how it is related ("data is what the system is not what is does"). So if I call a method on a role it will always be on the role. The role in its turn may reference the data through this which is contained in some place else. I thought Scala had some magic functions that where able to emulate this redirection behaviour.

About the topic. I get the feeling it was/is more about where the interaction should take place. In Rune's current Marvin example on the site and also his post on that topic the interaction directly takes place between objects. To me that doesn't feel like the way to go.

Op zondag 13 mei 2012 22:13:26 UTC+2 schreef Risto Välimäki het volgende:
2012/5/13 Tom B. <t.b...@gmail.com>
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To unsubscribe from this group, send email to object-composition+unsubscribe@googlegroups.com.
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To unsubscribe from this group, send email to object-composition+unsubscribe@googlegroups.com.
-Risto

2012/5/12 Risto Välimäki <risto.v...@gmail.com>



2012/5/12 rune funch <funchs...@gmail.com>
To unsubscribe from this group, send email to object-composition+unsubscribe@googlegroups.com.

For more options, visit this group at http://groups.google.com/group/object-composition?hl=en.

--
You received this message because you are subscribed to the Google Groups "object-composition" group.

Tom B.

unread,
May 13, 2012, 7:55:39 PM5/13/12
to object-co...@googlegroups.com
After a bit of more reading i found that my definition of method injection was too literal. Im wondering though if the method I described above also means method injection generaly speaken that is.

Jörgen Andersson

unread,
Dec 22, 2012, 11:35:59 AM12/22/12
to object-co...@googlegroups.com

Hi,
I know this thread is a bit old, but never the less I find it very interesting. The Marvin-inspired syntax looks really nice, but even the pure Scala, with prefixed role methods,  is a usable solution, I think. Especially with the use of Scala duck-typing to define role contracts.

I have one issue with the use of context-private methods to implement role methods, though. And that is that by having every role method globally accessible inside the context I see no way to decompose role method impementations using private methods, like you would do in almost any other case to increase clarity, and have the code communicate which metods are considered public role methods and which are purely decomposed implementation. Have this been discussed previously or is there in general no such need in the DCI paradigm?

Regards,
Jörgen

Ant Kutschera

unread,
Dec 22, 2012, 6:12:41 PM12/22/12
to object-co...@googlegroups.com
I've missed something.  This context compiles, but how do you call it using Account objects?

 Scala 2.9.2 gives this error:

type mismatch; found : Account required: AnyRef{def increaseBalance(amount: BigDecimal): Unit; def balance: BigDecimal}

Risto Välimäki

unread,
Dec 23, 2012, 4:16:16 AM12/23/12
to object-co...@googlegroups.com

@Ant

Your Account object is missing one of those methods or return values or parameter types for those methods are wrong.

Just implement increaseBalance(amount :BigDecimal): Unit and balance : BigDecimal in Account class and that should work.

--
You received this message because you are subscribed to the Google Groups "object-composition" group.

Ant Kutschera

unread,
Dec 23, 2012, 6:18:30 AM12/23/12
to object-co...@googlegroups.com
On Sunday, 23 December 2012 10:16:16 UTC+1, Risto Välimäki wrote:

@Ant

Just implement increaseBalance(amount :BigDecimal): Unit and balance : BigDecimal in Account class and that should work.


Ah ok, got it working.

Where did you learn to write code like this:

class MoneyTransfer(
  // ---------------
  // Role contracts:
  // ---------------
  Source:{
    def decreaseBalance(amount:BigDecimal) 
    def balance:BigDecimal
  }, 
  Destination:{
    def increaseBalance(amount:BigDecimal) 
    def balance:BigDecimal
  }, 
  Amount:BigDecimal) {
...

I've been using Scala for a year and have never seen that way of dynamically defining an interface...

Ant Kutschera

unread,
Dec 23, 2012, 6:23:22 AM12/23/12
to object-co...@googlegroups.com
On Sunday, 13 May 2012 10:16:41 UTC+2, Risto Välimäki wrote:
Now, this IS heavy stuff! (+ some 111s here)

There is a problem with this thread.  Your solution isn't object oriented.  In your method where the context is executed, you are just doing procedural programming:

  // -------------
  // Entry points:
  // -------------  
  def trans() {
    source_transfer(amount)    
  }

No roles have been assigned to objects, and you are not calling role methods on objects.

So this isn't DCI :-(

Marc Grue

unread,
Dec 23, 2012, 8:46:31 AM12/23/12
to object-co...@googlegroups.com
Hi Jörgen,

As a Scala alternative to Risto's solution, you don't need duck typing at all to define the role contracts if you use my macro approach (see my marvin-inspired MoneyTransfer example).

Duck typing doesn't really give you type safety - only type signature safety! Two different Data objects might have the same decreaseBalance(amount: BigDecimal) signature and do completely different things with that amount! One might start a nuclear war as a side effect and the other printing the amount to the console. Without being able to tell the difference in advance, you can end up with nasty surprises - which defeats one of the purposes of dci.

With the enforced self type of my Role trait, the compiler only allows a particular Data object type to play a specific Role. That enables you to reason about the code in a type safe manner without surprises.

I think it's also closer to the end user mental model of a Role when the behavior of that Role "belongs" to that Role. I want Role methods to belong to a Role name space (as they do in my Role traits). With the duck typing solution, Role methods with RoleName-prefixes all hang around in a collective Context name space. The RoleName-prefix could be misspelled and the compiler wouldn't care. Someone new on the team might go ahead reusing Role methods between Roles and give the methods random name-prefixes reflecting "shared functionality". And then we're down the path of blurring Role boundaries, loosing the connection to the mental model and not doing DCI anymore. 

HTH

Cheers,
Marc

Marc Grue

unread,
Dec 23, 2012, 9:20:56 AM12/23/12
to object-co...@googlegroups.com
Good overview of different Scala features:


Cheers,
Marc

--
You received this message because you are subscribed to the Google Groups "object-composition" group.

Jörgen Andersson

unread,
Dec 23, 2012, 12:50:16 PM12/23/12
to object-co...@googlegroups.com

Hi Marc,

I saw your  approach using macros some week ago, and I was impressed. But since then I've been following the discussion on this list regarding wether method injection is a good way to implement DCI in laguages available today and I've found my self leaning towards saying NO, due to problems with role methods affecting the objects even outside the contexts where they belong. Not going deeper into the discussion here as it is available in a resent thread on this list.

Then I found Ristos injection-less approach and I think it embodies the spirit of DCI even though, when it comes to role-method-prefixes, does it based on convention rather than compliation support. And that's where my thoughts on public versus private role methods arose.

>
> Duck typing doesn't really give you type safety - only type signature safety! Two different Data objects might have the same decreaseBalance(amount: BigDecimal) signature and do completely different things with that amount!

Isn't that the intention, that different data objects (or other contexts) should be able to play a role in a context if they fulfill the role contract?

> One might start a nuclear war as a side effect and the other printing the amount to the console. Without being able to tell the difference in advance, you can end up with nasty surprises - which defeats one of the purposes of dci.

I think it is up to the code constructing and executing the context to make sure the right implementations are supplied.

>
> I think it's also closer to the end user mental model of a Role when the behavior of that Role "belongs" to that Role. I want Role methods to belong to a Role name space (as they do in my Role traits). With the duck typing solution, Role methods with RoleName-prefixes all hang around in a collective Context name space. The RoleName-prefix could be misspelled and the compiler wouldn't care. Someone new on the team might go ahead reusing Role methods between Roles and give the methods random name-prefixes reflecting "shared functionality". And then we're down the path of blurring Role boundaries, loosing the connection to the mental model and not doing DCI anymore. 

Yes, I mostly agree with this. That is somewhat inline with my thought of lack of support for role-private methods. This got me started on thinking about a wrapped approach, but I've understood it is also rather questionable as a badis or a DCI-implementation. I havn't been able to find a discusdion on that as detailed as the one on method injection, so I can't really see all pros and cons.

BR,
Jörgen

James O Coplien

unread,
Dec 23, 2012, 2:16:31 PM12/23/12
to object-co...@googlegroups.com
On May 13, 2012, at 8:07 , Tom B. wrote:

> Although I don't see why one would want to pass the context I would rather
> argue that you only need to pass the role it interacts with. From my point
> contexts knows its roles and visa-versa isn't required. Specially since
> Roles only exist in their own context and thus can't be accessed from the
> outside.

I can absolutely see passing a Context instance. In the most generalized versions of DCI there are only Contexts and values — no classes (and this is kind of where Marvin is going). There is a lot of exploration that needs to be done in this area, viz. with respect to Context re-entrancy.

You can't pass a role. You can pass an object. If you pass an object to a method of an object that is outside that Context instantiation, any invocations of its non-const methods probably leads to undefined behavior in most implementations of DCI. Invocation of the Context's role methods in such a configuration is undefined behavior in all implementations of DCI, by definition.


Risto Välimäki

unread,
Dec 23, 2012, 6:08:45 PM12/23/12
to object-co...@googlegroups.com


23.12.2012 19.50 "Jörgen Andersson" <jorgen.x....@gmail.com> kirjoitti:
>
> Hi Marc,
>
> I saw your  approach using macros some week ago, and I was impressed. But since then I've been following the discussion on this list regarding wether method injection is a good way to implement DCI in laguages available today and I've found my self leaning towards saying NO, due to problems with role methods affecting the objects even outside the contexts where they belong. Not going deeper into the discussion here as it is available in a resent thread on this list.
>
> Then I found Ristos injection-less approach and I think it embodies the spirit of DCI even though, when it comes to role-method-prefixes, does it based on convention rather than compliation support. And that's where my thoughts on public versus private role methods arose.
>

Actually my intention is to make a compiler plugin to get rid of Role_method() syntax. That Role_method() thing (that was scarying Ant, btw) is something that only compiler should ever see. Earlier on this "thread" you can find what's the actual code like. As far as I remember, syntax should be like:

class MoneyTransfer( ... role contracts here ...) {
  .. constructors and entry points here...

  Role Source {
    def transferTo(...) {}
  }
  Role Foo {
    def bar() = ...
  }
}

I started writing the compiler plugin and managed to find all Role and Role method definitions, but being almost Scala illiterate I had hard times manipulating immutable trees and then I've been far too busy.

I haven't even checked Marc's code yet even it looked very interesting.

> >
> > Duck typing doesn't really give you type safety - only type signature safety! Two different Data objects might have the same decreaseBalance(amount: BigDecimal) signature and do completely different things with that amount!

Compiler checked duck typing + the fact that you as a developer allow Account to play role Moneytransfer::Source combined gives pretty much type safety for me, but yes, you never know for sure.

That is exactly same thing with Interfaces: you can just hope that they are implemented right. Even common super class / Trait doesn't guarantee that everything works as expected, since that method might have been overrided by wrong implementation.

>
> Isn't that the intention, that different data objects (or other contexts) should be able to play a role in a context if they fulfill the role contract?

Yes, that's intented. Biggest reason for this is that we don't want to pollute and also change all the time the structure / base of our application (what-the-system-is part). By not changing structure all the time when we need a new functionality also gives us more safety that our behaviour / what-the-system-does part is not constantly suffering for regressions.

>
> > One might start a nuclear war as a side effect and the other printing the amount to the console. Without being able to tell the difference in advance, you can end up with nasty surprises - which defeats one of the purposes of dci.
>
> I think it is up to the code constructing and executing the context to make sure the right implementations are supplied.
>
> >
> > I think it's also closer to the end user mental model of a Role when the behavior of that Role "belongs" to that Role. I want Role methods to belong to a Role name space (as they do in my Role traits). With the duck typing solution, Role methods with RoleName-prefixes all hang around in a collective Context name space. The RoleName-prefix could be misspelled and the compiler wouldn't care. Someone new on the team might go ahead reusing Role methods between Roles and give the methods random name-prefixes reflecting "shared functionality". And then we're down the path of blurring Role boundaries, loosing the connection to the mental model and not doing DCI anymore. 
>
> Yes, I mostly agree with this. That is somewhat inline with my thought of lack of support for role-private methods. This got me started on thinking about a wrapped approach, but I've understood it is also rather questionable as a badis or a DCI-implementation. I havn't been able to find a discusdion on that as detailed as the one on method injection, so I can't really see all pros and cons.
>
> BR,
> Jörgen
>

Reply all
Reply to author
Forward
0 new messages