![]()
Related Topics
![]()
cos ...@springmail.com (Costas Menico)
I would like a suggestion on instantiating unknown cl***es at runtime.
I need to intantiate a cl*** whose name is in a symbol variable.
For example normally if I can have a cl*** Date I would send it a new message.
myApp := Date perform: #newDay:year: with: 12 with: 2000 However I want to do the same thing based on a cl*** name that is a variable. So I tried the following which works: myAppCl*** := #Date.
mySelector:= #newDay:year:.
myAppObject := (Smalltalk at: myAppCl*** ) perform: mySelector with: 5 with: 2000.
Any other more elegant ways?
Costas
"Peter van Rooijen" pe...@vanrooijen.com
Apart from not relying on the global Smalltalk holding all the cl***es, I don't see what could be more elegant.
So, I'd just suggest using myAppCl*** asCl*** instead of Smalltalk at:.
Regards, Peter van Rooijen
"Andy Bower" bo...@object-arts.com
Costas, I'm not sure whether your need is to have the method selector in a variable or not. If not, then you can also do: (Smalltalk at: myAppCl***) newDay:5 year: 2000 Best regards, Andy Bower Dolphin Support http://www.object-arts.com
---
Visit the Dolphin Smalltalk Wiki Web http://www.object-arts.com/wiki/html/Dolphin/FrontPage.htm
---
"Andy Bower" bo...@object-arts.com
Peter, Dolphin, doesn't have #asCl*** (must be some other popular Smalltalk you are thinking of).
In general, we frown upon the us of #asXXXX selectors and tend to follow the recommendations in Beck's Smalltalk Best Practise Patterns.(which has mysteriously disappeared from the bookshelf so I can't quote you the page numbers at the moment). There was a discussion about this in the old newsgroup some time ago so it'll probably be available in the archives.
What we usually do to avoid hard coding a reference to the Smalltalk global (and to prepare the road for namespaces in future) is to use #environment to look it up. i.e.
(self environment at: myAppCl***) newDay:5 year: 2000 You'll see this is used quite a few times in the base image.
Best regards, Andy Bower Dolphin Support http://www.object-arts.com
---
Visit the Dolphin Smalltalk Wiki Web http://www.object-arts.com/wiki/html/Dolphin/FrontPage.htm
---
"Ted Bracht" ted.bra...@coda.com
Costas, Alternatively, create a method, say #myAppCl***, which returns the real cl*** name.
MyApp>>myAppCl*** ^Date MyApp>>mySelector ^#newDay:year: then the method to create the date would look like: (self myAppCl***) perform: (self mySelector) withAll: #( 5 2000 ) Now you can have different subcl***es, all returning a different cl*** and a different selector. The generic code in the supercl*** stays the same.
HTH Ted
cos ...@springmail.com (Costas Menico)
First thing I tried... but as you can see from Andy's response I have to consult my patterns too <g>. Costas
cos ...@springmail.com (Costas Menico)
what version of Dolphin should we expect namespaces in?
Costas
"Peter van Rooijen" pe...@vanrooijen.com
...
Ho! Not so fast!
1. Because Kent says so, doesn't make it right.
2. Kent doesn't say so. What he says (p. 28-29) is that he doesn't want protocol explosion, which he sees as a danger because there is no theoretical limit to the number of conversion methods you could add. This in *not* a case where you are in danger of a conversion protocol explosion.
3. In fact, Kent explicitly mentions (p. 29) that he would be happy to use a conversion method where there is only one reasonable way to do the conversion, as is the case with the conversion from Symbol to Cl***.
4. Don't take my word for it, ask Kent what he thinks about #asCl***.
5. #asCl*** isn't really a standard conversion method. If you feel it could be unrevealing of its intention (I think, slim chance of that), use Cl*** withName: aString. It's a little longer, but it also communicates just a little more.
Finally, I wasn't suggesting the method is in the Dolphin base system. I was suggesting Costas use it, for elegance, as per his request.
Regards, Peter
"Peter van Rooijen" pe...@vanrooijen.com
...
That's okay. I looked it up and what you say is his recommendation, actually isn't. See my post to Costas.
Might I respectfully suggest you refactor that code duplication ;-).
Best regards, Peter
cos ...@meezon.com (Costas Menico)
Peter, I agree that just because Kent said it it does not make it right.
Patterns are advisory as are many things in Smalltalk. There are many exceptions to the rules.
Hmmm, does he still work with Smalltalk?
No question #asCl*** or Cl*** withname: ***tring would be more meaningful. However I for my own reasons am trying to refrain from adding code to the base image of Dolphin.
I think for this I will just use what is available and just write a comment next to it.
Costas
"David Simmons" pul...@qks.com
Hi guys, If I might make a suggestion that would be a more general mapping pattern (that also happens to be compatible with features in SmallScript :-).
You might want to try implementing: #as: ... "in <Object>" This has the advantage that there is only ONE generalized selector #as:.
Then a variety of techniques like double dispatching or multi-method checking can be done to handle the conversion.
As in: =================== <?method [ as: aMapper ^aMapper asMapped: self ]?> <?method cl***=Cl*** [ asMapped: descriptor ^Smalltalk at: descriptor "or whatever is appropriate" ]?> <?method cl***=Boolean [ asMapped: descriptor ^descriptor !== false and: [descriptor !== nil and: [descriptor != 0]] ]?> <?method cl***=Interface "in SmallScript" [ asMapped: descriptor ...
]?> =================== Then one can write code like: a) anObject as: #String.
b) anObject as: Boolean.
*) anObject as: <some-cl*** or some-interface>
--
-- Dave Simmons [www.qks.com / www.smallscript.com] "Effectively solving a problem begins with how you express it." ...
"David Simmons" pul...@qks.com
Uhhh, duh. I went to all the trouble to write the post and then stupidly wrote the wrong answer here...
"SHOULD BE" a) #someCl***Name as: Cl***.
"David Simmons" pul...@qks.com
Dag nammit. I did it again. I was called to dinner a while ago and I've been rushing to get this post out. It's stupid I know. I apologize for a third try to get it right...
...
"THIS SHOULD SPECIFY cl***=Object" As in: <?method cl***=Object [ as: aMapper ^aMapper asMapped: self ]?>
cos ...@springmail.com (Costas Menico)
I think I will wait until later to study your posts. Make sure there is nothing else you left out <g>...
John Clonts jclo...@mastnet.net
[snipped excellent elaboration] Thank you, this helped me in something I was doing the other day.
I found even that squeak already had Object>>as: aSimilarCl*** ^ aSimilarCl*** newFrom: self So I just added: Cl*** cl***>>newFrom: aSymbolOrString ^ Smalltalk at: aSymbolOrString ***ymbol 'Date' as: Cl*** ==>Date Great! Thanks again!
Cheers, John
"David Simmons" pul...@qks.com
Cool. I'm glad it helped despite my rewrites.
By the way, did you notice that you still use #***ymbol.
^Smalltalk at: (aSymbolOrString as: Symbol) I'm not suggesting you shouldn't but it is worth noticing...
-- Dave Simmons [www.qks.com / www.smallscript.com] "Effectively solving a problem begins with how you express it."
"David Simmons" pul...@qks.com
Ok, so I probably should elaborate on this. And, it turns out to be a good basis for explaining a number of other related benefits and reasons for utilizing this kind of design/architecture.
First, let me point out that executing #***ymbol is a single dispatch for the case of <self> being a <Symbol>, vis-a-vis #as:<Symbol> which results in a double dispatch.
Without multi-method dispatch one could argue that double dispatching all conversions is more expensive in terms of performance. I.e., "aSymbol ***ymbol" is about as efficient as it gets for jitted execution. Whereas "aSymbol as: Symbol" results in a double dispatch and probably a type check in the second method -- so it is quite a bit more expensive. Read on to see how multi-method dispatch restores the performance balance while retaining the ability to generalize with #as:.
I.e., you would want both: "General method to obtain a canonically unique Symbol given a String" <?method cl***='Symbol.cl***' [ asMapped: <String> descriptor ...String to Symbol parse and intern code...
]?> "This is the method that gets you the same performance as you have with traditional #***ymbol." <?method cl***=Symbol [ as: <Symbol> ^self ]?> =============== How does this work?
Given: x as: Symbol If <x> is a <Symbol>, then the multi-method Symbol::as:(<Symbol>) would be invoked with exactly the same performance benefits as #***ymbol. I.e., no double dispatch. One-arg multi-method dispatch is almost identical performance as non-multi-method dispatch.
If, on the other hand, <x> is a <String> then it would be double-dispatched via Object::as:(<any>) to Symbol.cl***::asMapped:(<String>). Furthermore, as we note later on, the intern code will only be invoked if the <descriptor> is a type of <String> (an instance of <String> or one of its subcl***es).
Which is the same mechanism that #***ymbol typically uses. Unless, the <Symbol> intern code is actually performed in the Object::***ymbol() method.
While that is not how I would write it (because it is both a <Symbol> operation and it clutters <Object>), one could achieve that same mechanism by moving the code from Symbol.cl***::asMapped:(<String>).
AS IN: <?method cl***='Symbol.cl***' [ asMapped: <String> descriptor ...String to Symbol parse and intern code...
]?> TO <?method cl***=Object [ as: <Symbol.cl***> aMapper ...String to Symbol parse and intern code...
]?> Finally, with multi-method binding we have added type safety. First our <Symbol> intern code will only be invoked with a <descriptor> that is a <String> or one of its subcl***es. Second, as a result, we've pushed the contract compliance responsibility off our implementation shoulders and onto the callee's implementation shoulders (where it belongs).
In other words, multi-methods have allowed us to have runtime guaranteed behavior/contract safety in our methods. That means contract failure is no longer the responsibility of the provider, it becomes the exclusive responsibility of the consumer/client.
In this system we would get a DNR at the callsite for an expression of the form: 123 as: Symbol Whereas, without multi-method contract support the validation and thus failure point would be shifted to somewhere in the internals of the callee.
Which: (1) might make it much harder to figure out what the real problem was; (2) mean that as a "provider" we have to have more ***ertion/type-checks; (3) we might end up with side-effects for actions taken in the internals of the "provider" and therefore require more design work to add transaction semantics that allow reversing of the side-effects.
I.e., 123 as: Symbol Would presumably cause some exception inside the bowels of the <Symbol> intern code.
If you extrapolate this reasonably simple example to a complex application/framework architecture it could be much more challenging to validate and/or debug problems because the contract is not runtime enforced.
Hence, we would want to write/provide a significantly larger set of unit tests just to achieve a corresponding level of contract ***ertion/enforcement.
And that, in a nutshell, is the dynamic runtime equivalent of static (compile time) type safety/correctness behavior. Interfaces (mixin types), parametric polymorphism, and a few other things make the story even more attractive for dynamic type systems.
-- Dave Simmons [www.qks.com / www.smallscript.com] "Effectively solving a problem begins with how you express it."
"David Simmons" pul...@qks.com
BUG FIX: Today is clearly not a good day for me :(
-------
<?method cl***=Symbol [ as: <Symbol.cl***> "<-- Symbol.cl*** not Symbol" ^self ]?> I really should just grab real code from the system rather than writing this stuff off the top of my clearly overtaxed head.
"Blair McGlashan" bl...@object-arts.com
Peter Apologies for belatedly stepping into the middle of this conversation, but as I do posess a copy of the aforementioned tome I am going to support my colleague.
Actually I don't think Andy was citing Kent because he is a blind follower of the Beck faith. He (Kent) does happen to be a person who has a lucid way of setting down opinions with which we tend to feel an affinity based on our own experience. A citation is an economical way to transmit a well argued point of view without regurgitating it. When the citee is also a well respected individual then that opinion should rightly carry more weight.
Your contradiction may also not be right, and that of course the reader must judge. Of course when your own book is published Peter, we'll doubtless have another lucid reference to cite.
Well, your interpretation may be that Kent doesn't say so. Whether he does or does not, he does say rather more than that over those 2 pages. He mentions another problem - that of creating a tie between the cl***es.
Cl***es and Symbols are fundamental base cl***es, so this is probably not an issue in practical terms. It is though creating a coupling that need not exist.
I don't think one should attribute the explosion to any one case. Each converter method contributes a little, so it is blind application of the pattern which causes the explosion.
He does say that, but you have omitted his first precondition for implementing a converter method, which is that the "source and destination of conversion share the same protocol." Clearly not the case here.
Personally I don't think it is even correct to say that there is only one way to perform that conversion. That is probably true in Dolphin as it stands today (although the presence of demand loaded binary cl***es that don't appear in the system dictionary is a possible counter example), but in a future system with namespaces there might be a choice of binding algorithms to use to locate the cl***. How should the converter method locate the cl*** (i.e. in which namespace). Does it have sufficient context to do that? The natural lookup path would seem to be to first try the namespace of the calling context, information which is not available without using reflective means to access the sender on the stack.
Lastly there is a third precondition that I think should apply but which Kent does not mention - the conversion should be possible without error for the majority of cases. Perhaps this doesn't merit status as a pre-condition, but I hope you will agree that a conversion method which fails for 92% of existing instances (only 8% of the Symbols in my D4 image are cl*** names) has a fishy smell about it.
I'll have leave that to you - we haven't been introduced :-) I'd agree with that. If a method in Symbol is needed (certainly it is useful, however dubious) then it should have a name that indicates that it is a cl*** lookup since I wouldn't consider this to be a "conversion" at all.
Though it pains me to say it, what is really needed here is some more syntax. There should be a way to specify a late-bound reference to a cl*** in a declarative manner, rather than using some arbitrary expression. The ability to identify and track such binding references is important for both automated tools (image stripping springs to mind) and programmers alike.
Regards Blair
"Peter van Rooijen" pe...@vanrooijen.com
Hi Blair, ...
You are very welcome!
Good.
And I didn't say or think that. It was mostly Costas' remark that I was reacting to, who suggested that it was necessary for us to consult our patterns.
I completely agree with that.
Absolutely.
Actually, I agree with Kent. The main intent of my material remarks was to offer a different interpretation of what Kent wrote, than Andy's.
Well, thanks! I can't tell you when it will be out, but It's now almost certain it will be an e-book (that means non-paper, folks), and the title is expected to be 'How To Code Smalltalk' and it will likely have the subtitle 'IBM Smalltalk / VisualAge edition'. You could probably pay me to do the Dolphin edition first, though ;-).
And it may be wrong, certainly. Especially as I have been exploiting some ambiguity in what Kent wrote ;-).
I agree, and I put it to you, that that in itself probably makes all the other arguments moot, because it makes Symbol>asCl*** acceptable (or am I misunderstanding you?).
Why? And where is the coupling? I see coupling in the implementation of Symbol>>asCl*** ^Smalltalk cl***At: self "will return nil if no cl*** of the name is found" This is only one method in Symbol with one reference to a global, and one foreign selector! There is no coupling in the reverse direction. How much lower coupling can you get without staying entirely inside your own module?
Besides, the coupling is the whole idea of the programmer calling the method, isn't it?
I read Kent's story about the group who wrote asXXX for every cl*** in the domain code as illustration of what he means by 'protocol explosion'; that situation lead to every new cl*** starting out with dozens of methods just for converting, without it adding anything to the system otherwise. I can see that you don't want that. And I contend that Symbol>>asCl*** is a completely different case.
That only holds if the conditions are cumulative. If they are alternative (which is not an ureasonable position to take, I would say), it doesn't.
Certainly, Kent's own examples don't conform to the criteria as you (seem to :-)) interpret them: Collection>>***et
- Set supports Collection's protocol, but not the other way around (condition 1 only half satisfied)
- Is there only one reasonable implementation? ^(Set new: self size) addAll: self; yourself seems reasonable. Is it the only one? I'd want to redefine it as ^self in Set. Does that count as more than one way?
Number>>asFloat
- Condition 1 only half satisfied, as above
- Integer>>asFloat is a good candidate for a primitive, some others come to mind for different Number subcl***es. Condition 2 most probably not satisfied.
I'm agreeing.
Interesting issue, still. Binding things to names is what programming is all about.
I'm pretty sure nowhere mear that percentage of calls to Symbol>>asCl*** fails in the image of anyone using it :-). And I think you'd most probably agree. The metric you're proposing is not a very relevant criterium. Or have you ever thought, hey let's write Symbol>>asCl***, then run Symbol allInstances do: #asCl*** and see if it fails? I'll bet you haven't!
Kent's (yes/no) answer is not important to me. I decide for myself, based on thought, knowing Kent's excellent writings. Perhaps to someone else, it matters.
Well, I believe that there is little concern that #AbcDatabaseConnection asCl*** will be misunderstood. So, what's wrong with it? That seems to be the central question, after all.
I think I agree with that general idea. But when is the right time for binding, then? All I can think of, if not compile-time, is the latest possible time. With the variations 'bound once', and 'bound at each activation'. Interesting, indeed. Think you'll do it? I've long yearned for 'once blocks' anyway, BTW.
Best regards, Peter
| To Top |