[Glass] printString and asString

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|

[Glass] printString and asString

otto
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
Reply | Threaded
Open this post in threaded view
|

Re: [Glass] [Pharo-dev] printString and asString

Carlo-2
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
Reply | Threaded
Open this post in threaded view
|

Re: [Glass] [Pharo-dev] printString and asString

otto
> #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
Reply | Threaded
Open this post in threaded view
|

Re: [Glass] [Pharo-dev] printString and asString

Carlo-2
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
Reply | Threaded
Open this post in threaded view
|

Re: [Glass] [Pharo-dev] printString and asString

otto
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
Reply | Threaded
Open this post in threaded view
|

Re: [Glass] printString and asString

Martin McClure-5
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
Reply | Threaded
Open this post in threaded view
|

Re: [Glass] printString and asString

ccrraaiigg

     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
Reply | Threaded
Open this post in threaded view
|

Re: [Glass] printString and asString

Richard Sargent
Administrator
In reply to this post by Martin McClure-5
I agree with everything Martin wrote. He nailed it 100%.


Martin McClure-5 wrote
#printString and #printOn:
These should answer a human-readable description of the receiver.
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.


#asString
There should not be an Object>>asString. This message is *only* for converting string-like things to actual strings.
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.


#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.
What Martin says is correct, but one might want to override the default implementation to produce more readable and more succinct representations.

Reply | Threaded
Open this post in threaded view
|

Re: [Glass] [Pharo-dev] printString and asString

otto
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
Reply | Threaded
Open this post in threaded view
|

Re: [Glass] [Pharo-dev] printString and asString

Martin McClure-5
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