Yet another lurker here. While I'm not at all qualified to speak for the seasoned Squeaker communities, I am qualified to comment on the issues I've found attempting to implement some sample applications for contract bids on top of Squeak. I've also tracked the progress of Pharo with great anticipation, and have spent a lot of time pondering what needs to change to make it useful for my production work.
Here's a few thoughts, inspired by Dan Ingalls's famous 1981 Byte article on the principles of Smalltalk.
Principle 9:
*Modularity:** No component in a complex system should depend on the internal details of any other component.*
Well if you look in the latest Pharo image, you'll find there's around 38 methods of the Object class that amount to:
isFoo
^ false
Each of these methods violate this principle, not only in spirit but also in practice. While this has been common practice for ages, it means that building downloadable modules that can be easily merged with any given image is practically impossible. As soon as you have two packages that define their own class that wants to use the same name, and register its own methods in a "base object", you both violate the principle of modularity AND break other packages.
Principle 12:
*Factoring:* *Each independent component in a system would appear in only one place.*
This is probably the single greatest reason for moving towards Traits across the board. But look at this concept in the context of two seemingly entirely different methods:
Object acceptDroppingMorph: transferMorph event: evt inMorph: dstListMorph
Object hasLiteralSuchThat: testBlock
If you look at both of these methods, you'll quickly realize that they are in fact the exact same method! This is because they have the exact same bytecode representation. These two methods are also exactly the same as the 38 isFoo methods I mentioned above. The only different is the semantics as viewed by the programmer, but the reality is none of these methods should be necessary, and should be factored out. These methods only exist due to a flaw in the design of the API and its semantics. This brings up the issue of
Principle 2:
*Good Design:* *A system should be built with a minimum set of unchangeable parts; those parts should be as general as possible; and all parts of the system should be held in a uniform framework.*
If you look at the problems of creating packages (see principle 9 above) or the excessive verbosity which leads to inordinate redundancy (see principle 12), these ultimately stem from lapses in principle 2. Over the years, cruft has accumulated, people's code has become fragile and dependent on that cruft (remember message passing's and modularity's purpose is to reduce fragility), and result in an inertia against fixing the fundamental design flaws. We end up with arguments over Squeak vs. Pharo vs. Etoys because the design of the core system has lapsed into disrepair, much like the US's infrastructure. A serious effort to address this short coming, and re-examine old design decisions to improve the core infrastructure would dramatically impact the future of all of these projects (as they all inherit the same problems).
Principle 17:
*Natural Selection:** Languages and systems that are of sound design will persist, to be supplanted only by better ones.*
The concept of forking the language, fixing core issues, and addressing design flaws is a core principle of Smalltalk. Maybe it is time for a better one.
Just my two cents,
Dave
On Mon, Jun 29, 2009 at 9:16 AM, David Goehrigdave@nexttolast.com wrote:
I agree that Squeak has accumulated a lot of cruft and it needs to be removed. This happens to most systems as they age. But I disagree with a lot of the detailed things you said.
Modularity: No component in a complex system should depend on the internal details of any other component.
Well if you look in the latest Pharo image, you'll find there's around 38 methods of the Object class that amount to: isFoo
^ false
Each of these methods violate this principle, not only in spirit but also in practice.
No, they do not. You are assuming that Object is a component. Classes in Smalltalk are not necessarily components. Often a component is a set of classes. Sometimes it is a set of classes plus some methods on other classes. Components often add methods to existing classes.
Principle 12:
Factoring: Each independent component in a system would appear in only one place.
This is probably the single greatest reason for moving towards Traits across the board.
Maybe. This is certainly the purpose of traits, but there is disagreement about whether traits actually succeed.
But look at this concept in the context of two seemingly entirely different methods:
Object acceptDroppingMorph: transferMorph event: evt inMorph: dstListMorph
Object hasLiteralSuchThat: testBlock
If you look at both of these methods, you'll quickly realize that they are in fact the exact same method! This is because they have the exact same bytecode representation.
Again, I disgree. They are not the same method. They are just methods with the same implementation. There is much more to a method than its byte-code. The name of the method is important. The type of the argument is important. The number of arguments is important. The meaning of the the method is important, i.e. what is its precondition and what do you expect to be true after you call it.
-Ralph Johnson
2009/6/29 David Goehrig dave@nexttolast.com:
Yet another lurker here. While I'm not at all qualified to speak for the seasoned Squeaker communities, I am qualified to comment on the issues I've found attempting to implement some sample applications for contract bids on top of Squeak. I've also tracked the progress of Pharo with great anticipation, and have spent a lot of time pondering what needs to change to make it useful for my production work. Here's a few thoughts, inspired by Dan Ingalls's famous 1981 Byte article on the principles of Smalltalk. Principle 9:
Modularity: No component in a complex system should depend on the internal details of any other component.
Well if you look in the latest Pharo image, you'll find there's around 38 methods of the Object class that amount to: isFoo
^ false
I feel uneasy each time, when adding own isXXX methods to Object. But i don't know another plausible way how to make a difference between an objects which having a certain capability vs rest of objects in a universe.
Here the code snippets of CVLambda class ( a quick & dirty lambda calculus implementation, which i using in own project). This class represents a lambda-message-send, i.e. it keeps all what a message send needs: receiver, selector, arguments. any of its slots can be a free variable or, in own turn, be another lambda and so on, without any limitations.
I need to compare, if two lambdas are equivalent:
= aLambda
^ aLambda isCVLambda and: [ aLambda isCVLambdaSlot not and: [ self size = aLambda size and: [ self do: [:i :obj | obj = (aLambda at: i) ifFalse: [ ^ false ]]. true ]]]
Sometimes i need to not reduce the lambdas immediately (because some of them could have a side effects), but keep them for a while as a message send, not yet performed , even if there is no free variables left, but i want to test, if it can be reduced:
hasSlots self do: [:i :obj | ( obj isCVLambda and: [ obj hasSlots ]) ifTrue: [ ^ true ] ]. ^ false
Sometimes i need to walk over all lambdas (contained in a topmost one):
lambdasDo: aBlock | copy | copy := self class basicNew: self size. self do: [:i :obj | copy at: i put: (obj isCVLambda ifTrue:[aBlock value: obj] ifFalse:[obj]) ]. ^ copy
so, here the question, can i implement the same behavior w/o using #isCVLambda , and without putting any additional methods to Object class?
Maybe the design is plainly wrong! Maybe i don't need any of the above behavior in correctly designed class. I don't know.
Each of these methods violate this principle, not only in spirit but also in practice. While this has been common practice for ages, it means that building downloadable modules that can be easily merged with any given image is practically impossible. As soon as you have two packages that define their own class that wants to use the same name, and register its own methods in a "base object", you both violate the principle of modularity AND break other packages. Principle 12:
Factoring: Each independent component in a system would appear in only one place.
This is probably the single greatest reason for moving towards Traits across the board. But look at this concept in the context of two seemingly entirely different methods:
Object acceptDroppingMorph: transferMorph event: evt inMorph: dstListMorph
Object hasLiteralSuchThat: testBlock
If you look at both of these methods, you'll quickly realize that they are in fact the exact same method! This is because they have the exact same bytecode representation. These two methods are also exactly the same as the 38 isFoo methods I mentioned above. The only different is the semantics as viewed by the programmer, but the reality is none of these methods should be necessary, and should be factored out. These methods only exist due to a flaw in the design of the API and its semantics. This brings up the issue of Principle 2:
Good Design: A system should be built with a minimum set of unchangeable parts; those parts should be as general as possible; and all parts of the system should be held in a uniform framework.
If you look at the problems of creating packages (see principle 9 above) or the excessive verbosity which leads to inordinate redundancy (see principle 12), these ultimately stem from lapses in principle 2. Over the years, cruft has accumulated, people's code has become fragile and dependent on that cruft (remember message passing's and modularity's purpose is to reduce fragility), and result in an inertia against fixing the fundamental design flaws. We end up with arguments over Squeak vs. Pharo vs. Etoys because the design of the core system has lapsed into disrepair, much like the US's infrastructure. A serious effort to address this short coming, and re-examine old design decisions to improve the core infrastructure would dramatically impact the future of all of these projects (as they all inherit the same problems). Principle 17:
Natural Selection: Languages and systems that are of sound design will persist, to be supplanted only by better ones.
The concept of forking the language, fixing core issues, and addressing design flaws is a core principle of Smalltalk. Maybe it is time for a better one. Just my two cents, Dave -- -=-=-=-=-=-=-=-=-=-=- http://blog.dloh.org/
I didn't read your example closely, but, generally....
(obj respondsTo: #isCVLambda) and: [ obj isCVLambda ]
is very general (independent of hierarchy), or if you want to trap people on a class tree...
obj isKindOf: CVLambda
or trap exceptions for when something doesn't respond to isCVLambda ...
or...
-- BUT, it will be slower.
an intriguing idea is having Object always respond to any pattern isXXX with false, unless the method is explicitly defined. (Now there I go, changing the root behavior...)
-Cam
On Jun 29, 2009, at 1:18 PM, Igor Stasenko wrote:
so, here the question, can i implement the same behavior w/o using #isCVLambda , and
What I typically what I've been doing to eliminate all of these methods with a single simple change: Object doesNoUnderstand: aMessage ^ false
Occasionally, I'll add a line in there to log the message to a transcript just to see if I'm doing something stupid, but 99.9 out of 100, the edit time method checks are enough to catch most typo bugs. Surprisingly enough, this breaks incredibly little existing code, since anything that would fire off the exception ends up user trapped :) And false is a pretty good default, following the principle "when in doubt return false"!
Then again I also like replacing "respondsTo: canUnderstand: and canPerform:" with a single "can:" method, because semantically they're all asking the same basic question. There's so much of the system that can be refactored this way, where you have multiple methods with the same semantic meaning but slight variations in implementation that you could squash the vocabulary to a more manageable base with some careful thought.
Of course, I'm not married to a large existing code base :)
On Tue, Jun 30, 2009 at 7:43 AM, David Goehrig dave@nexttolast.com wrote:
What I typically what I've been doing to eliminate all of these methods with a single simple change: Object doesNoUnderstand: aMessage ^ false
/me runs away screaming.
Gulik.
What you write (down below) is close to what I was thinking when I said have all #isXXX return false by default; although I would test that the first two characters match 'i' and 's' and that more characters exist, if so, return false, otherwise, normal #doesNotUnderstand: behavior.
I would treat #is by itself differently... it is ... or it couldn't be tested! So I would do nothing for simple #is. and I don't like #is: because it looks like a class type test... but that is part of the point, eh?
I'll have to go back to the original example (by siguctua@gmail.com, and read more about lambdas) but I thought that CVLambda would implement #isCVLambda to return true when it can be verified to be one. The example did not illustrate #doesNotUnderstand:.
Back to the question of adding behavior to classes that you don't own. VisualWorks has a means to extend a class in a different package ... as I recall. As I recall, squeak has no such capability, right?
Thanks. You have given me food for thought...
Ciao, Cam
On Jun 29, 2009, at 3:43 PM, David Goehrig wrote:
What I typically what I've been doing to eliminate all of these methods with a single simple change:
Object doesNoUnderstand: aMessage ^ false
2009/6/30 Cameron Sanders csanders.personal@functional-analyst.com:
What you write (down below) is close to what I was thinking when I said have all #isXXX return false by default; although I would test that the first two characters match 'i' and 's' and that more characters exist, if so, return false, otherwise, normal #doesNotUnderstand: behavior.
I would treat #is by itself differently... it is ... or it couldn't be tested! So I would do nothing for simple #is. and I don't like #is: because it looks like a class type test... but that is part of the point, eh?
I wouldn't say that. It is more trait-based approach than class-based.
The concept of #is: are: When object foo having some trait, it should answer true on 'foo is: sometrait ', otherwise false. Obviously since most subclasses inherit the behavior & traits of base class, you should honor this rule in overrides of #is: method i.e.:
Someclass>>is: object ^ (your tests here) or: [ super is: object ]
otherwise, if you omit the super send, some of the traits will become unavailable. But of course, except when you doing this intentionally.
I'll have to go back to the original example (by siguctua@gmail.com, and read more about lambdas) but I thought that CVLambda would implement #isCVLambda to return true when it can be verified to be one. The example did not illustrate #doesNotUnderstand:.
Back to the question of adding behavior to classes that you don't own. VisualWorks has a means to extend a class in a different package ... as I recall. As I recall, squeak has no such capability, right?
MC having this capability for a years.
Thanks. You have given me food for thought...
Ciao, Cam
On Jun 29, 2009, at 3:43 PM, David Goehrig wrote:
What I typically what I've been doing to eliminate all of these methods with a single simple change:
Object doesNoUnderstand: aMessage ^ false
Oh... ... oh... the CVLambda class was/is trait based. Confession: I haven't done my homework on traits. That might explain a little!
I was not aware of the rules/origin for #is: -- thank you!
<more down below>
On Jun 29, 2009, at 11:59 PM, Igor Stasenko wrote:
2009/6/30 Cameron Sanders csanders.personal@functional-analyst.com:
The concept of #is: are: When object foo having some trait, it should answer true on 'foo is: sometrait ', otherwise false. Obviously since most subclasses inherit the behavior & traits of base class, you should honor this rule in overrides of #is: method i.e.:
Someclass>>is: object ^ (your tests here) or: [ super is: object ]
otherwise, if you omit the super send, some of the traits will become unavailable. But of course, except when you doing this intentionally.
I'll have to go back to the original example (by siguctua@gmail.com, and read more about lambdas) but I thought that CVLambda would implement #isCVLambda to return true when it can be verified to be one. The example did not illustrate #doesNotUnderstand:.
Back to the question of adding behavior to classes that you don't own. VisualWorks has a means to extend a class in a different package ... as I recall. As I recall, squeak has no such capability, right?
MC having this capability for a years.
So how do I do extend a class. Simply define it again for a given package?
I did not know this was in Squeak.
Again, thank you... now I am glad I offered up answers to things I didn't understand!
Cheers, Cam
2009/6/30 Cameron Sanders csanders.personal@functional-analyst.com:
Oh... ... oh... the CVLambda class was/is trait based. Confession: I haven't done my homework on traits. That might explain a little!
I was not aware of the rules/origin for #is: -- thank you!
Don't take a 'trait' word literally. I didn't mean an implemetation specific Traits (introduced in Squeak 3.9) but rather some 'label' which you, as developer want to put on some kind/group of objects. The main point is, that often, such labels not in sync with class hierarchy (imagine you could treat a Number subclasses and DatabaseConnection subclasses as objects which share some common trait).
But of course, my #is: proposal lays perfectly on Traits basis. If you implement this method in Trait, and then use this trait in multiple different classes - then it will work exactly as i describing.
<more down below>
On Jun 29, 2009, at 11:59 PM, Igor Stasenko wrote:
2009/6/30 Cameron Sanders csanders.personal@functional-analyst.com:
The concept of #is: are: When object foo having some trait, it should answer true on 'foo is: sometrait ', otherwise false. Obviously since most subclasses inherit the behavior & traits of base class, you should honor this rule in overrides of #is: method i.e.:
Someclass>>is: object ^ (your tests here) or: [ super is: object ]
otherwise, if you omit the super send, some of the traits will become unavailable. But of course, except when you doing this intentionally.
I'll have to go back to the original example (by siguctua@gmail.com, and read more about lambdas) but I thought that CVLambda would implement #isCVLambda to return true when it can be verified to be one. The example did not illustrate #doesNotUnderstand:.
Back to the question of adding behavior to classes that you don't own. VisualWorks has a means to extend a class in a different package ... as I recall. As I recall, squeak has no such capability, right?
MC having this capability for a years.
So how do I do extend a class. Simply define it again for a given package?
you can put an extension method to any class, by putting it into a category named by your package name, prepended by '*' character:
*MyPackage
I did not know this was in Squeak.
Again, thank you... now I am glad I offered up answers to things I didn't understand!
You welcome :)
Cheers, Cam
Since multiple people seem don't grok my example, let me simplify it a bit:
Suppose you have a special collection of objects over which you can iterate.
The items in that collection could be anything - you don't really care on almost any of them, except those ones which is also a special collections of same kind as a container.
so, then you could have a method, which by iterating through your special collection, ignores all objects except those who also can be iterated in same manner:
MySpecialCollection>>mySpecialCollect: aBlock ^ self collect: [:each | each isMySpecialCollection ifTrue: [ each mySpecialCollect: aBlock] ifFalse: [each] ]
2009/6/30 Igor Stasenko siguctua@gmail.com:
Since multiple people seem don't grok my example, let me simplify it a bit:
Suppose you have a special collection of objects over which you can iterate.
The items in that collection could be anything - you don't really care on almost any of them, except those ones which is also a special collections of same kind as a container.
so, then you could have a method, which by iterating through your special collection, ignores all objects except those who also can be iterated in same manner:
MySpecialCollection>>mySpecialCollect: aBlock ^ self collect: [:each | each isMySpecialCollection ifTrue: [ each mySpecialCollect: aBlock] ifFalse: [each] ]
oops. looks like aBlock is not used :) Okay.. how about that:
MySpecialCollection>>mySpecialCollect: aBlock | copy | copy := self collect: [:each | each isMySpecialCollection ifTrue: [ each mySpecialCollect: aBlock] ifFalse: [each] ]. ^ aBlock value: copy
-- Best regards, Igor Stasenko AKA sig.
On Tue, 30 Jun 2009 07:15:37 +0200, Igor Stasenko wrote:
Since multiple people seem don't grok my example, let me simplify it a bit:
Suppose you have a special collection of objects over which you can iterate.
The items in that collection could be anything - you don't really care on almost any of them, except those ones which is also a special collections of same kind as a container.
so, then you could have a method, which by iterating through your special collection, ignores all objects except those who also can be iterated in same manner:
MySpecialCollection>>mySpecialCollect: aBlock ^ self collect: [:each | each isMySpecialCollection ifTrue: [ each mySpecialCollect: aBlock] ifFalse: [each] ]
In _any_ Smalltalk, (each respondsTo: #mySpecialCollect:) is what you want.
2009/6/30 Klaus D. Witzel klaus.witzel@cobss.com:
On Tue, 30 Jun 2009 07:15:37 +0200, Igor Stasenko wrote:
Since multiple people seem don't grok my example, let me simplify it a bit:
Suppose you have a special collection of objects over which you can iterate.
The items in that collection could be anything - you don't really care on almost any of them, except those ones which is also a special collections of same kind as a container.
so, then you could have a method, which by iterating through your special collection, ignores all objects except those who also can be iterated in same manner:
MySpecialCollection>>mySpecialCollect: aBlock ^ self collect: [:each | each isMySpecialCollection ifTrue: [ each mySpecialCollect: aBlock] ifFalse: [each] ]
In _any_ Smalltalk, (each respondsTo: #mySpecialCollect:) is what you want.
where the guarantees that some completely different class does not implements such method with completely different behavior?
-- "If at first, the idea is not absurd, then there is no hope for it". Albert Einstein
On Tue, 30 Jun 2009 16:39:43 +0200, Igor Stasenko wrote:
2009/6/30 Klaus D. Witzel :
...
so, then you could have a method, which by iterating through your special collection, ignores all objects except those who also can be iterated in same manner:
MySpecialCollection>>mySpecialCollect: aBlock ^ self collect: [:each | each isMySpecialCollection ifTrue: [ each mySpecialCollect: aBlock] ifFalse: [each] ]
In _any_ Smalltalk, (each respondsTo: #mySpecialCollect:) is what you want.
where the guarantees that some completely different class does not implements such method with completely different behavior?
Yes, I know, and I know your other requirements from CVLambda as well :) the answer is :
1] you want to reflect on every possible class/behavior
2] you don't want to reflect on 1] as well
Shall I write 3] or was this already Okay ;)
/Klaus
2009/6/30 Klaus D. Witzel klaus.witzel@cobss.com:
On Tue, 30 Jun 2009 16:39:43 +0200, Igor Stasenko wrote:
2009/6/30 Klaus D. Witzel :
...
so, then you could have a method, which by iterating through your special collection, ignores all objects except those who also can be iterated in same manner:
MySpecialCollection>>mySpecialCollect: aBlock ^ self collect: [:each | each isMySpecialCollection ifTrue: [ each mySpecialCollect: aBlock] ifFalse: [each] ]
In _any_ Smalltalk, (each respondsTo: #mySpecialCollect:) is what you want.
where the guarantees that some completely different class does not implements such method with completely different behavior?
Yes, I know, and I know your other requirements from CVLambda as well :) the answer is :
1] you want to reflect on every possible class/behavior
2] you don't want to reflect on 1] as well
Shall I write 3] or was this already Okay ;)
Sorry, can't understand. A meaning of 'reflect on' doesn't fits well with my non-english mindset :)
/Klaus
On Mon, Jun 29, 2009 at 8:59 PM, Igor Stasenko siguctua@gmail.com wrote:
2009/6/30 Cameron Sanders csanders.personal@functional-analyst.com:
What you write (down below) is close to what I was thinking when I said
have
all #isXXX return false by default; although I would test that the first two characters match 'i' and 's' and that more characters exist, if so, return false, otherwise, normal #doesNotUnderstand: behavior.
I would treat #is by itself differently... it is ... or it couldn't be tested! So I would do nothing for simple #is. and I don't like #is:
because
it looks like a class type test... but that is part of the point, eh?
I wouldn't say that. It is more trait-based approach than class-based.
The concept of #is: are: When object foo having some trait, it should answer true on 'foo is: sometrait ', otherwise false. Obviously since most subclasses inherit the behavior & traits of base class, you should honor this rule in overrides of #is: method i.e.:
Someclass>>is: object ^ (your tests here) or: [ super is: object ]
otherwise, if you omit the super send, some of the traits will become unavailable. But of course, except when you doing this intentionally.
I'll have to go back to the original example (by siguctua@gmail.com, and read more about lambdas) but I thought that CVLambda would implement #isCVLambda to return true when it can be verified to be one. The
example
did not illustrate #doesNotUnderstand:.
Back to the question of adding behavior to classes that you don't own. VisualWorks has a means to extend a class in a different package ... as I recall. As I recall, squeak has no such capability, right?
MC having this capability for a years.
Arguably not. (BTW VW has had it from 98). The crucial difference is that in VW an extension can be in more than one package. In Squeak a selector can only be in a single Monticello extension category. That leads to the awful bug that when an MC package patches a base selector to extend it to fix a bug it ends up in the package and its membership of the package it was in previously being lost. I understand that with method history this situation can be detected but if you e.g. condense changes then that history is lost, and the base package selector becomes only an extension Ouch.
The VW "solution" is OK as far as it goes; a package is a set of class, selector pairs (more than this, but this is the essence). An MC package is defined by class and method categories. VW's parcels are intensional, MC packages are extensional. There's a tension in both; many VW'ers want, at least at the UI level, for packages to be extensional, but precision (knowing whether something is in a package or not, allowing things to be in more than one package so that one can include patches, etc) requires an intentional construct.
When you also provide selector namespaces I don't think the situation changes because one still needs patches in the absence of a perfectly designed system. So IMO somehow MC needs to move to an intensional package definition, at least for extensions ;)
Thanks. You have given me food for thought...
Ciao, Cam
On Jun 29, 2009, at 3:43 PM, David Goehrig wrote:
What I typically what I've been doing to eliminate all of these methods with a single simple change:
Object doesNoUnderstand: aMessage ^ false
-- Best regards, Igor Stasenko AKA sig.
2009/6/30 Eliot Miranda eliot.miranda@gmail.com:
On Mon, Jun 29, 2009 at 8:59 PM, Igor Stasenko siguctua@gmail.com wrote:
2009/6/30 Cameron Sanders csanders.personal@functional-analyst.com:
What you write (down below) is close to what I was thinking when I said have all #isXXX return false by default; although I would test that the first two characters match 'i' and 's' and that more characters exist, if so, return false, otherwise, normal #doesNotUnderstand: behavior.
I would treat #is by itself differently... it is ... or it couldn't be tested! So I would do nothing for simple #is. and I don't like #is: because it looks like a class type test... but that is part of the point, eh?
I wouldn't say that. It is more trait-based approach than class-based.
The concept of #is: are: When object foo having some trait, it should answer true on 'foo is: sometrait ', otherwise false. Obviously since most subclasses inherit the behavior & traits of base class, you should honor this rule in overrides of #is: method i.e.:
Someclass>>is: object ^ (your tests here) or: [ super is: object ]
otherwise, if you omit the super send, some of the traits will become unavailable. But of course, except when you doing this intentionally.
I'll have to go back to the original example (by siguctua@gmail.com, and read more about lambdas) but I thought that CVLambda would implement #isCVLambda to return true when it can be verified to be one. The example did not illustrate #doesNotUnderstand:.
Back to the question of adding behavior to classes that you don't own. VisualWorks has a means to extend a class in a different package ... as I recall. As I recall, squeak has no such capability, right?
MC having this capability for a years.
Arguably not. (BTW VW has had it from 98). The crucial difference is that in VW an extension can be in more than one package. In Squeak a selector can only be in a single Monticello extension category. That leads to the awful bug that when an MC package patches a base selector to extend it to fix a bug it ends up in the package and its membership of the package it was in previously being lost. I understand that with method history this situation can be detected but if you e.g. condense changes then that history is lost, and the base package selector becomes only an extension Ouch. The VW "solution" is OK as far as it goes; a package is a set of class, selector pairs (more than this, but this is the essence). An MC package is defined by class and method categories. VW's parcels are intensional, MC packages are extensional. There's a tension in both; many VW'ers want, at least at the UI level, for packages to be extensional, but precision (knowing whether something is in a package or not, allowing things to be in more than one package so that one can include patches, etc) requires an intentional construct. When you also provide selector namespaces I don't think the situation changes because one still needs patches in the absence of a perfectly designed system. So IMO somehow MC needs to move to an intensional package definition, at least for extensions ;)
i just wanted to point that MC having an extension mechanism. Yes, it is different from VW one. And in the light of your description - it is done wrong (at least for me), but its still can be called 'extension mechanism' :)
Thanks. You have given me food for thought...
Ciao, Cam
On Jun 29, 2009, at 3:43 PM, David Goehrig wrote:
What I typically what I've been doing to eliminate all of these methods with a single simple change:
Object doesNoUnderstand: aMessage ^ false
-- Best regards, Igor Stasenko AKA sig.
On Tue, Jun 30, 2009 at 10:33 AM, Igor Stasenko siguctua@gmail.com wrote:
2009/6/30 Eliot Miranda eliot.miranda@gmail.com:
On Mon, Jun 29, 2009 at 8:59 PM, Igor Stasenko siguctua@gmail.com
wrote:
2009/6/30 Cameron Sanders csanders.personal@functional-analyst.com:
What you write (down below) is close to what I was thinking when I
said
have all #isXXX return false by default; although I would test that the first two characters match 'i' and 's' and that more characters exist, if so, return false, otherwise, normal #doesNotUnderstand: behavior.
I would treat #is by itself differently... it is ... or it couldn't be tested! So I would do nothing for simple #is. and I don't like #is: because it looks like a class type test... but that is part of the point, eh?
I wouldn't say that. It is more trait-based approach than class-based.
The concept of #is: are: When object foo having some trait, it should answer true on 'foo is: sometrait ', otherwise false. Obviously since most subclasses inherit the behavior & traits of base class, you should honor this rule in overrides of #is: method i.e.:
Someclass>>is: object ^ (your tests here) or: [ super is: object ]
otherwise, if you omit the super send, some of the traits will become unavailable. But of course, except when you doing this intentionally.
I'll have to go back to the original example (by siguctua@gmail.com,
and
read more about lambdas) but I thought that CVLambda would implement #isCVLambda to return true when it can be verified to be one. The example did not illustrate #doesNotUnderstand:.
Back to the question of adding behavior to classes that you don't own. VisualWorks has a means to extend a class in a different package ...
as
I recall. As I recall, squeak has no such capability, right?
MC having this capability for a years.
Arguably not. (BTW VW has had it from 98). The crucial difference is that in VW an extension can be in more than one package. In Squeak a selector
can
only be in a single Monticello extension category. That leads to the
awful
bug that when an MC package patches a base selector to extend it to fix a bug it ends up in the package and its membership of the package it was in previously being lost. I understand that with method history this
situation
can be detected but if you e.g. condense changes then that history is
lost,
and the base package selector becomes only an extension Ouch. The VW "solution" is OK as far as it goes; a package is a set of class, selector pairs (more than this, but this is the essence). An MC package
is
defined by class and method categories. VW's parcels are intensional, MC packages are extensional. There's a tension in both; many VW'ers want,
at
least at the UI level, for packages to be extensional, but precision (knowing whether something is in a package or not, allowing things to be
in
more than one package so that one can include patches, etc) requires an intentional construct. When you also provide selector namespaces I don't think the situation changes because one still needs patches in the absence of a perfectly designed system. So IMO somehow MC needs to move to an intensional
package
definition, at least for extensions ;)
i just wanted to point that MC having an extension mechanism. Yes, it is different from VW one. And in the light of your description - it is done wrong (at least for me), but its still can be called 'extension mechanism' :)
then +1 :)
Thanks. You have given me food for thought...
Ciao, Cam
On Jun 29, 2009, at 3:43 PM, David Goehrig wrote:
What I typically what I've been doing to eliminate all of these
methods
with a single simple change:
Object doesNoUnderstand: aMessage ^ false
-- Best regards, Igor Stasenko AKA sig.
-- Best regards, Igor Stasenko AKA sig.
Hi!
Eliot Miranda wrote:
Arguably not. (BTW VW has had it from 98). The crucial difference is that in VW an extension can be in more than one package. In Squeak a selector can only be in a single Monticello extension category. That leads to the awful bug that when an MC package patches a base selector to extend it to fix a bug it ends up in the package and its membership of the package it was in previously being lost. I understand that with method history this situation can be detected but if you e.g. condense changes then that history is lost, and the base package selector becomes only an extension Ouch.
The VW "solution" is OK as far as it goes; a package is a set of class, selector pairs (more than this, but this is the essence). An MC package is defined by class and method categories. VW's parcels are intensional, MC packages are extensional. There's a tension in both; many VW'ers want, at least at the UI level, for packages to be extensional, but precision (knowing whether something is in a package or not, allowing things to be in more than one package so that one can include patches, etc) requires an intentional construct.
When you also provide selector namespaces I don't think the situation changes because one still needs patches in the absence of a perfectly designed system. So IMO somehow MC needs to move to an intensional package definition, at least for extensions ;)
Now, yet again I pull out from my hat... DeltaStreams! :)
If a package was defined as a Delta, then - since a Delta is a *completely standalone object* - you can never lose any information by loading another Delta into the image. NOTE: Loading is not the same as applying.
So if you have 2 Deltas loaded into the image (no changes have yet been made to the image!) you can analyze them and discover that they both include SomeClass>>someMethod - with conflicting code in them (otherwise we would still be kinda ok).
If you would proceed and apply first one (=make the changes to the image) and then the other you would of course end up with the last version of that specific method.
But fact remains - Deltas are *standalone* and can be loaded *before* you actually apply them. And you can revert them if all their changes are revertable.
Deltas can thus also be used as a "quilt" system (like Mercurial queues) where you can juggle Deltas and "shelf" / "unshelf" stuff etc.
Sorry, just couldn't help mentioning it.
regards, Göran
Eliot Miranda wrote:
Arguably not. (BTW VW has had it from 98). The crucial difference is that in VW an extension can be in more than one package. In Squeak a selector can only be in a single Monticello extension category. That leads to the awful bug that when an MC package patches a base selector to extend it to fix a bug it ends up in the package and its membership of the package it was in previously being lost. I understand that with method history this situation can be detected but if you e.g. condense changes then that history is lost, and the base package selector becomes only an extension Ouch.
That's a bug in condenseChanges, not a conceptual problem. BTW, I really fail to see how it would make any difference whatsoever if extension methods are in multiple packages or not. In either case you are *completely* screwed if you have multiple packages trying to extend the same method. Seriously, has there ever been a situation where that actually works? (my personal preference is actually that MC should blow up straight into your face if you try to change a method that's in an extension category already, but that's just me - I generally avoid extensions like the plague)
The VW "solution" is OK as far as it goes; a package is a set of class, selector pairs (more than this, but this is the essence). An MC package is defined by class and method categories. VW's parcels are intensional, MC packages are extensional.
I don't understand the distinction you are making between intensional and extensional. Care to elaborate?
MC internally actually uses a set of class and selector pairs as well, it just derives them via PackageInfo from categories. And it most certainly can have methods in multiple packages. For example, if you add a method in category '*Foo-Bar-Baz' it will be part of the packages 'Foo-Bar-Baz', 'Foo-Bar' and 'Foo' (if those exist).
The only thing that prevents you from having other combinations of multiple package memberships is that there is no UI that allows you to specify which combinations of packages a method should be part of. The category hack is great because it works with existing tools, but existing tools don't allow for methods in multiple categories. If you fix that you get your multiple package membership in Monticello for free. Alternatively provide a UI to specify which other packages the method should be part of, and you're there, too.
Cheers, - Andreas
The only thing that prevents you from having other combinations of multiple package memberships is that there is no UI that allows you to specify which combinations of packages a method should be part of. The category hack is great because it works with existing tools, but existing tools don't allow for methods in multiple categories. If you fix that you get your multiple package membership in Monticello for free. Alternatively provide a UI to specify which other packages the method should be part of, and you're there, too.
That would be very useful !
Stef
On Tue, Jun 30, 2009 at 11:37 AM, Andreas Raab andreas.raab@gmx.de wrote:
Eliot Miranda wrote:
Arguably not. (BTW VW has had it from 98). The crucial difference is that in VW an extension can be in more than one package. In Squeak a selector can only be in a single Monticello extension category. That leads to the awful bug that when an MC package patches a base selector to extend it to fix a bug it ends up in the package and its membership of the package it was in previously being lost. I understand that with method history this situation can be detected but if you e.g. condense changes then that history is lost, and the base package selector becomes only an extension Ouch.
That's a bug in condenseChanges, not a conceptual problem. BTW, I really fail to see how it would make any difference whatsoever if extension methods are in multiple packages or not. In either case you are *completely* screwed if you have multiple packages trying to extend the same method. Seriously, has there ever been a situation where that actually works? (my personal preference is actually that MC should blow up straight into your face if you try to change a method that's in an extension category already, but that's just me - I generally avoid extensions like the plague)
I don't avoid extensions, and personally feel extensions are a core part of building frameworks. If objects are to interact they must provide protocol to support that interaction. In adding a new framework to an existing system this means extending existing objects so that they can interact with the new framework. Yes, one can do this badly, but there are lots of examples where this makes sense.
As far as patching, we've had this conversation before. I agree that a well-designed system will provide extension mechanisms that make patching unnecessary. But unless the designer has excellent foresight (which is more difficult the further forward in time one projects) then there will be areas of the system which have to be patched. If two packages need to patch the same method then there is a problem, but one is not totally screwed. Provided that both packages have the same patch then they are compatible. That means that one can load package A and get the patch, load package B and get the patch and load bth packages and get the patch provided it is identical in both packages. Seems fine to me, and much more desirable than requiring extra-package management of the patch (e.g. "please file-in this changeset before loading the package") because that extra-package stuff will get lost, or ignored and then you're screwed too.
So for me handling extensions in packages in robust ways is about the system pragmatically supporting real practice, rather than enforcing ideals which one should strive for but are costly to arrive at and must be evolved towards in practice.
The VW "solution" is OK as far as it goes; a package is a set of class,
selector pairs (more than this, but this is the essence). An MC package is defined by class and method categories. VW's parcels are intensional, MC packages are extensional.
I don't understand the distinction you are making between intensional and extensional. Care to elaborate?
I hope I've got this the right way round. An extensional concept is one where a rule selects the members, e.g. the set of all sets is an extentionally defined set. An intentional concept is where individual members are defined as such, so A labrador is a dog is an intentional definition. MC packages are extensional; the methods on a class in a category of the package are all the methods on the class that are not otherwise categorised in category extensions of other packages and whose history does not include a non-extension category. VW parcels are intentional; the methods in a parcel are those that are explicitly listed in the parcel. A parcel has a Dictionary of class to set of selector for each class with methods in the parcel.
I think its clear that the extensional approach is more convenient and provides a nicer UI because (especially in MC's use of the existing categorisation scheme) it dovetails with the existing programming facilities. It is tedious to have to add a method to a package every time one defines one. An extensional approach allows the adding to be implicit. But as I've said earlier in the thread the extensional approach has problems when it comes to managing multiple packages that can overlap (as I think s inevitable in imperfect complex systems). So I would argue for the physical implementation of a package, and an extension mechanism ot be intentional, but for the UI to be extensional, so that e.g. when one is in a particular project all methods get added to the project's current package without programmer intervention. One still needs additional tools that allow interacting with the intentional nature of things (much like the change set browser allows in moving changes between sets), but largely one can have one's cake and eat it too if the UI i extensional but packages are intentional.
MC internally actually uses a set of class and selector pairs as well, it
just derives them via PackageInfo from categories. And it most certainly can have methods in multiple packages. For example, if you add a method in category '*Foo-Bar-Baz' it will be part of the packages 'Foo-Bar-Baz', 'Foo-Bar' and 'Foo' (if those exist).
No it doesn't. The list is an internal representation used merely in recording the set of methods in question. But that set is compyted by an extensional approach by applying a category pattern to the system as it exists currently and writing that projection of the rule out.
The only thing that prevents you from having other combinations of multiple package memberships is that there is no UI that allows you to specify which combinations of packages a method should be part of. The category hack is great because it works with existing tools, but existing tools don't allow for methods in multiple categories. If you fix that you get your multiple package membership in Monticello for free. Alternatively provide a UI to specify which other packages the method should be part of, and you're there, too.
Agreed. But internally MC packages should specify an exact set of entities, and not rely on an extensional projection. It is imprecise and hence inherently buggy. (I am being very anal today ;).
Cheers,
- Andreas
On Tue, Jun 30, 2009 at 3:00 PM, Eliot Miranda eliot.miranda@gmail.comwrote:
But as I've said earlier in the thread the extensional approach has problems when it comes to managing multiple packages that can overlap (as I think s inevitable in imperfect complex systems).
I realize this thread is more concerned with the here and the now, but doesn't this problem go away entirely in a system like Newspeak? When you can have modules of code co-resident and active at the same time, wouldn't you just create a new version of some package on which you depend rather than patch it with extension methods you define in your own package? Your code would happily use your version of the package while others happily use the unmodified version. I can see a potential need in cases where a body of code is designed to interface with some already present service (for example, a phone dialer application designed to use an AddressBook service). But, in such a system, you wouldn't want to allow monkey patching anyway out of security concerns (if you could override the AddressBook interface, your code could potentially access information that the owner didn't want your code to access).
- Stephen
On Tue, Jun 30, 2009 at 1:38 PM, Stephen Pair stephen@pairhome.net wrote:
On Tue, Jun 30, 2009 at 3:00 PM, Eliot Miranda eliot.miranda@gmail.comwrote:
But as I've said earlier in the thread the extensional approach has problems when it comes to managing multiple packages that can overlap (as I think s inevitable in imperfect complex systems).
I realize this thread is more concerned with the here and the now, but doesn't this problem go away entirely in a system like Newspeak? When you can have modules of code co-resident and active at the same time, wouldn't you just create a new version of some package on which you depend rather than patch it with extension methods you define in your own package? Your code would happily use your version of the package while others happily use the unmodified version.
In theory yes. But as soon as one had a living system which contained objects already active then I don't see how the Newspeak module system solves the problem. It neatly allows one to reuse modules and extend them locally. But as far as I can see once you want to interact in new ways with already live objects then one may still need to patch.
Gilad is virulently against monkey-patching and so there is no support for extensions in Newspeak modules; all very well. But at the same time one of the models of use for Newspeak is where clients implemented in Newspeak are orthogonally synchronised (both data and code are updated) at suitable points in an applications' use (e.g. when a server interaction is initiated). Here the author of a module is allowed to modify it and the next time any client containing an instance of that module would have the client's instance of the module updated transparently. So in a situation where some rich application on the client is composed of two modules, and the author of one of them wants an extension method added to the other so that his modifications can function one would have to request the author of the other module to add it and (presumably) wait for changes to propagate. There are lots of issues here (what if the author of the second module goes bust) but if the picture can be realised its actually much better than the Smalltalk package status quo because every instance is continuously integrated. One of the issues that gives rise to the need for patches is that one can't ask the author to update their package and so one has to do it oneself. Gilad's scheme is a great idea, but I'm not entirely sure it is that different from the current situation with automatically updated software (OSs etc). As the software gets more complex so organizations are less able to change it frequently. Newspeak may have some new means with which to attack the problem but the problem still remains.
I can see a potential need in cases where a body of code is designed to
interface with some already present service (for example, a phone dialer application designed to use an AddressBook service). But, in such a system, you wouldn't want to allow monkey patching anyway out of security concerns (if you could override the AddressBook interface, your code could potentially access information that the owner didn't want your code to access).
But how do you override your own AddressBook? Bringing a new one into existence is one thing, but modifying your fully-populated one implemented by someone else is another thing altogether. If the system is so architected then it is presumably trivial to create a new instance, move the data across and replace the old with the new. But if the system isn't (e.g. for security or ip concerns) then you can't. But these issues operate at a higher level than the language/module/update level right? The issues with monkey patching are to do with keeping software maintainable and comprehensible to software authors. The issues of mutable extensible systems also involve their users, and that's more than I can think about right now :)
- Stephen
On Tue, Jun 30, 2009 at 8:48 PM, Eliot Miranda eliot.miranda@gmail.comwrote:
On Tue, Jun 30, 2009 at 1:38 PM, Stephen Pair stephen@pairhome.netwrote:
I can see a potential need in cases where a body of code is designed to interface with some already present service (for example, a phone dialer application designed to use an AddressBook service). But, in such a system, you wouldn't want to allow monkey patching anyway out of security concerns (if you could override the AddressBook interface, your code could potentially access information that the owner didn't want your code to access).
But how do you override your own AddressBook? Bringing a new one into existence is one thing, but modifying your fully-populated one implemented by someone else is another thing altogether. If the system is so architected then it is presumably trivial to create a new instance, move the data across and replace the old with the new. But if the system isn't (e.g. for security or ip concerns) then you can't. But these issues operate at a higher level than the language/module/update level right? The issues with monkey patching are to do with keeping software maintainable and comprehensible to software authors. The issues of mutable extensible systems also involve their users, and that's more than I can think about right now :)
Overriding your own AddressBook would be a problem of loading in new code and migrating the instances (pretty much like software works today where you load a new version of an application and it has to update your existing database(s)). So, the new code would have whatever fancy new stuff you wanted to do in your AddressBook, but for external applications like the phone dialer to be able to interact with it, it would still need support some defined (and ideally versioned) interface. To the degree that new versions of the phone dialer and the address book are backward compatible with the versions of the interface they support, you are free to upgrade each independently. And, the AddressBook is free to use some highly customized collections framework while the phone dialer uses an entirely different implementation of collections. Such a system would offer very significant benefits to a community like squeak. Imagine being able to able to run squeak 2.0 and squeak 3.10 concurrently (with them able to communicate with each other) and imagine all the old, abandoned, but still useful projects that could be readily used without the need to migrate them to all the latest version of your favorite fork. I don't see these issues as being at some higher level, I see the language and platform as lacking a much needed capability.
(btw, none of this necessarily implies everything must be in a single process, I imagine much of this could be accomplished using hydra for instance)
- Stephen
On Tue, Jun 30, 2009 at 6:15 PM, Stephen Pair stephen@pairhome.net wrote:
On Tue, Jun 30, 2009 at 8:48 PM, Eliot Miranda eliot.miranda@gmail.comwrote:
On Tue, Jun 30, 2009 at 1:38 PM, Stephen Pair stephen@pairhome.netwrote:
I can see a potential need in cases where a body of code is designed to interface with some already present service (for example, a phone dialer application designed to use an AddressBook service). But, in such a system, you wouldn't want to allow monkey patching anyway out of security concerns (if you could override the AddressBook interface, your code could potentially access information that the owner didn't want your code to access).
But how do you override your own AddressBook? Bringing a new one into existence is one thing, but modifying your fully-populated one implemented by someone else is another thing altogether. If the system is so architected then it is presumably trivial to create a new instance, move the data across and replace the old with the new. But if the system isn't (e.g. for security or ip concerns) then you can't. But these issues operate at a higher level than the language/module/update level right? The issues with monkey patching are to do with keeping software maintainable and comprehensible to software authors. The issues of mutable extensible systems also involve their users, and that's more than I can think about right now :)
Overriding your own AddressBook would be a problem of loading in new code and migrating the instances (pretty much like software works today where you load a new version of an application and it has to update your existing database(s)).
That's a fine scheme. My point is that unless the system is so architected you can't necessarily do that. Look at the iPhone for example; there are lots of restrictions on what a user can accomplish and there is software therein to detect attempts to hack around these restrictions and break things like software update when you try and update a hacked phone. So your model only works in an architecture which supports it. Since Newspeak doesn't encompass a complete device architecture it can't provide an update schema for it. Expecting a language to address this is IMO a category error. When one starts talking about for example a Smalltalk system running on bare hardware (e.g. Squeak NOS) then the system can encompass the entirety. So we should first try and evolve Newspeak to NewspeakNOS and ten we can see how an evolved NewspeakNOS would address these update issues.
So, the new code would have whatever fancy new stuff you wanted to do in your AddressBook, but for external applications like the phone dialer to be able to interact with it, it would still need support some defined (and ideally versioned) interface. To the degree that new versions of the phone dialer and the address book are backward compatible with the versions of the interface they support, you are free to upgrade each independently. And, the AddressBook is free to use some highly customized collections framework while the phone dialer uses an entirely different implementation of collections. Such a system would offer very significant benefits to a community like squeak. Imagine being able to able to run squeak 2.0 and squeak 3.10 concurrently (with them able to communicate with each other) and imagine all the old, abandoned, but still useful projects that could be readily used without the need to migrate them to all the latest version of your favorite fork. I don't see these issues as being at some higher level, I see the language and platform as lacking a much needed capability.
(btw, none of this necessarily implies everything must be in a single process, I imagine much of this could be accomplished using hydra for instance)
- Stephen
On Wed, Jul 1, 2009 at 12:18 PM, Eliot Miranda eliot.miranda@gmail.comwrote:
On Tue, Jun 30, 2009 at 6:15 PM, Stephen Pair stephen@pairhome.netwrote:
On Tue, Jun 30, 2009 at 8:48 PM, Eliot Miranda eliot.miranda@gmail.comwrote:
On Tue, Jun 30, 2009 at 1:38 PM, Stephen Pair stephen@pairhome.netwrote:
I can see a potential need in cases where a body of code is designed to interface with some already present service (for example, a phone dialer application designed to use an AddressBook service). But, in such a system, you wouldn't want to allow monkey patching anyway out of security concerns (if you could override the AddressBook interface, your code could potentially access information that the owner didn't want your code to access).
But how do you override your own AddressBook? Bringing a new one into existence is one thing, but modifying your fully-populated one implemented by someone else is another thing altogether. If the system is so architected then it is presumably trivial to create a new instance, move the data across and replace the old with the new. But if the system isn't (e.g. for security or ip concerns) then you can't. But these issues operate at a higher level than the language/module/update level right? The issues with monkey patching are to do with keeping software maintainable and comprehensible to software authors. The issues of mutable extensible systems also involve their users, and that's more than I can think about right now :)
Overriding your own AddressBook would be a problem of loading in new code and migrating the instances (pretty much like software works today where you load a new version of an application and it has to update your existing database(s)).
That's a fine scheme. My point is that unless the system is so architected you can't necessarily do that.
LoL...well, of course...if the problem were already solved, there would be no point in contemplating possible solutions.
Look at the iPhone for example; there are lots of restrictions on what a user can accomplish and there is software therein to detect attempts to hack around these restrictions and break things like software update when you try and update a hacked phone. So your model only works in an architecture which supports it. Since Newspeak doesn't encompass a complete device architecture it can't provide an update schema for it. Expecting a language to address this is IMO a category error.
Didn't someone once say that the operating system is where you resolve all of the inadequacies of your language (or something to that effect)? ;)
When one starts talking about for example a Smalltalk system running on bare hardware (e.g. Squeak NOS) then the system can encompass the entirety. So we should first try and evolve Newspeak to NewspeakNOS and ten we can see how an evolved NewspeakNOS would address these update issues.
I don't think you need to go to such lengths to either show that such a scheme would work, or to get use out of it if it does. Regarding your iPhone example, it takes the discussion beyond the realm of that which we have control and into the realm of interfacing with legacy systems in which none of these kinds of issues have been considered or addressed. Take and example of upgrading the AddressBook on the iPhone platform (where it's a third party tool over which you have no control other than to upgrade it) while having it still work with say a custom Dialer app that you wrote. You are free to upgrade either of them independently so long as they both support a common interface. If the interface were version controlled, you can deal with this problem in a pretty rational manner for an end user (when you go to the AppStore and try to upgrade the AddressBook app, it could alert you that "this new version of the AddressBook no longer supports version 1 of the AddressBook API and since we see that you have a registered dependency form some Dialer app on version 1.0, we can pretty much guarantee that it's going to break your Dialer app, are you sure you want to do this?" You could then independently upgrade the Dialer app.
To bring this back to monkey patching...no, you cannot monkey patch the AddressBook API, but you can create a new version of some framework (with as many customizations as you desire) for use in your Dialer app while the, while the AddressBook continues to use some other version of that very same framework concurrently. Of course, Cocoa is not going to support this kind of thing (at least not today), but it doesn't preclude apps in some other language that has Newspeak style modularization from doing this (regardless of whether it's running on bare metal or not).
I think I've run out of steam on this topic for now.
- Stephen
On Wed, Jul 1, 2009 at 10:12 AM, Stephen Pair stephen@pairhome.net wrote:
On Wed, Jul 1, 2009 at 12:18 PM, Eliot Miranda eliot.miranda@gmail.comwrote:
On Tue, Jun 30, 2009 at 6:15 PM, Stephen Pair stephen@pairhome.netwrote:
On Tue, Jun 30, 2009 at 8:48 PM, Eliot Miranda eliot.miranda@gmail.comwrote:
On Tue, Jun 30, 2009 at 1:38 PM, Stephen Pair stephen@pairhome.netwrote:
I can see a potential need in cases where a body of code is designed to interface with some already present service (for example, a phone dialer application designed to use an AddressBook service). But, in such a system, you wouldn't want to allow monkey patching anyway out of security concerns (if you could override the AddressBook interface, your code could potentially access information that the owner didn't want your code to access).
But how do you override your own AddressBook? Bringing a new one into existence is one thing, but modifying your fully-populated one implemented by someone else is another thing altogether. If the system is so architected then it is presumably trivial to create a new instance, move the data across and replace the old with the new. But if the system isn't (e.g. for security or ip concerns) then you can't. But these issues operate at a higher level than the language/module/update level right? The issues with monkey patching are to do with keeping software maintainable and comprehensible to software authors. The issues of mutable extensible systems also involve their users, and that's more than I can think about right now :)
Overriding your own AddressBook would be a problem of loading in new code and migrating the instances (pretty much like software works today where you load a new version of an application and it has to update your existing database(s)).
That's a fine scheme. My point is that unless the system is so architected you can't necessarily do that.
LoL...well, of course...if the problem were already solved, there would be no point in contemplating possible solutions.
Look at the iPhone for example; there are lots of restrictions on what a user can accomplish and there is software therein to detect attempts to hack around these restrictions and break things like software update when you try and update a hacked phone. So your model only works in an architecture which supports it. Since Newspeak doesn't encompass a complete device architecture it can't provide an update schema for it. Expecting a language to address this is IMO a category error.
Didn't someone once say that the operating system is where you resolve all of the inadequacies of your language (or something to that effect)? ;)
When one starts talking about for example a Smalltalk system running on bare hardware (e.g. Squeak NOS) then the system can encompass the entirety. So we should first try and evolve Newspeak to NewspeakNOS and ten we can see how an evolved NewspeakNOS would address these update issues.
I don't think you need to go to such lengths to either show that such a scheme would work, or to get use out of it if it does. Regarding your iPhone example, it takes the discussion beyond the realm of that which we have control and into the realm of interfacing with legacy systems in which none of these kinds of issues have been considered or addressed. Take and example of upgrading the AddressBook on the iPhone platform (where it's a third party tool over which you have no control other than to upgrade it) while having it still work with say a custom Dialer app that you wrote. You are free to upgrade either of them independently so long as they both support a common interface. If the interface were version controlled, you can deal with this problem in a pretty rational manner for an end user (when you go to the AppStore and try to upgrade the AddressBook app, it could alert you that "this new version of the AddressBook no longer supports version 1 of the AddressBook API and since we see that you have a registered dependency form some Dialer app on version 1.0, we can pretty much guarantee that it's going to break your Dialer app, are you sure you want to do this?" You could then independently upgrade the Dialer app.
To bring this back to monkey patching...no, you cannot monkey patch the AddressBook API, but you can create a new version of some framework (with as many customizations as you desire) for use in your Dialer app while the, while the AddressBook continues to use some other version of that very same framework concurrently. Of course, Cocoa is not going to support this kind of thing (at least not today), but it doesn't preclude apps in some other language that has Newspeak style modularization from doing this (regardless of whether it's running on bare metal or not).
I think I've run out of steam on this topic for now.
Hi Stephen, apologies for apparently being obstructive. I was just trying to properly frame the discussion. I want to separate linguistic from system issues. Your first question was an excellent one and asked if linguistic mechanisms could resolve the issue and I said why I thought not. I didn't realise you were shifting ground to propose solutions and thought you were still discussing linguistic mechanisms. Apologies. Please don't run out of steam because I'm not going with the flow. I hope I get where you're aiming at now.
Could you extend your scenario with the potential for malware to attempt to patch the AddressBook and suggest ways one can legitimately patch the AddressBook and at the same time protect it from unwarranted modification?
- Stephen
On Wed, Jul 1, 2009 at 1:59 PM, Eliot Miranda eliot.miranda@gmail.comwrote:
On Wed, Jul 1, 2009 at 10:12 AM, Stephen Pair stephen@pairhome.netwrote:
On Wed, Jul 1, 2009 at 12:18 PM, Eliot Miranda eliot.miranda@gmail.comwrote:
On Tue, Jun 30, 2009 at 6:15 PM, Stephen Pair stephen@pairhome.netwrote:
On Tue, Jun 30, 2009 at 8:48 PM, Eliot Miranda <eliot.miranda@gmail.com
wrote:
On Tue, Jun 30, 2009 at 1:38 PM, Stephen Pair stephen@pairhome.netwrote:
I can see a potential need in cases where a body of code is designed to interface with some already present service (for example, a phone dialer application designed to use an AddressBook service). But, in such a system, you wouldn't want to allow monkey patching anyway out of security concerns (if you could override the AddressBook interface, your code could potentially access information that the owner didn't want your code to access).
But how do you override your own AddressBook? Bringing a new one into existence is one thing, but modifying your fully-populated one implemented by someone else is another thing altogether. If the system is so architected then it is presumably trivial to create a new instance, move the data across and replace the old with the new. But if the system isn't (e.g. for security or ip concerns) then you can't. But these issues operate at a higher level than the language/module/update level right? The issues with monkey patching are to do with keeping software maintainable and comprehensible to software authors. The issues of mutable extensible systems also involve their users, and that's more than I can think about right now :)
Overriding your own AddressBook would be a problem of loading in new code and migrating the instances (pretty much like software works today where you load a new version of an application and it has to update your existing database(s)).
That's a fine scheme. My point is that unless the system is so architected you can't necessarily do that.
LoL...well, of course...if the problem were already solved, there would be no point in contemplating possible solutions.
Look at the iPhone for example; there are lots of restrictions on what a user can accomplish and there is software therein to detect attempts to hack around these restrictions and break things like software update when you try and update a hacked phone. So your model only works in an architecture which supports it. Since Newspeak doesn't encompass a complete device architecture it can't provide an update schema for it. Expecting a language to address this is IMO a category error.
Didn't someone once say that the operating system is where you resolve all of the inadequacies of your language (or something to that effect)? ;)
When one starts talking about for example a Smalltalk system running on bare hardware (e.g. Squeak NOS) then the system can encompass the entirety. So we should first try and evolve Newspeak to NewspeakNOS and ten we can see how an evolved NewspeakNOS would address these update issues.
I don't think you need to go to such lengths to either show that such a scheme would work, or to get use out of it if it does. Regarding your iPhone example, it takes the discussion beyond the realm of that which we have control and into the realm of interfacing with legacy systems in which none of these kinds of issues have been considered or addressed. Take and example of upgrading the AddressBook on the iPhone platform (where it's a third party tool over which you have no control other than to upgrade it) while having it still work with say a custom Dialer app that you wrote. You are free to upgrade either of them independently so long as they both support a common interface. If the interface were version controlled, you can deal with this problem in a pretty rational manner for an end user (when you go to the AppStore and try to upgrade the AddressBook app, it could alert you that "this new version of the AddressBook no longer supports version 1 of the AddressBook API and since we see that you have a registered dependency form some Dialer app on version 1.0, we can pretty much guarantee that it's going to break your Dialer app, are you sure you want to do this?" You could then independently upgrade the Dialer app.
To bring this back to monkey patching...no, you cannot monkey patch the AddressBook API, but you can create a new version of some framework (with as many customizations as you desire) for use in your Dialer app while the, while the AddressBook continues to use some other version of that very same framework concurrently. Of course, Cocoa is not going to support this kind of thing (at least not today), but it doesn't preclude apps in some other language that has Newspeak style modularization from doing this (regardless of whether it's running on bare metal or not).
I think I've run out of steam on this topic for now.
Hi Stephen, apologies for apparently being obstructive. I was just trying to properly frame the discussion. I want to separate linguistic from system issues. Your first question was an excellent one and asked if linguistic mechanisms could resolve the issue and I said why I thought not. I didn't realise you were shifting ground to propose solutions and thought you were still discussing linguistic mechanisms. Apologies. Please don't run out of steam because I'm not going with the flow. I hope I get where you're aiming at now.
Could you extend your scenario with the potential for malware to attempt to patch the AddressBook and suggest ways one can legitimately patch the AddressBook and at the same time protect it from unwarranted modification?
Well, when you download the malware code, instantiate it and give it a handle to your AddressBook interface, as long as that interface doesn't let you do reflective things (like adding new methods) and doesn't let you do other destructive or undesirable things, then no matter how nefariously intentioned the malware is, it won't be able to do anything that you didn't explicitly give it the ability to do by exposing capability. This is what E is all about isn't it? And part of what Newspeak aims to address as well.
To legitimately patch the AddressBook, and do it in safe(ish) manner (I believe at some level, you have to have trust...after all, you trusted the original AddressBook code with your data) you would expose access to completely read (but not write) your address book objects...you would not expose access to the outside world...this new code would then migrate your data and setup the new address book code...you would then test out the new address book, and when, to you satisfaction it is handling you data in a responsible manner, you would expose any additional capabilities the address book needs (for example, send a vCard over bluetooth), then you would redirect your address book interfaces to the new code and data (you would need some means of forcing active clients off the old AddressBook implementation and onto the new).
I imagine much of the manual and careful steps above could be fully automated if you really trust the publisher of the code (so trust networks might be relevant). But, the you are still making a high level decision regarding to what degree you trust a given bit of code. In both cases, you are exposing only the capability that you expect is needed and you are holding back access to facilities that would enable the foreign code to do something it's not supposed to do.
(btw, you've alluded to some of Newspeak's synchronization plans, I'm not familiar with those (not sure if they've written extensively about those plans yet). So, they might have some good ideas around this stuff that I'm not bringing to this discussion because I haven't read up on it)
- Stephen
Eliot Miranda wrote:
On Tue, Jun 30, 2009 at 11:37 AM, Andreas Raab <andreas.raab@gmx.de mailto:andreas.raab@gmx.de> wrote:
... I really fail to see how it would make any difference whatsoever if extension methods are in multiple packages or not. In either case you are *completely* screwed if you have multiple packages trying to extend the same method. Seriously, has there ever been a situation where that actually works? (my personal preference is actually that MC should blow up straight into your face if you try to change a method that's in an extension category already, but that's just me - I generally avoid extensions like the plague)
I don't avoid extensions, and personally feel extensions are a core part of building frameworks. If objects are to interact they must provide protocol to support that interaction. In adding a new framework to an existing system this means extending existing objects so that they can interact with the new framework. Yes, one can do this badly, but there are lots of examples where this makes sense.
As far as patching, we've had this conversation before. I agree that a well-designed system will provide extension mechanisms that make patching unnecessary.
And later...
Gilad is virulently against monkey-patching and so there is no support for extensions in Newspeak modules...
Extension methods are useful, but can be dangerous. Problems will arise if a module modifies the behavior that is expected by the base system or by other modules that do not include us as a prerequisite.
Things a module should not be able to do to existing classes (defined in the base system or in other modules) - patching: modify existing methods, including any extensions done by other modules - add new methods that redefine inherited behavior (I mean, a method not already implemented but inherited) - delete any method
Things a module should be able to do: - extend existing classes with new methods that do not affect the base system or any other module that does not include us as a prerequisite
An easy way to ensure this is to require each module to define a prefix for its extension methods. The system must ensure that the prefixes are distinct for all modules, and that a prefix can be used only by its owning module to define new methods. The base system can not use any of those prefixes. A module can call a prefixed method only if it owns it, or it belongs to some other module that was declared as a prerequisite.
It is sort of cheap namespaces for methods (not for classes).
What do you think?
Cheers, Juan Vuletich
On Wed, Jul 1, 2009 at 9:08 AM, Juan Vuletich juan@jvuletich.org wrote:
Eliot Miranda wrote:
On Tue, Jun 30, 2009 at 11:37 AM, Andreas Raab <andreas.raab@gmx.demailto: andreas.raab@gmx.de> wrote:
... I really fail to see how it would make any difference whatsoever if extension methods are in multiple packages or not. In either case you are *completely* screwed if you have multiple packages trying to extend the same method. Seriously, has there ever been a situation where that actually works? (my personal preference is actually that MC should blow up straight into your face if you try to change a method that's in an extension category already, but that's just me - I generally avoid extensions like the plague)
I don't avoid extensions, and personally feel extensions are a core part of building frameworks. If objects are to interact they must provide protocol to support that interaction. In adding a new framework to an existing system this means extending existing objects so that they can interact with the new framework. Yes, one can do this badly, but there are lots of examples where this makes sense.
As far as patching, we've had this conversation before. I agree that a well-designed system will provide extension mechanisms that make patching unnecessary.
And later...
Gilad is virulently against monkey-patching and so there is no support for extensions in Newspeak modules...
Extension methods are useful, but can be dangerous. Problems will arise if a module modifies the behavior that is expected by the base system or by other modules that do not include us as a prerequisite.
Things a module should not be able to do to existing classes (defined in the base system or in other modules)
- patching: modify existing methods, including any extensions done by other
modules
- add new methods that redefine inherited behavior (I mean, a method not
already implemented but inherited)
- delete any method
Things a module should be able to do:
- extend existing classes with new methods that do not affect the base
system or any other module that does not include us as a prerequisite
An easy way to ensure this is to require each module to define a prefix for its extension methods. The system must ensure that the prefixes are distinct for all modules, and that a prefix can be used only by its owning module to define new methods. The base system can not use any of those prefixes. A module can call a prefixed method only if it owns it, or it belongs to some other module that was declared as a prerequisite.
It is sort of cheap namespaces for methods (not for classes).
What do you think?
Cheers, Juan Vuletich
In the absence of a modular system like Newspeak, this is probably the best proposal. It's probably even better than real selector namespaces, since I think a modular system like Newspeak makes even that irrelevant. I once implemented real selector namespaces, modifying the compiler, updating the browsers (you could see multiple methods of the same name in the browser with the namespace to which each was affiliated in parens), etc. It all worked and was even almost comprehensible, but I still was left with an icky feeling about it and abandoned the experiment. I still think a modular system makes this problem go away and short of that, your proposal is probably the most practical.
- Stephen
On Wed, Jul 1, 2009 at 6:08 AM, Juan Vuletich juan@jvuletich.org wrote:
Eliot Miranda wrote:
On Tue, Jun 30, 2009 at 11:37 AM, Andreas Raab <andreas.raab@gmx.demailto: andreas.raab@gmx.de> wrote:
... I really fail to see how it would make any difference whatsoever if extension methods are in multiple packages or not. In either case you are *completely* screwed if you have multiple packages trying to extend the same method. Seriously, has there ever been a situation where that actually works? (my personal preference is actually that MC should blow up straight into your face if you try to change a method that's in an extension category already, but that's just me - I generally avoid extensions like the plague)
I don't avoid extensions, and personally feel extensions are a core part of building frameworks. If objects are to interact they must provide protocol to support that interaction. In adding a new framework to an existing system this means extending existing objects so that they can interact with the new framework. Yes, one can do this badly, but there are lots of examples where this makes sense.
As far as patching, we've had this conversation before. I agree that a well-designed system will provide extension mechanisms that make patching unnecessary.
And later...
Gilad is virulently against monkey-patching and so there is no support for extensions in Newspeak modules...
Extension methods are useful, but can be dangerous. Problems will arise if a module modifies the behavior that is expected by the base system or by other modules that do not include us as a prerequisite.
Things a module should not be able to do to existing classes (defined in the base system or in other modules)
- patching: modify existing methods, including any extensions done by other
modules
- add new methods that redefine inherited behavior (I mean, a method not
already implemented but inherited)
- delete any method
Things a module should be able to do:
- extend existing classes with new methods that do not affect the base
system or any other module that does not include us as a prerequisite
An easy way to ensure this is to require each module to define a prefix for its extension methods. The system must ensure that the prefixes are distinct for all modules, and that a prefix can be used only by its owning module to define new methods. The base system can not use any of those prefixes. A module can call a prefixed method only if it owns it, or it belongs to some other module that was declared as a prerequisite.
It is sort of cheap namespaces for methods (not for classes).
What do you think?
Hi Juan, basically my experience leads me to disagree that one can't patch. I think one needs to patch. The difference is between the kind of package and how deep the change it needs to make is. if, like me, you're working on the compiler you *absolutely need* to patch existing base classes to install a new compiler unless the system was architected to have a new compiler in the first place. This is the tension. When the system is well-enough architected one can use pluggable schemes that have been provided to avoid base patches. But real systems don't have those facilities everywhere and only have those facilities in places where system designers have the foresight to provide them. But to modify a system by loading packages to get to a point where it is more pluggable you'll need to patch base classes. Chicken and egg. So I prefer the approach that provides absolute power (you can patch anything) and provides argumentation guidelines and examples that help people avoid using that absolute power unless they absolutely have to.
I think there's an analogy with become and thisContext. These two features are extremely powerful and can be horribly abused (along with MNU handlers :) ). But in practice they don't get horribly abused, except perhaps by people experimenting and learning. But think what the system would be like *without* become and thisContext. It would loose huge amounts of power. With power comes responsibility but without power there is impotence and apathy :)
Cheers, Juan Vuletich
Please reply under posts. It makes things less confusing.
On Jun 29, 2009, at 3:43 PM, David Goehrig wrote:
What I typically what I've been doing to eliminate all of these methods
with a single simple change:
Object doesNoUnderstand: aMessage ^ false
On Tue, Jun 30, 2009 at 3:26 PM, Cameron Sanders < csanders.personal@functional-analyst.com> wrote: What you write (down below) is close to what I was thinking when I said have all #isXXX return false by default; although I would test that the first two characters match 'i' and 's' and that more characters exist, if so, return false, otherwise, normal #doesNotUnderstand: behavior.
I would treat #is by itself differently... it is ... or it couldn't be tested! So I would do nothing for simple #is. and I don't like #is: because it looks like a class type test... but that is part of the point, eh?
I'll have to go back to the original example (by siguctua@gmail.com, and read more about lambdas) but I thought that CVLambda would implement #isCVLambda to return true when it can be verified to be one. The example did not illustrate #doesNotUnderstand:.
Back to the question of adding behavior to classes that you don't own. VisualWorks has a means to extend a class in a different package ... as I recall. As I recall, squeak has no such capability, right?
Squeak certainly has that capability, but I would discourage you from using it.
Changing the implementation of doesNotUnderstand: is a code smell. You're starting to play with voodoo and you need a *very* good reason to do it. Otherwise you'll have made yourself a very difficult application to debug.
In my opinion (which doubtless isn't shared by everyone here), base classes such as Object should not have a lot of >>isXXX methods. >>isBoolean and
isString are okay (although I never use them), but >>isMorph or >>isColor
or anything not related to the Kernel classes are pollution.
If you're using an >>isXXX method, then your code probably needs refactoring. It means you're implementing class specific behaviour using conditional >>ifTrue: blocks. Typically, your code will be working with subclasses of a specific class, and you'd invoke polymorphic methods to get class specific behaviour rather than using conditional blocks such as
ifTrue:, >>ifFalse: etc.
If your code really does handle an object of any possible class (which is rare but does occur), then you can use >>isKindOf:, >>isMemberOf: and
respondsTo:.
Igor's lambda example was a bit complex for me to work through so I can't really comment on it.
Gulik.
2009/6/30 Michael van der Gulik mikevdg@gmail.com:
Please reply under posts. It makes things less confusing.
On Jun 29, 2009, at 3:43 PM, David Goehrig wrote:
What I typically what I've been doing to eliminate all of these methods with a single simple change:
Object doesNoUnderstand: aMessage ^ false
On Tue, Jun 30, 2009 at 3:26 PM, Cameron Sanders csanders.personal@functional-analyst.com wrote: What you write (down below) is close to what I was thinking when I said have all #isXXX return false by default; although I would test that the first two characters match 'i' and 's' and that more characters exist, if so, return false, otherwise, normal #doesNotUnderstand: behavior.
I would treat #is by itself differently... it is ... or it couldn't be tested! So I would do nothing for simple #is. and I don't like #is: because it looks like a class type test... but that is part of the point, eh?
I'll have to go back to the original example (by siguctua@gmail.com, and read more about lambdas) but I thought that CVLambda would implement #isCVLambda to return true when it can be verified to be one. The example did not illustrate #doesNotUnderstand:.
Back to the question of adding behavior to classes that you don't own. VisualWorks has a means to extend a class in a different package ... as I recall. As I recall, squeak has no such capability, right?
Squeak certainly has that capability, but I would discourage you from using it.
Changing the implementation of doesNotUnderstand: is a code smell. You're starting to play with voodoo and you need a *very* good reason to do it. Otherwise you'll have made yourself a very difficult application to debug.
In my opinion (which doubtless isn't shared by everyone here), base classes such as Object should not have a lot of >>isXXX methods. >>isBoolean and
isString are okay (although I never use them), but >>isMorph or >>isColor
or anything not related to the Kernel classes are pollution.
If you're using an >>isXXX method, then your code probably needs refactoring. It means you're implementing class specific behaviour using conditional >>ifTrue: blocks. Typically, your code will be working with subclasses of a specific class, and you'd invoke polymorphic methods to get class specific behaviour rather than using conditional blocks such as
ifTrue:, >>ifFalse: etc.
If your code really does handle an object of any possible class (which is rare but does occur), then you can use >>isKindOf:, >>isMemberOf: and
respondsTo:.
Igor's lambda example was a bit complex for me to work through so I can't really comment on it.
Your suggestion is correct , except from situations like with my lambdas:
sometimes you need to differentiate a single kind of objects from the rest of objects in universe. isKindOf: doesn't helps , because then if we go that road , we're soon will need isKindOf:orOf:
simply because different objects could have same trait but belong to different class hierarchies.
Concerning #respondsTo: - its also smells a bit, because you are depending not on a specific behavior (by invoking some method) but rather testing the class method dictionary, which is mostly static. This approach (use of #respondsTo:) also doesn't fits well with proxies (and we need them sometimes).
Gulik.
You're right. And I have a similar situation in some of my code right now, which is why i piped up earlier. I have two or three examples in my code. The first is a container (financial series) that holds Numbers or something else. Under mathematical operations, my financial series will operate on the numbers only, and simply replace anything else with a FaNullDatum (singleton) -- FaNullDatum has its own printing/display methods, and it knows nothing about mathematical operations.
A second example in my code addresses what is mentioned below. But for this aspect, I know that all of the candidate objects were defined under a certain tree that implements a #canCompute: method, or that objects wishing to be substituted will implement the method. That way I don't have to worry about how it is implemented (e.g. #doesNotUnderstand:) and whether it will be in the method dictionary.
So... obviously, "existing" objects can be substituted... but now I am re-thinking the generality of these objects. Perhaps I can loosen this constraint -- even though I am not convinced it would ever matter for my situation. (Mine are FinancialAnalyst objects that know how to operate on my set of financial data keys.)
... i need to look into Traits.
Ciao, Cam
On Jun 30, 2009, at 12:46 AM, Igor Stasenko wrote:
Concerning #respondsTo: - its also smells a bit, because you are depending not on a specific behavior (by invoking some method) but rather testing the class method dictionary, which is mostly static. This approach (use of #respondsTo:) also doesn't fits well with proxies (and we need them sometimes).
correction... with apologies:
The word "not" is missing from the first line of the second paragraph illustrated below.
On Jun 30, 2009, at 1:28 AM, Cameron Sanders wrote:
A second example in my code addresses what is mentioned below. But for this aspect, I know that all of the candidate objects were defined under a certain tree that implements a #canCompute: method, or that objects wishing to be substituted will implement the method. That way I don't have to worry about how it is implemented (e.g. #doesNotUnderstand:) and whether it will be in the method dictionary.
So... obviously, "existing" objects can be substituted... but now I am re-thinking the generality of these objects. Perhaps I can loosen this constraint -- even though I am not convinced it would ever matter for my situation. (Mine are FinancialAnalyst objects that know how to operate on my set of financial data keys.)
On Jun 30, 2009, at 12:22 AM, Michael van der Gulik wrote:
Back to the question of adding behavior to classes that you don't own. VisualWorks has a means to extend a class in a different package ... as I recall. As I recall, squeak has no such capability, right?
Squeak certainly has that capability, but I would discourage you from using it.
Why?
Changing the implementation of doesNotUnderstand: is a code smell. You're starting to play with voodoo and you need a *very* good reason to do it. Otherwise you'll have made yourself a very difficult application to debug.
I have done it a fair amount for mapping symbols in broker objects... so that symbols may look like unary messages. It is slower than simply invoking some keyword message directly, but it really gives the right feel for some objects. e.g. ResultsSet.
And you are right, it will likely lead to a debugging nightmare. In some cases I have built symbol stacks to aid in debugging -- i'm tired, this is hurried.... my apologies if it is unclear. (Besides, I doubt you care...)
In my opinion (which doubtless isn't shared by everyone here), base classes such as Object should not have a lot of >>isXXX methods.
isBoolean and >>isString are okay (although I never use them), but isMorph or >>isColor or anything not related to the Kernel classes
are pollution.
I agree with you here too.
If you're using an >>isXXX method, then your code probably needs refactoring. It means you're implementing class specific behaviour using conditional >>ifTrue: blocks. Typically, your code will be working with subclasses of a specific class, and you'd invoke polymorphic methods to get class specific behaviour rather than using conditional blocks such as >>ifTrue:, >>ifFalse: etc.
Some specialized containers may wish to process some things differently than others. But I agree that OO code should not frequently be branching on type-tests.
Cheers, Cam
2009/6/29 Cameron Sanders csanders.personal@functional-analyst.com:
I didn't read your example closely, but, generally....
(obj respondsTo: #isCVLambda) and: [ obj isCVLambda ]
can't do that for lambdas, because it traps all DNU's and in this case a send of #respondsTo: (or any other unknown message) will be converted into a lambda message send instead of answering expected result. Because:
lambda := LambdaSlot id: #x. lambda foo ==> lambda( <x> #foo)
is very general (independent of hierarchy), or if you want to trap people on a class tree...
obj isKindOf: CVLambda
sorry, but this even worse than using #isXXX
or trap exceptions for when something doesn't respond to isCVLambda ...
or...
-- BUT, it will be slower.
an intriguing idea is having Object always respond to any pattern isXXX with false, unless the method is explicitly defined. (Now there I go, changing the root behavior...)
Exactly :)
Maybe it worth add and standardize the #is: message for Object ? Which by default can be implemented as:
Object is: object ^ self == object
and if you need to add the differentiation, then could be something like:
Object is: object ^ object == #lambda or: [ super is: object ]
-Cam
On Jun 29, 2009, at 1:18 PM, Igor Stasenko wrote:
so, here the question, can i implement the same behavior w/o using #isCVLambda , and
2009/6/30 Igor Stasenko siguctua@gmail.com:
2009/6/29 Cameron Sanders csanders.personal@functional-analyst.com:
I didn't read your example closely, but, generally....
(obj respondsTo: #isCVLambda) and: [ obj isCVLambda ]
can't do that for lambdas, because it traps all DNU's and in this case a send of #respondsTo: (or any other unknown message) will be converted into a lambda message send instead of answering expected result. Because:
lambda := LambdaSlot id: #x. lambda foo ==> lambda( <x> #foo)
is very general (independent of hierarchy), or if you want to trap people on a class tree...
obj isKindOf: CVLambda
sorry, but this even worse than using #isXXX
or trap exceptions for when something doesn't respond to isCVLambda ...
or...
-- BUT, it will be slower.
an intriguing idea is having Object always respond to any pattern isXXX with false, unless the method is explicitly defined. (Now there I go, changing the root behavior...)
Exactly :)
Maybe it worth add and standardize the #is: message for Object ? Which by default can be implemented as:
Object is: object ^ self == object
and if you need to add the differentiation, then could be something like:
Object is: object ^ object == #lambda or: [ super is: object ]
Sorry.. it can be confusing, because i wrote it not very clear:
Object>>is: object ^ self == object
SomeOtherThing>>is: object ^ object == #otherThing or: [ super is: object]
-Cam
On Jun 29, 2009, at 1:18 PM, Igor Stasenko wrote:
so, here the question, can i implement the same behavior w/o using #isCVLambda , and
-- Best regards, Igor Stasenko AKA sig.
is very general (independent of hierarchy), or if you want to trap people on a class tree...
obj isKindOf: CVLambda
sorry, but this even worse than using #isXXX
Hence my preference for askFor:
http://bugs.squeak.org/view.php?id=5319
anObject askFor: #isRectangle
where askFor: returns the value if it is defined and false otherwise.
Keith
This class represents a lambda-message-send, i.e. it keeps all what a message send needs: receiver, selector, arguments. any of its slots can be a free variable or, in own turn, be another lambda and so on, without any limitations.
are you aware of this: http://www.zogotounga.net/comp/squeak/functionaltalk.htm
... one of my mostly unnoticed contribution to Squeak :)
Stef
2009/6/29 Stéphane Rollandin lecteur@zogotounga.net:
This class represents a lambda-message-send, i.e. it keeps all what a message send needs: receiver, selector, arguments. any of its slots can be a free variable or, in own turn, be another lambda and so on, without any limitations.
are you aware of this: http://www.zogotounga.net/comp/squeak/functionaltalk.htm
... one of my mostly unnoticed contribution to Squeak :)
Of course i am. And your package inspired me to use lambdas in own project. But i did own. Because i don't need so much generality as in your implementation. And i wanted it to behave a little differently. Don't want to go in details right now.
Stef
squeak-dev@lists.squeakfoundation.org