Hi,
I've run into one of my favorite problems in Smalltalk and decided to try the list. Please don't shoot me on this one; perhaps you've run into it yourself. If there are discussions that I should read, please send me some info. My first assumption is that the difference between printString and asString is that printString may be more elaborate than asString, but often they are the same. Do you agree? 1. In Pharo, the default implementation of asString is ^ self printString. 2. printString normally calls self printOn: aStream. (Pharo and GemStone. Yay, consistency!) 3. In Pharo, the default printOn: writes the class name prefixed with 'a' or 'an'. 4. In GemStone, the default implementation of printOn: writes self asString to the stream. My second assumption is one should never override printString. Rather override printOn:. Agreed? In Pharo, we have very few classes that overrides printString. One problem area is UUID. It overrides asString, printOn: and printString, with a similar approach to GemStone in printOn:. In GemStone, there's a big heap of inconsistency. Character also overrides all 3 methods, with an apparent awful inconsistency there! In Pharo, I get infinite recursion when I override printOn: to call asString. I get infinite recursion problems in GemStone when I override asString and call printString. So with my strategy to develop in Pharo and deploy in GemStone, we have to be very careful. Most often, overriding printString and / or asString has to do with performance. If we have a simple object, we don't want to override printOn: that creates a stream, prints an instanceVariable (already a string) to it, and then throw away the stream. My suggestion is that we should get consistency. Decide on a way and follow that. To me, this makes sense: 1. Keep the default printString that calls printOn:. 2. Override asString to return a simple representation of the object, if you have one. Don't create streams in here, or go wild with string concatenations. Practically, many objects will return a simple string, which could be one instVar. 3. Override printOn: to return a representation concatenating parts of the object as strings. Use this if you want to print a more elaborate description than asString. 4. Change Object >> asString return 'a' or 'an', class name. 5. Change Object >> printOn: to write self asString to the stream. 6. Fix printString overrides (more work in GemStone than Pharo) 7. Normally use printString, but use asString where you are concerned about performance. In some cases, you may have a longer printString... Well, thanks for reading this far. Looking forward to your answers. Otto _______________________________________________ Glass mailing list [hidden email] http://lists.gemtalksystems.com/mailman/listinfo/glass |
Hi
The summary version of my view below: #printString and #printOn: protocol should be used for printing and #asString for conversion. This implies that #asString should not depend on #print protocol. On 03 Jan 2014, at 10:33 AM, Otto Behrens <[hidden email]> wrote: > My first assumption is that the difference between printString and > asString is that printString may be more elaborate than asString, but > often they are the same. Do you agree? From my experience I’d tend to agree. #asString seems to be replacing #displayString in certain scenarios (I prefer #asString to be used as more of a conversion method as per Smalltalk Best Practice Patterns) > My second assumption is one should never override printString. Rather > override printOn:. Agreed? Agreed. > In Pharo, I get infinite recursion when I override printOn: to call > asString. I get infinite recursion problems in GemStone when I > override asString and call printString. So with my strategy to develop > in Pharo and deploy in GemStone, we have to be very careful. If we are going to use #asString to mimic #displayString (instead of as a conversion method) then to me #asString should be viewed at a higher level abstraction than #printOn:. If not then we can run into these issues when calling the higher-level abstraction from a lower level in the same objectt, as you mentioned. Calling #printString from #asString from an API perspective should not cause issues (as displaying something is very different from converting something) so I’d say this is an API design problem. > Most often, overriding printString and / or asString has to do with > performance. If we have a simple object, we don't want to override > printOn: that creates a stream, prints an instanceVariable (already a > string) to it, and then throw away the stream. Agreed on the performance reasoning for #printString. For me the issue with #asString is that we are using it as a substitute for #displayString (instead of a conversion method) but without the underlying support protocol (i.e. #displayOn:). This means any performance related overrides for #asString can cause some confusion for other developers. i.e. When overriding #asString what base selector can you rely on, something along the lines of #displayOn: protocol or the #basicXYZ family of protocols where you are stating that this is the lowest level API and must not be overridden. > 1. Keep the default printString that calls printOn:. > 2. Override asString to return a simple representation of the object, > if you have one. Don't create streams in here, or go wild with string > concatenations. Practically, many objects will return a simple string, > which could be one instVar. Agreed. Although creating a stream and calling #printOn: should be safe to do and not cause issues, although not recommended. > 3. Override printOn: to return a representation concatenating parts of > the object as strings. Use this if you want to print a more elaborate > description than asString. It should read, if you want to print a more elaborate description than default #printOn:. #asString should not have any conceptual relation to #printOn:. > 4. Change Object >> asString return ‘a' or 'an', class name. Agreed, but this should be implemented by delegating to #printOn: or by creating a base method akin to #basicDisplayString so there is always some base display string functionality that subtypes can rely on. (Notice that naming the method with #basicAsString doesn’t really make sense which again tends to validate that we are using #asString not for conversion but more for display purposes, as in #displayString which has been deprecated via grease). > 5. Change Object >> printOn: to write self asString to the stream. No, I don’t think so as per above. #asString could depend on #printOn: but not the other way around. > 6. Fix printString overrides (more work in GemStone than Pharo) #printString should depend on #printOn:. If overriding for performance then it must use simple representations wherever possible or #asString. > 7. Normally use printString, but use asString where you are concerned > about performance. In some cases, you may have a longer printString… Use #printString always as developers would override #printString for performance if need be. Use #asString for conversion. My last statement again raises the point that #asString should be used for conversion and not for displaying or printing. We should only have a handful of #asString methods in an image but multiple implementations of #displayOn: and #printOn: These are my personal (limited) views as I only use Pharo for personal projects. Kind Regards Carlo On 03 Jan 2014, at 10:33 AM, Otto Behrens <[hidden email]> wrote: Hi, I've run into one of my favorite problems in Smalltalk and decided to try the list. Please don't shoot me on this one; perhaps you've run into it yourself. If there are discussions that I should read, please send me some info. My first assumption is that the difference between printString and asString is that printString may be more elaborate than asString, but often they are the same. Do you agree? 1. In Pharo, the default implementation of asString is ^ self printString. 2. printString normally calls self printOn: aStream. (Pharo and GemStone. Yay, consistency!) 3. In Pharo, the default printOn: writes the class name prefixed with 'a' or 'an'. 4. In GemStone, the default implementation of printOn: writes self asString to the stream. My second assumption is one should never override printString. Rather override printOn:. Agreed? In Pharo, we have very few classes that overrides printString. One problem area is UUID. It overrides asString, printOn: and printString, with a similar approach to GemStone in printOn:. In GemStone, there's a big heap of inconsistency. Character also overrides all 3 methods, with an apparent awful inconsistency there! In Pharo, I get infinite recursion when I override printOn: to call asString. I get infinite recursion problems in GemStone when I override asString and call printString. So with my strategy to develop in Pharo and deploy in GemStone, we have to be very careful. Most often, overriding printString and / or asString has to do with performance. If we have a simple object, we don't want to override printOn: that creates a stream, prints an instanceVariable (already a string) to it, and then throw away the stream. My suggestion is that we should get consistency. Decide on a way and follow that. To me, this makes sense: 1. Keep the default printString that calls printOn:. 2. Override asString to return a simple representation of the object, if you have one. Don't create streams in here, or go wild with string concatenations. Practically, many objects will return a simple string, which could be one instVar. 3. Override printOn: to return a representation concatenating parts of the object as strings. Use this if you want to print a more elaborate description than asString. 4. Change Object >> asString return 'a' or 'an', class name. 5. Change Object >> printOn: to write self asString to the stream. 6. Fix printString overrides (more work in GemStone than Pharo) 7. Normally use printString, but use asString where you are concerned about performance. In some cases, you may have a longer printString... Well, thanks for reading this far. Looking forward to your answers. Otto _______________________________________________ Glass mailing list [hidden email] http://lists.gemtalksystems.com/mailman/listinfo/glass |
> #printString and #printOn: protocol should be used for printing and #asString for conversion. This implies that #asString should not depend on #print protocol.
Ah, I used it the other way around: printOn depends on asString. I saw 'conversion' as the lower level. Why do you distinguish here between conversion and 'printing'? Especially if displayString is being replaced with asString - to me that sounds against your thinking in principle. > If we are going to use #asString to mimic #displayString (instead of as a conversion method) then to me #asString should be viewed at a higher level abstraction than #printOn:. If not then we can run into these issues when calling the higher-level abstraction from a lower level in the same objectt, as you mentioned. Calling #printString from #asString from an API perspective should not cause issues (as displaying something is very different from converting something) so I’d say this is an API design problem. Yes, I think the API could be clearer. So this becomes the essence of the question here: What is the meaning of these methods from an API perspective? Thanks for the response _______________________________________________ Glass mailing list [hidden email] http://lists.gemtalksystems.com/mailman/listinfo/glass |
To me #asString is meant to be used when you need an object with the String protocol.
As mentioned it looks like the seaside #asString and #greaseString are meant for displaying which causes the confusion but I’m not sure why #displayString was removed apart from the obvious reasons of trying to cater for common denominator across Smalltalk dialects. In my codebases I also tend to use #asString for displaying but have tried to move to a #displayString and #displayOn: protocol for these purposes. Regards Carlo On 03 Jan 2014, at 1:24 PM, Otto Behrens <[hidden email]> wrote: > #printString and #printOn: protocol should be used for printing and #asString for conversion. This implies that #asString should not depend on #print protocol. Ah, I used it the other way around: printOn depends on asString. I saw 'conversion' as the lower level. Why do you distinguish here between conversion and 'printing'? Especially if displayString is being replaced with asString - to me that sounds against your thinking in principle. > If we are going to use #asString to mimic #displayString (instead of as a conversion method) then to me #asString should be viewed at a higher level abstraction than #printOn:. If not then we can run into these issues when calling the higher-level abstraction from a lower level in the same objectt, as you mentioned. Calling #printString from #asString from an API perspective should not cause issues (as displaying something is very different from converting something) so I’d say this is an API design problem. Yes, I think the API could be clearer. So this becomes the essence of the question here: What is the meaning of these methods from an API perspective? Thanks for the response _______________________________________________ Glass mailing list [hidden email] http://lists.gemtalksystems.com/mailman/listinfo/glass |
In reply to this post by otto
> #printString is used for debugging, that's why the default
> implementation shows the class name. It's implemented using #printOn: Makes sense. So unless there's a nice symbol that one can add to show what it is (eg #foo printString = '#foo'), implementors of printOn: should call super printOn:, yes? In Pharo, this seems to be the general idea. But then printString should not be used (generally) as the mechanism to display something to for a user, right? As this would show something like 'a Person Otto' to the user. Some implementors of printOn: could be renamed to storeOn: In GemStone, the default implementation of asString is to show the class name. > #displayString (deprecated in Pharo*) is used to display the object in > the UI. It is implemented by means of #displayOn: (Dolphin used this > pattern) So we don't have this in Pharo anymore. Looks like it is mostly asString now. But then, really, Object asString ^ self printString does not make sense? > #asString is the toString() equivalent of other languages. In some > cases `anObject asString asObject` should give the same object back > (e.g. in Date, Numbers, etc.). Most of the times it is the same string > as #printString, but it is not consistent. The intention of #storeString implemented via #storeOn: is to do that: (Compiler evaluate: anObject storeString) = anObject. Probably not completely true for really complex objects... > Ej: > If you have aPerson: > aPerson printString -> "aPerson ('John Doe')" > aPerson displayString -> 'John Doe' Ok, since displayString is gone, is it: aPerson asString -> 'John Doe' aPerson storeString -> "Person name: 'John Doe'" Which means that by default, both printString and storeString can call asString, and asString is the 'lowest level'? _______________________________________________ Glass mailing list [hidden email] http://lists.gemtalksystems.com/mailman/listinfo/glass |
In reply to this post by otto
On 01/03/2014 12:33 AM, Otto Behrens wrote:
> Hi, > > I've run into one of my favorite problems in Smalltalk and decided to > try the list. Please don't shoot me on this one; perhaps you've run > into it yourself. If there are discussions that I should read, please > send me some info. Hi Otto, Well, you asked for comments. This is my personal opinion, which varies quite a bit from most implementations, since it's a bit 'old-school Smalltalk'. But I think the scheme below does fairly neatly distinguish the purposes of the various messages, which have gotten muddied over the years as new developers use the language somewhat differently. But maybe it was never the way I think I remember it being. :-) #printString and #printOn: These should answer a human-readable description of the receiver. The description does not need to include the class name, though it can. All objects should respond to these messages. Generally, #printString should be implemented in terms of #printOn: -- if you're writing to a stream you may not want to pay the cost of creating an extra string. #printString is the equivalent of other languages' toString(). #asString There should not be an Object>>asString. This message is *only* for converting string-like things to actual strings. So it would be implemented in String (to return self) and in Symbol, and *maybe* in things like ByteArray (where it does not *describe* the byte array, but answers a string whose characters have the values of the bytes). #storeString Produces legal Smalltalk source code that when executed will produce an equivalent object to the receiver. Needs to be implemented only for objects that can be specified as literals in Smalltalk syntax, but could probably be implemented for a few other simple objects. #displayString and so on. UI frameworks and applications will need to have their own ways of textually representing objects, so will add their own messages such as #displayString. The *only* messages that any application or framework can depend on *all* objects responding to are those that it defines itself, and #printString and #printOn:. Regards, -Martin _______________________________________________ Glass mailing list [hidden email] http://lists.gemtalksystems.com/mailman/listinfo/glass |
Hear, hear. :) -C -- Craig Latta www.netjam.org/resume +1 510 984 8117 (Skype rings this until 31 January 2014) _______________________________________________ Glass mailing list [hidden email] http://lists.gemtalksystems.com/mailman/listinfo/glass |
Administrator
|
In reply to this post by Martin McClure-5
I agree with everything Martin wrote. He nailed it 100%.
Human-readable does not necessarily mean appropriate for an end user. In fact, it doesn't even guarantee the emitter's class can recreate an equivalent object from the print string. This generalizes to families of objects, such as numbers allowing conversion between different number representations. Again, conversions are not guaranteed to be reversible. (Float to Integer, for example.) Like Martin, I don't agree with the notion of #asString applying to every object. What Martin says is correct, but one might want to override the default implementation to produce more readable and more succinct representations. |
In reply to this post by otto
Thank you everyone for your comments.
I agree with Martin's comments and Stef's most recent one. Some of the messages may not have reached the other lists as this was actually posted to glass and pharo. Is this the right conclusion of what we're going to do now? - On Pharo and GemStone, we will remove the asString behaviour from Object. Perhaps this is just an item on the list to deprecate and gradually remove. Practically, a lot of people would not be using asString in the best way, right? - Subclasses that implement asString inappropriately (i.e. non CharacterCollection like things) - printString and asString will then become independent, mostly (especially on Object) - printString would generally be used to display a 'debugging' view of an object, to be used for example in the inspector. This brings back a question regarding displayString, or something similar. It seems as if many people need something like this. I need for example a string representation of objects generally to display in anchors in our seaside application. We have a class DomainObject from which most of our classes inherit, so this is probably the place to add a 'displayString' for something like this. Thanks Otto On Sat, Jan 4, 2014 at 1:47 PM, Stéphane Ducasse <[hidden email]> wrote: > > And number, no? For me as* methods are for conversion between types. E.i. if > you have string and want it as Integer you use asInteger. > > > no the point is exactly that :) > conversion between polymorphic objects > > asFloat > asInteger > asFraction > > > > asString > asSymbol > asByteArray > > asArray > asSet > > not defined in Object :) > > > > Uko > > > #storeString > Produces legal Smalltalk source code that when executed will produce an > equivalent object to the receiver. Needs to be implemented only for > objects that can be specified as literals in Smalltalk syntax, but could > probably be implemented for a few other simple objects. > > #displayString and so on. > UI frameworks and applications will need to have their own ways of > textually representing objects, so will add their own messages such as > #displayString. The *only* messages that any application or framework > can depend on *all* objects responding to are those that it defines > itself, and #printString and #printOn:. > > Regards, > > -Martin > > Glass mailing list [hidden email] http://lists.gemtalksystems.com/mailman/listinfo/glass |
On 01/05/2014 11:54 PM, Otto Behrens wrote:
> Thank you everyone for your comments. > > I agree with Martin's comments and Stef's most recent one. Some of the > messages may not have reached the other lists as this was actually > posted to glass and pharo. > > Is this the right conclusion of what we're going to do now? > > - On Pharo and GemStone, we will remove the asString behaviour from > Object. Perhaps this is just an item on the list to deprecate and > gradually remove. Practically, a lot of people would not be using > asString in the best way, right? I've filed a request to deprecate those uses of #asString in GemStone/S. I don't know what version that might take effect in (it's too late to get that into 3.2) but in the meantime avoiding use of #asString for non-string-like objects is a good pattern. > > This brings back a question regarding displayString, or something > similar. It seems as if many people need something like this. > > I need for example a string representation of objects generally to > display in anchors in our seaside application. We have a class > DomainObject from which most of our classes inherit, so this is > probably the place to add a 'displayString' for something like this. This sounds good to me. Regards, -Martin _______________________________________________ Glass mailing list [hidden email] http://lists.gemtalksystems.com/mailman/listinfo/glass |
Free forum by Nabble | Edit this page |