from pydci import *
class CleanTable(Context):
class Waiter(StageProp):
def clean_table(self):
print("{} is cleaning the table".format(self.name))
def __init__(self, waiter):
self.Waiter = waiter
def clean(self):
self.Waiter.clean_table()
class WaitTable(Context):
stuff = 'important stuff'
class Waiter(Role):
def wait_table(self):
self.context.Apprentice.watch_and_learn()
CleanTable(waiter=self).clean()
print("{} waiting table".format(self.name))
class Apprentice(StageProp):
def watch_and_learn(self):
print("{} started watching {}".format(self.name, self.context.Waiter.name))
def __init__(self, waiter, apprentice):
self.Waiter = waiter
self.Apprentice = apprentice
def start(self, *args, **kwargs):
self.Waiter.wait_table()
class Person(object):
__slots__ = ['name']
def __init__(self, name):
self.name = name
p = Person('Bob')
apprentice = Person('Alice')
wt = WaitTable(p, apprentice)
wt.start()
--
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.
Visit this group at https://groups.google.com/group/object-composition.
For more options, visit https://groups.google.com/d/optout.
from pydci import *
from collections import namedtuple
infinity = float('inf')
Edge = namedtuple('Edge', ('start', 'dest'))
##################################
########## DATA CLASSES ##########
##################################
class Node(object):
__slots__ = ['name', '_tentative_distance']
def __init__(self, name):
self.name = name
@property
def tentative_distance(self):
return self._tentative_distance
@tentative_distance.setter
def tentative_distance(self, x):
self._tentative_distance = x
def __eq__(self, other):
return self.name == other.name
class ManhattanGeometry(object):
def east_neighbor_of(self, a):
pass
def south_neighbor_of(self, a):
pass
@property
def root(self):
return None
def destination(self):
pass
##################################
######## pydci INTERACTION #########
##################################
class CalculateShortestPath(Context):
class Map(Role):
@property
def unvisited(self):
return self.context.unvisited
@property
def origin(self):
return self.root
@unvisited.setter
def unvisited(self, unvisited):
self.context.unvisited = unvisited
def distance_between(self, a, b):
return self.distances[Edge(a, b)]
def nearest_unvisited_node_to_target(self):
minimum = infinity
selection = None
for intersection, unvisited in self.unvisited.items():
if unvisited:
if intersection.tentative_distance < minimum:
minimum = intersection.tentative_distance
selection = intersection
return selection
class CurrentIntersection(Role):
@property
def unvisited_neighbors(self):
ret = []
if self.context.Map.unvisited.get(self.context.SouthNeighbor):
ret.append(self.context.SouthNeighbor)
if self.context.Map.unvisited.get(self.context.EastNeighbor):
ret.append(self.context.EastNeighbor)
return ret
class EastNeighbor(Role):
def relable_node_as(self, x):
if x < self.tentative_distance:
self.tentative_distance = x
return "distance_was_updated"
else:
return "distance_was_not_updated"
class SouthNeighbor(Role):
def relable_node_as(self, x):
if x < self.tentative_distance:
self.tentative_distance = x
return "distance_was_updated"
else:
return "distance_was_not_updated"
def __init__(self, origin_node, target_node, geometries, path_vector=None, unvisited_dict=None, pathto_dict=None):
self.destination = target_node
self.unvisited = unvisited_dict
self.rebind(origin_node, geometries)
self.execute(path_vector, unvisited_dict, pathto_dict)
def rebind(self, origin_node, geometries):
self.CurrentIntersection = origin_node
self.Map = geometries
map(lambda n: n, geometries.nodes)
en = self.Map.east_neighbor_of(origin_node)
if en:
self.EastNeighbor = en
sn = self.Map.south_neighbor_of(origin_node)
if sn:
self.SouthNeighbor = sn
def execute(self, path_vector, unvisited_dict, pathto_dict):
self.do_inits(path_vector, unvisited_dict, pathto_dict)
unvisited_neighbours = self.CurrentIntersection.unvisited_neighbors
for neighbor in unvisited_neighbours:
curr_dist = self.CurrentIntersection.tentative_distance
dist_btw = self.Map.distance_between(self.CurrentIntersection, neighbor)
net_distance = curr_dist + dist_btw
if neighbor.relable_node_as(net_distance) == 'distance_was_updated':
self.pathTo[neighbor] = self.CurrentIntersection
self.unvisited.pop(self.CurrentIntersection)
if len(self.Map.unvisited) == 0:
self.save_path(self.path)
else:
selection = self.Map.nearest_unvisited_node_to_target()
CalculateShortestPath(selection, self.destination, self.Map, self.path, self.unvisited, self.pathTo)
def do_inits(self, path_vector, unvisited_dict, pathto_dict):
if path_vector is None:
self.unvisited = dict()
for n in self.Map.nodes:
self.unvisited[n] = True
n.tentative_distance = infinity
self.Map.origin.tentative_distance = 0
self.path = []
self.pathTo = dict()
else:
self.unvisited = unvisited_dict
self.path = path_vector
self.pathTo = pathto_dict
def save_path(self, path_vector):
node = self.destination
while node != None:
path_vector.append(node)
node = self.pathTo.get(node)
def each(self):
for node in self.path:
yield node
##################################
############## TEST ##############
##################################
class Geometry1(ManhattanGeometry):
def __init__(self):
self.distances = dict()
names = ["a", "b", "c", "d", "a", "b", "g", "h", "i"]
self.nodes = [Node(name) for name in names]
self.a = self.nodes[0]
self.b = self.nodes[1]
self.c = self.nodes[2]
self.d = self.nodes[3]
self.e = self.nodes[4]
self.f = self.nodes[5]
self.g = self.nodes[6]
self.h = self.nodes[7]
self.i = self.nodes[8]
for i in range(9):
for j in range(9):
self.distances[Edge(self.nodes[i], self.nodes[j])] = infinity
self.distances[Edge(self.a, self.b)] = 2
self.distances[Edge(self.b, self.c)] = 3
self.distances[Edge(self.c, self.f)] = 1
self.distances[Edge(self.f, self.i)] = 4
self.distances[Edge(self.b, self.e)] = 2
self.distances[Edge(self.e, self.f)] = 1
self.distances[Edge(self.a, self.d)] = 1
self.distances[Edge(self.d, self.g)] = 2
self.distances[Edge(self.g, self.h)] = 1
self.distances[Edge(self.h, self.i)] = 2
self.distances[Edge(self.d, self.e)] = 1
self.next_down_the_street_from = dict()
self.next_down_the_street_from[self.a] = self.b
self.next_down_the_street_from[self.b] = self.c
self.next_down_the_street_from[self.d] = self.e
self.next_down_the_street_from[self.e] = self.f
self.next_down_the_street_from[self.g] = self.h
self.next_down_the_street_from[self.h] = self.i
self.next_along_the_avenue_from = dict()
self.next_along_the_avenue_from[self.a] = self.d
self.next_along_the_avenue_from[self.b] = self.e
self.next_along_the_avenue_from[self.c] = self.f
self.next_along_the_avenue_from[self.d] = self.g
self.next_along_the_avenue_from[self.f] = self.i
def east_neighbor_of(self, a):
return self.next_down_the_street_from.get(a, None)
def south_neighbor_of(self, a):
return self.next_along_the_avenue_from.get(a, None)
@property
def root(self):
return self.a
@property
def destination(self):
return self.i
if __name__ == '__main__':
geometry = Geometry1()
path = CalculateShortestPath(geometry.root, geometry.destination, geometry)
for n in path.each():
print(n.name)
from pydci import *
class Interview(Context):
class Interviewee(Role):
def say(self):
print("[{}] {} Hello!".format(self.name, self.comms))
class Interviewer(Role):
def interview(self):
print("[Interviewer] Hello!")
self.context.Interviewee.say()
def __init__(self, interviewer, interviewee):
self.Interviewer = interviewer
self.Interviewee = interviewee
def start(self):
self.Interviewer.interview()
class Battle(Context):
class Bear(Role):
def say(self):
print("{} [{}] Grrrrr.....".format(self.context.GameId.id, self.name))
def fight(self):
self.say()
self.context.Lion.fight()
class Lion(Role):
def say(self):
print("{} [{}] Meow.....".format(self.context.GameId.id, self.name))
def fight(self):
self.say()
class Game(StageProp):
@property
def id(self):
return self.gid
def __init__(self, id, player1, player2):
self.GameId = id
self.Bear = player1
self.Lion = player2
def start(self):
print("Started battle of id {}".format(self.GameId.id))
self.Bear.fight()
class Player(object):
def __init__(self, name, comms):
self.name = name
self.comms = comms
class Game(object):
def __init__(self, gid):
self.gid = gid
player = Player(name='Jack', comms='says')
cpu = Player(name='Cyborg', comms='beleeps')
b1 = Battle(Game(1), player, cpu)
b2 = Battle(Game(2), cpu, player)
b3 = Battle(Game(3), cpu, cpu)
b1.start()
b2.start()
b3.start()
Interview(Player('Interviewer', 'says'), player).start()
Interview(Player('Interviewer', 'says'), cpu).start()
from pydci import *
class Interview(Context):
class Interviewee(Role):
def say(self):
print("[{}] {} Hello!".format(self.name, self.comms))
class Interviewer(Role):
def interview(self):
print("[Interviewer] Hello!")
self.context.Interviewee.say()
def __init__(self, interviewer, interviewee):
self.Interviewer = interviewer
self.Interviewee = interviewee
def start(self):
self.Interviewer.interview()
class Battle(Context):
class Bear(Role):
def say(self):
print("{} [{}] Grrrrr.....".format(self.context.Game.id, self.name))
def fight(self):
self.say()
self.context.Lion.fight()
class Lion(Role):
def say(self):
print("{} [{}] Meow.....".format(self.context.Game.id, self.name))
def fight(self):
self.say()
class Game(StageProp):
@property
def id(self):
return self.gid
def __init__(self, id, player1, player2):
self.Game = id
self.Bear = player1
self.Lion = player2
def start(self):
print("Started battle of id {}".format(self.Game.id))
--
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/QuEuuxGy330/unsubscribe.
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.
--
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.
Den 12. nov. 2017 kl. 21.37 skrev Vlad Lyga <lyv...@gmail.com>:And the Python example in the book is flawed in exactly the same way.
Egon — nice!Curious: Do you have a trygve version of this already?
--
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.
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/QuEuuxGy330/unsubscribe.
To unsubscribe from this group and all its topics, send an email to object-composit...@googlegroups.com.
Hi Vlad,
I realized that one of your messages went to me privately. I don't
know if that was intentional, and if so it's a good impulse to
avoid sending too many messages to the group about things that
have been discussed before, but for our current discussion I think
it's helpful to CC the group; that way Cope and others can correct
anything I don't explain quite correctly or add other helpful
comments. So I am replying here publicly with another thought and
also including our previous exchange below. I also think your most
recent email contains some questions that would be good questions
for the group (I have a fairly strong opinion on most of it but
would be curious to hear what others have to say)...but I'll leave
it up to you whether to post it publicly.
---
(Addendum to the top message below, 1:58pm):
BTW, proper handling of asynchronous functions is a pretty high
bar for a DCI implementation that doesn't use source
transformation. If I'm not mistaken, neither the Smalltalk nor the
C++ implementation would handle them correctly since those
implementations rely on a Context stack, and I don't see how you
would ensure that the Context stack gets updated properly in the
case of asynchronous role methods running on a separate thread (at
least not without modifying the compiler). So I wouldn't worry
about it too much if this proves problematic for Python. Just
something worth investigating and then if you find any issues, you
can evaluate the tradeoffs and complexity of fixing them.
On 11/14/17 3:28 AM, Vladi Lyga wrote:I realized that my example didn't show everything I had intended...changes shown below in bold:
What about calling an asynchronous method in another ContextGood question. Would you care giving me an example , i would like to make sure i understand what you are talking about?
Yes I am sure, since this has always been the position of Cope and Trygve and the general consensus of the group. And the reasoning makes sense: roles are inherently contextual. Roles might be similar across contexts, but not exactly the same or it would be the same use case (this can take some time to grok...I imagine you're already familiar with this concept but if you like I can point you to some past discussions on this). I can't claim that Cope and Trygve would agree with everything I wrote below, but the basic premise of roles being completely private to Contexts is very well-established.If they understand DCI, then they should expect the external function to get the data object.You sure about that? After all , i all programming languages - you get on the other side what you pass. So if you pass a Role object - it can be very confusing to get ‘something else’ on the other side. My thinking is that if after ejecting the Context use case you want to continue doing something with the data object , then you call the other code *after* the Context finishes executing.
Can it be that we are slightly mixing DCI as a paradigm, with DCI as a language?
You are right, and I agree that in DCI environment role players should be completely contextualised. But my effort is for bringing the DCI *paradigm* to python.
I am not feeling comfortable going against Python semantics, or trying to create a DCI dsl in Python.
Just trying to think about the effects of Python code that behaved differently in different parts of the same program. Seems to me that can do a lot more harm to readability.
So looking on this through Python, not DCI, developers eyes. When he calls external 3d party function and pass them the role player, it is expected that this role players methods will be available.
The bigger challenge in my mind to harvesting DCI power will be in education.
That is the reason I want to try and educate for DCI through development of a library of examples.
In my view, a developer is better to *understand* that important 3d party function that is ought to be called from the context to complete a use case, is probably part of the use case itself, and therefore should be included in the use case as part of a role, or given a role in that context.
Your feedback triggers me to think deeper and broader.
Thank you.
A final point (sorry for the separate messages; I just thought of
this): by introducing DCI, you're already venturing into uncharted
territory for most Python programmers. So I don't think you can be
sure about their expectations about role methods without actually
doing a study on it. Ordinary Python objects don't have transient
methods that are added and removed depending on the context, so
maybe the average Python programmer would be just as surprised by
role methods being removed after the execution of a Context (or
inside a nested Context). But surprises aren't necessarily bad; in
this case they would simply be part of the process of learning
DCI.
--
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.
Den 21. nov. 2017 kl. 18.59 skrev Matthew Browne <mbro...@gmail.com>:Yes, there is definitely a balance to be struck. But my impression of what I've seen of PyDCI is that it's already at least as "pure" as some of the existing implementations on fulloo.info, e.g. the Ruby examples. And I don't think any of the remaining issues it still has that we've been discussing are show-stoppers. Of course we haven't seen the full source code yet, and perhaps someone will point out some other issue that really should be addressed. But that's my assessment based on what I've seen so far.