Thanks to everyone for the illuminating discussion. It looks like it
may keep going on for a while... I know know the answers to all my questions, and a little more besides. My original problem was partially UI-related: I have a (mathematical) function which is just the composition of three simpler functions each chosen from a (math.) set. My idea was to have a method with three parameters for the composed function, something like: MyDataObject>>computeResultWith: aFunctionSelectorFromSet1 and: aFunctionSelectorFromSet2 and: aFunctionSelectorFromSet3 With this setup I could manipulate the call to the composed function both programmatically and from the GUI (i.e. by having menus populated automatically from the respective sets). It seems the perform: aString asSymbol solution is much simpler, but I understand well (and much better after the discussion) the potential maintenance problems, especially in debugging and refactoring. Cheers, Stefano __________________________________________________ Stefano Franchi Department of Philosophy Ph: (64) 9 373-7599 x83940 University Of Auckland Fax: (64) 9 373-8768 Private Bag 92019 [hidden email] Auckland New Zealand |
In reply to this post by Georg Heeg
On 19/03/07, Georg Heeg <[hidden email]> wrote:
> Bruce, > > The most predominant examples are in the UI. Most actions in menu > specifications are symbols as well as aspects. They all are sent by perform: Well, yes. But are these illustrations of Best Practice that you would hold up to someone as an example to follow? FWIW, I would not. Never say never, though. I'm sure there must be an example of the use of perform which is obviously the best way of solving a particular problem. I've just never seen one. All the best, Bruce -- Make the most of your skills - with OpenSkills http://www.openskills.org/ |
In reply to this post by stefano-franchi
I hate to sound like a broken record, but for me, appropriate is being able to search/refactor code, and symbols being literals helps neither.. |
In reply to this post by Bruce Badger
Bruce:
I may be catching this mid-stream but not sure about "more" appropriate but obviously in situations where the invocation is deferred and simple e.g. no arguments there are a few examples such as AspectAdaptors , DependencyTransformers etc, there are some recent extensions that make certain usages of symbols and blocks effectively interchangeable, e.g. collection collect: #money which amounts to : collection collect:[:ea | ea money]. Basically symbols have been made "evaluable" so when you bring up "appropriate" what do you mean ? On Mon, 19 Mar 2007 10:33:37 -0500, Bruce Badger <[hidden email]> wrote: > On 19/03/07, Travis Griggs <[hidden email]> wrote: >> Both techniques have their place. With much cross over. They are both >> used >> _extensively_ throughout the system itself, so I think that neither can >> be >> all that bad. > > Really? Can you point to an example in the base VisualWorks system > where, in your view, it was more appropriate to use a perform than use > a block? > > All the best, > Bruce -- Charles A. Monteiro http://wiki.nycsmalltalk.org http://www.monteirosfusion.com http://monteirofusion.blogspot.com |
On 19/03/07, Charles A. Monteiro <[hidden email]> wrote:
> Bruce: > > I may be catching this mid-stream but not sure about "more" appropriate > but obviously in situations where the invocation is deferred and simple > e.g. no arguments there are a few examples such as AspectAdaptors , > DependencyTransformers etc, > > there are some recent extensions that make certain usages of symbols and > blocks effectively interchangeable, e.g. > > collection collect: #money > > which amounts to : > > collection collect:[:ea | ea money]. > > Basically symbols have been made "evaluable" > > so when you bring up "appropriate" what do you mean ? I mean the kind of thing where you would say: Look, see how perform is used here? This is a good example of where perform should be used. I would not say that "collection collect: #money" was such an example. In this case what would happen if I refactored things and wanted to rename >>money to >>instrument (which might be more generic in some cases)? What if I just want to know the impact of possibly changing >>money? Of course one can still do these things even with a perform being used, it's just harder. Sorry chaps. I'll sut up now :-/ -- Make the most of your skills - with OpenSkills http://www.openskills.org/ |
In reply to this post by Boris Popov, DeepCove Labs (SNN)
So Boris, do you mean that you want for one, to be able to do "senders of
selector" for selector #foo and if you have code such as: MyClass>>>myMethod "for illustration purposes only" self blah. self moreBlahBlah. self doThis: #foo. you ought to be able to find the method "myMethod" ? Well I can and I believe is because our friend Terry a zillion years ago game me some code for it. Or does the stock image already do this? Don't know, I have had this thing in my image for ever. BTW, didn't you write that regex addon to RB? can that beast help with the above? Or would it be necessary that the rewrite editor can handle this? I actually don't know if it can. BTW , is a broken record worse or better than a CD with a glitch? I don't know. -Charles On Mon, 19 Mar 2007 11:05:10 -0500, Boris Popov <[hidden email]> wrote: > I hate to sound like a broken record, but for me, appropriate is being > able to search/refactor code, and symbols being literals helps neither.. > > Cheers! > > -Boris > (Sent from a BlackBerry) > > ----- Original Message ----- > From: Charles A. Monteiro <[hidden email]> > To: Bruce Badger <[hidden email]>; Travis Griggs <[hidden email]> > Cc: vwnc <[hidden email]> > Sent: Mon Mar 19 10:06:49 2007 > Subject: Re: Selectors as variables? > > Bruce: > > I may be catching this mid-stream but not sure about "more" appropriate > but obviously in situations where the invocation is deferred and simple > e.g. no arguments there are a few examples such as AspectAdaptors , > DependencyTransformers etc, > > there are some recent extensions that make certain usages of symbols and > blocks effectively interchangeable, e.g. > > collection collect: #money > > which amounts to : > > collection collect:[:ea | ea money]. > > Basically symbols have been made "evaluable" > > so when you bring up "appropriate" what do you mean ? > > On Mon, 19 Mar 2007 10:33:37 -0500, Bruce Badger <[hidden email]> > wrote: > >> On 19/03/07, Travis Griggs <[hidden email]> wrote: >>> Both techniques have their place. With much cross over. They are both >>> used >>> _extensively_ throughout the system itself, so I think that neither can >>> be >>> all that bad. >> >> Really? Can you point to an example in the base VisualWorks system >> where, in your view, it was more appropriate to use a perform than use >> a block? >> >> All the best, >> Bruce > > > -- Charles A. Monteiro http://wiki.nycsmalltalk.org http://www.monteirosfusion.com http://monteirofusion.blogspot.com |
In reply to this post by Bruce Badger
On Mar 19, 2007, at 9:10, Bruce Badger wrote:
Not sure I understand correctly. So forgive if I'm off base here. Define a class called Foo. Define a method 'money ^42'. Define another method 'performMoney ^self perform: #money'. Now rename 'money' to be 'currency'. You'll find that the performMoney has been rewritten. As it should be. Frankly, the RB rewrite engine would be FAR less useful than it is if it couldn't do that. Ponder just how many "specifications of selector to send" there are literal array specs. If the rewrite scope did not include those, you could never rename any method that was specified in a spec, because it might be broken later. -- Travis Griggs Objologist "HTTP. It's like a bike pretending to be a bus, a bulldozer, and a cup of coffee at the same time." - Martin Kobetic |
In reply to this post by Charles A. Monteiro-2
On Mar 19, 2007, at 10:45, Charles A. Monteiro wrote:
The stock image already does this. Since I don't know when. A zillion and one years ago I think. :) "senders of selector" is a great example of "it's ok to cheat if you don't get caught." Because cheat it does. It simply searches for methods with #foo in their frame. As well as scanning the bytecodes for "embedded" selectors. IOW, it's really just "references to symbol." Has been for a long time. -- Travis Griggs Objologist "You A students, you'll be back soon teaching here with me. You B students, you'll actually go on to be real engineers. You C students, you'll go into management and tell the A and B students what to do." - My Fluid Dynamics Professor whom I have yet to disprove |
In reply to this post by Travis Griggs-3
Ah, but symbol refactorings are half-baked simply because that's all they are, literal-based searches.
Foo>>one
self bar Foo>>two
self perform: #bar
Foo>>three
#(#bar) do: [:ea | self perform: ea]
Foo>>four #bar << #bar >> 'Bar'
Foo>>five
(Array with: [:v | v bar]) do: [:ea | ea value: self]
Foo>>six
#('bar') do: [:ea | self perform: ea asSymbol]
Now lets try renaming #bar to #foo,
one (okay)
self foo two (okay)
self perform: #foo three (okay)
#(#foo) do: [:ea | self perform: ea]
four (not okay)
#foo << #foo >> 'Bar'
five (okay)
(Array with: [:v | v foo]) do: [:ea | ea value: self]
six (nowhere to be found)
-Boris -- +1.604.689.0322 DeepCove Labs Ltd. 4th floor 595 Howe Street Vancouver, Canada V6C 2T5 [hidden email] CONFIDENTIALITY NOTICE This email is intended only for the persons named in the message header. Unless otherwise indicated, it contains information that is private and confidential. If you have received it in error, please notify the sender and delete the entire message including any attachments. Thank you. From: Travis Griggs [mailto:[hidden email]] Sent: Mon 19/03/2007 9:50 AM To: vwnc Subject: Re: Selectors as variables? On Mar 19, 2007, at 9:10, Bruce Badger wrote:
Not sure I understand correctly. So forgive if I'm off base here.
Define a class called Foo. Define a method 'money ^42'. Define another method 'performMoney ^self perform: #money'. Now rename 'money' to be 'currency'. You'll find that the performMoney has been rewritten. As it should be. Frankly, the RB rewrite engine would be FAR less useful than it is if it couldn't do that. Ponder just how many "specifications of selector to send" there are literal array specs. If the rewrite scope did not include those, you could never rename any method that was specified in a spec, because it might be broken later. --
Travis Griggs
Objologist
"HTTP. It's like a bike pretending to be a bus, a bulldozer, and a cup of coffee at the same time." - Martin Kobetic |
In reply to this post by Travis Griggs-3
"references to symbol" ~~ "message sends" :)
/me hides under the bed
-Boris -- +1.604.689.0322 DeepCove Labs Ltd. 4th floor 595 Howe Street Vancouver, Canada V6C 2T5 [hidden email] CONFIDENTIALITY NOTICE This email is intended only for the persons named in the message header. Unless otherwise indicated, it contains information that is private and confidential. If you have received it in error, please notify the sender and delete the entire message including any attachments. Thank you. From: Travis Griggs [mailto:[hidden email]] Sent: Mon 19/03/2007 9:55 AM To: VW NC Subject: Re: Selectors as variables? On Mar 19, 2007, at 10:45, Charles A. Monteiro wrote:
The stock image already does this. Since I don't know when. A zillion and one years ago I think. :)
"senders of selector" is a great example of "it's ok to cheat if you don't get caught." Because cheat it does. It simply searches for methods with #foo in their frame. As well as scanning the bytecodes for "embedded" selectors. IOW, it's really just "references to symbol." Has been for a long time. --
Travis Griggs
Objologist
"You A students, you'll be back soon teaching here with me. You B students, you'll actually go on to be real engineers. You C students, you'll go into management and tell the A and B students what to do." - My Fluid Dynamics Professor whom I have yet to disprove |
OK, I didn't know it was there either -- I only ever looked in
"Visualworks | Browse" -- should be there too!
Boris Popov wrote:
-- Dennis Smith +1 416.798.7948 Cherniak Software Development Corporation Fax: +1 416.798.0948 509-2001 Sheppard Avenue East [hidden email] Toronto, ON M2J 4Z8 <a class="moz-txt-link-freetext" href="sip:dennis@CherniakSoftware.com">sip:dennis@... Canada http://www.CherniakSoftware.com Entrance off Yorkland Blvd south of Sheppard Ave east of the DVP |
In reply to this post by Bruce Badger
On Mar 19, 2007, at 9:04, Bruce Badger wrote:
There is no "best practice" to follow here IMO. Obvioulsy, UIs use it heavily. Browse senders of perform: yourself to see lots more. perform: #someSymbol is used by the change engine. In Trippy. Lots in the RB. To dispatch the correct action in ParagraphEditor. TestCases use it. CharacterScanners. All those icons that show up all over the image. The list goes on and on and on and on. Don't get me wrong, if my reputation for going nuts with blocks doesn't precede me in this discussion, then we're definitely in different universes. But I simply can find no validation of a semi-religious belief that "perform: #aSymbol" is to be viewed as taboo. I would also argue that indeed for a beginner, perform: #aSymbol, is much easier to grok than blocks. The reason is that it has direct analogs with other programming environments. It's _just_ a function pointer as it were. You can pass them around. And invoke them. Many/most languages have the ability to invoke functions from the indirect specification of a variable. The whole "callback" metaphor in just about every language but Smalltalk is based on this whole notion. -- Travis Griggs Objologist "There are a thousand hacking at the branches of evil to one who is striking at the root" - Henry David Thoreau |
In reply to this post by Travis Griggs-3
On 19/03/07, Travis Griggs <[hidden email]> wrote:
> Define a class called Foo. Define a method 'money ^42'. Define another > method 'performMoney ^self perform: #money'. Now rename 'money' to be > 'currency'. You'll find that the performMoney has been rewritten. Thanks, Travis (and Charles). I didn't realise the RB would do that! ... I'm not sure I feel very good that it does. It strikes me that there could be unwanted side effects, for example if a symbol is coincidentally the same as the selector of a method that gets renamed. I would prefer it if the RB recognised the distinction that Boris made, viz symbol != message selector. I would go along with you and avoid religiously applying a rule that banned >>perform even if I could (never say never, and all that), but I think it's OK to talk about best practice. There are benefits from having a view of what is best practice and constantly trying to trend towards that. Of course, one should always be open to Even Better practice too! I certainly don't buy the argument that perform must be OK because it is used in the base VW image, though. Having said that, if you know of a place in the base image that is an illustration of where >>perform is the Right Thing To Do, then I'd love to see it. Really. I'm also not so convinced that encouraging people to pick up bad habits (because it's a bit easier for now) is a good thing. Blocks are not that hard to grok once you have played with them for a bit. Lastly Travis, my apologies if I have portrayed you as anything other than a big fan of blocks :-) All the best, Bruce -- Make the most of your skills - with OpenSkills http://www.openskills.org/ |
In reply to this post by Alexander Ivanov-2
Finally catching up on my email, so excuse me coming in late.
> How do you implement this: > > Foo>>doSeveralActions: actions > actions do: [ :selector | anObject perform: selector] > > Foo new doSeveralActions: #(read write save print) How about: Foo>>actionObject ^anObject Bar>>actionsWith: aFoo (aFoo actionObject) doThis; doThat; doTheOther; etc. Admittedly, this is a contrived example. But then, so was yours. :) Dave Alexander Ivanov wrote: > It is hard to say what would be more confusing for newbies, blocks or > 'perform'. From my point of view both are not quite easy for > non-smalltalkers: > > [:v | v inspect] value: anObject > or > anObject perform: #inspect > Yes, perform is bad in debugging but blocks are less convenient if we go > a little bit further. > > How do you implement this: > > Foo>>doSeveralActions: actions > actions do: [ :selector | anObject perform: selector] > > Foo new doSeveralActions: #(read write save print) > > Definition is OK: > > Foo>>doSeveralActions: actions > actions do: [ :block | block value: anObject ]. > > But what about call? > > blocks := OrderedCollection new > add: [ :v | v read ]; > add: [ :v | v write ]; > add: [ :v | v save ]; > add: [ :v | v print]; > yourself. > Foo new doSeveralActions: blocks. > > If somebody find something less ugly, I will switch to blocks too. > > Regards, > > Alex > > Alex Ivanov > [hidden email] > 778 898-2527 > > > > > > > -----Original Message----- > *From:* Steve Aldred [mailto:[hidden email]] > *Sent:* Sunday, March 18, 2007 8:22 PM > *To:* vwnc > *Subject:* Re: Selectors as variables? > > Alexander Ivanov wrote: >> >> OtherFoo>>beforeDoSomething >> | block otherObject | >> otherObject := 5. >> block := [ :v | otherObject inspect ]. >> Foo new doSomething: block >> >> Then >> >> Foo>>doSomething: aBlock >> aBlock value: myInstVar >> >> inspects 5 instead of myInstVar because aBlock was compiled in >> other context. >> That is what I meant. Sorry, I gave a hypothetical example, did >> not have time to invent a useful one. > If you want the block to do things with the argument, which is the > whole point of the exercise, then you write block code that does > just that. For the above the block should then be: > > [:v | v inspect] > > then there is no confusion over what is being acted on. Of course > any self references in the block refer to where the block was > compiled not where it is executed - which does confuse newbies. > > cheers > Steve A >> >> Regards, >> >> Alex >> >> >> >> -----Original Message----- >> *From:* Boris Popov [mailto:[hidden email]] >> *Sent:* Sunday, March 18, 2007 11:30 AM >> *To:* Alexander Ivanov; [hidden email]; >> [hidden email] >> *Cc:* [hidden email] >> *Subject:* RE: Selectors as variables? >> >> Not sure what you mean by context here, could you elaborate? >> >> Foo>>doSomething: aBlock >> aBlock value: myInstVar >> >> aFoo doSomething: [:v | v inspect] >> >> versus, >> >> Foo>>doSomething: aSelector >> myInstVar perform: aSelector >> >> aFoo doSomething: #inspect >> >> Using perform's leads to brittle code as they are not subject >> to refactorings (method renames, rewrites and such), runtime >> packager has no way to trace them, code highligter doesn't >> know what they are etc. >> >> Cheers! >> >> -Boris >> >> -- >> +1.604.689.0322 >> DeepCove Labs Ltd. >> 4th floor 595 Howe Street >> Vancouver, Canada V6C 2T5 >> >> [hidden email] >> >> CONFIDENTIALITY NOTICE >> >> This email is intended only for the persons named in the message >> header. Unless otherwise indicated, it contains information >> that is >> private and confidential. If you have received it in error, please >> notify the sender and delete the entire message including any >> attachments. >> >> Thank you. >> >> ------------------------------------------------------------------------ >> *From:* Alexander Ivanov [mailto:[hidden email]] >> *Sent:* Sun 18/03/2007 11:19 AM >> *To:* Boris Popov; [hidden email]; [hidden email] >> *Cc:* [hidden email] >> *Subject:* RE: Selectors as variables? >> >> Passing a block is more powerful but might be more complicated >> because 'fooClass' would never know in which context a block >> is going to be performed. In 'perform' case it is always clear >> that selector will be performed in 'anObject' context. >> >> For beginners I would recommend to start exploring 'perform' >> case and if it is not enough, try to use blocks. >> >> Regards, >> >> Alex >> >> >> >> -----Original Message----- >> *From:* Boris Popov [mailto:[hidden email]] >> *Sent:* Sunday, March 18, 2007 10:34 AM >> *To:* [hidden email]; [hidden email] >> *Cc:* [hidden email] >> *Subject:* Re: Selectors as variables? >> >> Technically, yes, but practically one is much better off >> passing a block instead. >> >> Cheers! >> >> -Boris >> (Sent from a BlackBerry) >> >> ----- Original Message ----- >> From: [hidden email] <[hidden email]> >> To: Stefano Franchi <[hidden email]> >> Cc: vwnc <[hidden email]>; Stefano Franchi >> <[hidden email]> >> Sent: Sun Mar 18 10:27:51 2007 >> Subject: Re: Selectors as variables? >> >> Stefano >> >> >fooClass>>doSomething: withThisMethod >> >anObject withThisMethod >> >> If you pass in the selector, then: >> >> doSomething: aSymbol >> anObject perform: aSymbol >> >> And So It Goes >> Sames >> ______________________________________________________________________ >> >> Samuel S. Shuster [|] >> VisualWorks Engineering, GUI Project >> Smalltalk Enables Success -- What Are YOU Using? >> > |
In reply to this post by Bruce Badger
> I mean the kind of thing where you would say: Look, see how perform
is used here? This is a good example of where perform should be used. Umm, maybe MessageSend>>value ? value "Evaluate the message send." ^receiver perform: selector withArguments: args Dave Bruce Badger wrote: > On 19/03/07, Charles A. Monteiro <[hidden email]> wrote: >> Bruce: >> >> I may be catching this mid-stream but not sure about "more" appropriate >> but obviously in situations where the invocation is deferred and simple >> e.g. no arguments there are a few examples such as AspectAdaptors , >> DependencyTransformers etc, >> >> there are some recent extensions that make certain usages of symbols and >> blocks effectively interchangeable, e.g. >> >> collection collect: #money >> >> which amounts to : >> >> collection collect:[:ea | ea money]. >> >> Basically symbols have been made "evaluable" >> >> so when you bring up "appropriate" what do you mean ? > > I mean the kind of thing where you would say: Look, see how perform is > used here? This is a good example of where perform should be used. > > I would not say that "collection collect: #money" was such an example. > In this case what would happen if I refactored things and wanted to > rename >>money to >>instrument (which might be more generic in some > cases)? What if I just want to know the impact of possibly changing >>> money? Of course one can still do these things even with a perform > being used, it's just harder. > > Sorry chaps. I'll sut up now :-/ > |
On 20/03/07, Dave Stevenson <[hidden email]> wrote:
> > I mean the kind of thing where you would say: Look, see how perform > is used here? This is a good example of where perform should be used. > > Umm, maybe MessageSend>>value ? > > value > "Evaluate the message send." > > ^receiver perform: selector withArguments: args Heh, well, this seems to be the mechanism used in loads of places so people can say "me? nah, I don't use >>perform" :-) Looking at the references to MessageSend it seemed to me that blocks could do the job just as well if not better in the situations where MessageSend is used (not that I am suggesting a wholesale change!). Indeed MessageSend seems to me to be a class created to try and mitigate the problems with passing symbols around to be later used as selectors. So rather that being an example of a good use of >>perform, it seems to be an example of why one should consider using blocks instead IMO. The one situation where I can see that >>perform *may* be handy is when bringing in strings from outside the image which you want to use as selectors, though one might then be open to dodgy code being injected (much like the SQL injection vulnerabilities in some systems). So even in this case I'd be more inclined to define an acceptable vocabulary and look up an action in a dictionary whose keys are the "selectors" and whose values are blocks and then I'd say value to the block. All the best, Bruce -- Make the most of your skills - with OpenSkills http://www.openskills.org/ |
Bruce,
How would you do SUnit without perform:? -- Travis Griggs Objologist "You A students, you'll be back soon teaching here with me. You B students, you'll actually go on to be real engineers. You C students, you'll go into management and tell the A and B students what to do." - My Fluid Dynamics Professor whom I have yet to disprove |
Travis,
There's a big difference between perform'ing selectors that are effectively extracted from the runtime and perform'ing selectors that are coded as literals in the source. Okay: (self selectorsMatching: 'test*') do: [:ea | self perform: ea] Not so much: #(testOne testTwo testThree) do: [:ea | self perform: ea] That's the pattern I was campaigning against, not all uses of perform, Cheers! -Boris -- +1.604.689.0322 DeepCove Labs Ltd. 4th floor 595 Howe Street Vancouver, Canada V6C 2T5 http://tinyurl.com/r7uw4 [hidden email] CONFIDENTIALITY NOTICE This email is intended only for the persons named in the message header. Unless otherwise indicated, it contains information that is private and confidential. If you have received it in error, please notify the sender and delete the entire message including any attachments. Thank you. > -----Original Message----- > From: Travis Griggs [mailto:[hidden email]] > Sent: Tuesday, March 20, 2007 12:51 PM > To: vwnc > Subject: Re: Selectors as variables? > > Bruce, > > How would you do SUnit without perform:? > > -- > Travis Griggs > Objologist > "You A students, you'll be back soon teaching here with me. You B > students, you'll actually go on to be real engineers. You C students, > you'll go into management and tell the A and B students what to do." - > Fluid Dynamics Professor whom I have yet to disprove > |
In reply to this post by Travis Griggs-3
On 20/03/07, Travis Griggs <[hidden email]> wrote:
> How would you do SUnit without perform:? Good point! Thank goodness I never say never! :-) Travis, I do I like Boris's latest response to you and agree with the distinction he makes. I think his thoughts on this are more refined than mine. I shall steal them forthwith. Perhaps we can give this topic a more extensive flogging at StS07? All the best, Bruce -- Make the most of your skills - with OpenSkills http://www.openskills.org/ |
In reply to this post by Bruce Badger
We make extensive use of perform in our business application framework.
Simple example -- for large lists of things (e.g. employees) we maintain some different sort orders. They have to be maintained when udpates are made for performance reasons. We define a sort order using a "Key" object which lists the attributes (which have read accessors) which are used to define the order. For example, "surname, firstName, sin". We then, in the framework, use "perform" to get the values to sort on. In a different case, we can have named groups of attributes so the developer can say "give me an Array of the employee name" which might be defined as title, firstName, surname, initial The attribute "set" would be called name, and would contain the 4 selector names and asking for "name" would have to use perform to get the array of values. Could we create a method "name" and have it build the set, yes but we want to build it at runtime, not by hard-coding. There are numerous other examples where perform is used in the framework because the names of attributes are in the data. -- Dennis Smith +1 416.798.7948 Cherniak Software Development Corporation Fax: +1 416.798.0948 509-2001 Sheppard Avenue East [hidden email] Toronto, ON M2J 4Z8 sip:[hidden email] Canada http://www.CherniakSoftware.com Entrance off Yorkland Blvd south of Sheppard Ave east of the DVP |
Free forum by Nabble | Edit this page |