Sure...for Contexts, I was thinking of something like Cope's money transfer example where CheckingAccount, SavingsAccount, and InvestmentAccount are subtypes of Account. If Account were implemented as a DCI Context wrapping a collection of ledgers like it is in other versions such as Rune's, then how would one create the subtypes?
I was thinking more in terms of overall strategies for managing subtyping rather than solving a particular problem in a real project, but I can think of other examples if needed. I'm not currently facing this as a real-world problem, just anticipating that it's something I or others might encounter in the future.
(Actually there was a more esoteric case I
encountered on a real project where I thought Context extension
might be helpful, but that was a special case involving code
management for 2 very similar systems with a very similar use
case that had slight differences in each system. But that's not
what I was thinking of when I raised this question...and I'm
less certain of whether subtyping was really needed there or if
there was a better solution.)
--
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.
To post to this group, send email to object-co...@googlegroups.com.
Visit this group at https://groups.google.com/group/object-composition.
For more options, visit https://groups.google.com/d/optout.
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-composition@googlegroups.com.
Visit this group at https://groups.google.com/group/object-composition.
For more options, visit https://groups.google.com/d/optout.
--
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-composition@googlegroups.com.
Sure...for Contexts, I was thinking of something like Cope's money transfer example where CheckingAccount, SavingsAccount, and InvestmentAccount are subtypes of Account. If Account were implemented as a DCI Context wrapping a collection of ledgers like it is in other versions such as Rune's, then how would one create the subtypes?
To ensure that specific Account can be varied -- use interface Account or role Account in the parent context.
Hmm, perhaps that's all that's needed for Contexts. (It still leaves the question of whether there should be a separate construct for simple data objects - i.e. do we need classes - and whether inheritance should be available for those, but let's focus on contexts for the moment.)
I just tried out creating a Context in trygve that implements an interface and it works. (I either forgot about that or didn't realize that feature had been added - thanks Cope!)
To implement a type hierarchy that goes more than 2 levels deep, I suppose a Context could just implement all the relevant interfaces, e.g.:
context MyContext
implements ParentInterface, SomeInterface
Yes, inheritance (or any kind of implicit extension) of Contexts
is a slippery slope - thanks for the explanation. While I can see
potential uses for it in the data model (e.g. the Account
example), it could be risky to allow it for typical use case
Contexts (e.g. the MoneyTransfer context), and I'm not sure how
you would allow the former while preventing the use of the latter.
On the other hand, I also think Rune (over the course of past discussions) has made a compelling case for doing away with classes and using contexts to define all objects with the exception of immutable data objects.
On 12/5/16 12:24 PM, James O Coplien wrote:
So I think we’re stuck with both.
Well, there are alternatives to inheritance that would perhaps be
suitable for Contexts in ways that inheritance is not...like maybe
embeding/composition as Egon suggested as a possible
alternative...or some way to make implementing multiple interfaces
for related types more convenient (maybe not quite as convenient
as "Car extends Vehicle" but close). The hard part would be to
keep it "useful without being confusing, if tastefully used".
Something to think about...
To ensure that specific Account can be varied -- use interface Account or role Account in the parent context.Hmm, perhaps that's all that's needed for Contexts. (It still leaves the question of whether there should be a separate construct for simple data objects - i.e. do we need classes - and whether inheritance should be available for those, but let's focus on contexts for the moment.)I just tried out creating a Context in trygve that implements an interface and it works. (I either forgot about that or didn't realize that feature had been added - thanks Cope!)
To implement a type hierarchy that goes more than 2 levels deep, I suppose a Context could just implement all the relevant interfaces, e.g.:
context MyContext implements ParentInterface, SomeInterface
...so that you could do this:
MyContext c = new MyContext();
c instanceof ParentInterface; //true
c instanceof SomeInterface; //true
(There doesn't seem to be an instanceof operator in trygve, but the above is just intended to show the idea.)
Does that sound like a good approach?
Yes, I was just going to add...we already have good ways of implementing abstraction boundaries, as Cope pointed out. The only thing really lacking in terms of the end result is that you don't automatically get parent types and sub-types like you do when extending classes. I think your suggestion of using interfaces is a fine solution; the main reason to consider alternatives would be concerns about syntax or convenience.
This would probably be a bigger concern for a new language that
did away with classes entirely. In terms of the approach of the
trygve language, the current features seem sufficient to get the
job done, although some form of implicit interfaces might make it
a bit easier. Maybe it would help if one interface could inherit
from another?
On 12/5/16 2:19 PM, Matthew Browne wrote:
In terms of the approach of the trygve language, the current features seem sufficient to get the job done, although some form of implicit interfaces might make it a bit easier. Maybe it would help if one interface could inherit from another?
I should have made it clearer that these are two separate,
orthogonal ideas.
Yes, I was just going to add...we already have good ways of implementing abstraction boundaries, as Cope pointed out. The only thing really lacking in terms of the end result is that you don't automatically get parent types and sub-types like you do when extending classes. I think your suggestion of using interfaces is a fine solution; the main reason to consider alternatives would be concerns about syntax or convenience.
This would probably be a bigger concern for a new language that did away with classes entirely. In terms of the approach of the trygve language, the current features seem sufficient to get the job done, although some form of implicit interfaces might make it a bit easier. Maybe it would help if one interface could inherit from another?
To implement all these interfaces you would need to do:
type MockAccount struct { ... }func (acc *MockAccount) Withdraw(amount int) { ... }
func (acc *MockAccount) Deposit(amount int) { ... }
func (acc *MockAccount) Print() { ... }
Never mind, I just remembered that in Go, as long as a type
implements all the methods in an interface, then it's
automatically considered to be of that interface type (without
needing to explicitly specify that it implements the interface
with an "implements" keyword or anything like that). Kind of like
the duck typing of role players in trygve. Did I get that right?
Never mind, I just remembered that in Go, as long as a type implements all the methods in an interface, then it's automatically considered to be of that interface type (without needing to explicitly specify that it implements the interface with an "implements" keyword or anything like that). Kind of like the duck typing of role players in trygve. Did I get that right?
On 5 Dec 2016, at 20.55, Egon Elbre <egon...@gmail.com> wrote:This sometimes helps to get better error-messages... or catch the errors nearer the implementation.
I realized that I was thinking of something different in terms of
"implicit interfaces" than what Egon meant. Egon's usage is the
more common and logical use of the term, so let me describe what I
was imagining, which is something different (I was thinking about
how implicit/explicit the implementation of the interface
would be...I'll avoid the misuse of the term "implicit interfaces"
in the future).
The below pseudocode is just one of many possible ways to facilitate subtyping of Contexts:
interface
AccountInterface {
public void increaseBalance(Currency amt);
public void decreaseBalance(Currency amt);
public Currency getBalance();
}
context GeneralLedgerAccount implements AccountInterface {
...
}
context SavingsAccount subtypeOf GeneralLedgerAccount {
public void increaseBalance: Account.increaseBalance;
public void decreaseBalance: Account.decreaseBalance;
public Currency availableBalance: Account.availableBalance;
public SavingsAccount() {
Account = new GeneralLedgerAccount();
}
role Account {
public void increaseBalance(Currency amt);
public void decreaseBalance(Currency amt);
public Currency availableBalance();
}
requires {
void increaseBalance(Currency amt);
void decreaseBalance(Currency amt);
Currency availableBalance();
}
...
}
The idea here is that the programmer can tell the compiler to
create forwarding methods automatically (first 3 lines of
SavingsAccount forward calls to increaseBalance, decreaseBalance,
and availableBalance to the Account role player). And instances of
SavingsAccount would also be instances of AccountInterface and
GeneralLegerAccount.
Note: The AccountInterface is used to stay in keeping with Cope's example where Account is an abstract type; that's why I called the concrete implementation GeneralLedgerAccount, with the idea being that SavingsAccount and CheckingAccount are both subtypes of GeneralLedgerAccount. I don't have much domain knowledge of banking so I don't know if this structure makes sense, but hopefully it's sufficient for discussion purposes.
The above is still a little verbose and we could go one step further, e.g.:
context
SavingsAccount subtypeOf GeneralLedgerAccount {
expose Account {increaseBalance, decreaseBalance,
availableBalance);
...
}
As I mentioned earlier, I'm thinking at this point mainly of convenience and reducing the amount of boilerplate code required to achieve this sort of thing. The semantics would need refinement and careful consideration of course, but what do you all think?
Since this feature wouldn't actually allow you to accomplish
anything that you can't already accomplish with more verbose code
in trygve, I'm unsure whether or not it would be worth
implementing in trygve since it's a research language. But I do
think that at least for a production language, facilitating the
subtyping of contexts with something similar to the above would be
useful.
This is how you would do it in Go:
type Account interface {IncreaseBalance(amount Currency)DecreaseBalance(amount Currency)AvailableBalance() Currency}
type SavingsAccount struct {Account
...}
You can use either:
acc := &SavingsAccount{...}acc.IncreaseBalance(10)acc.Account.IncreaseBalance(10)
Second, if the same name appears at the same nesting level, it is usually an error; it would be erroneous to embedAnd there are also alternative ways that name conflicts could be handled (e.g. the way name conflicts are handled in the implementation of traits in some languages).log.Logger
if theJob
struct contained another field or method calledLogger
. However, if the duplicate name is never mentioned in the program outside the type definition, it is OK. This qualification provides some protection against changes made to types embedded from outside; there is no problem if a field is added that conflicts with another field in another subtype if neither field is ever used.