[Glass] [Pharo-dev] printString and asString

Carlo snoobabk at gmail.com
Fri Jan 3 03:13:18 PST 2014


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 <otto at finworks.biz> 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 <otto at finworks.biz> 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




More information about the Glass mailing list