<p>For example:</p>
<ul>
<li>Java uses a <code>toString()</code> method, and calls that behind the scenes. If you don't implement one, you inherit it from the base class (<code>Object</code>).</li>
<li>JavaScript uses the ToPrimitive abstract operation, which involves @@toPrimitive, <strong>&quot;toString&quot;</strong>, and <strong>&quot;valueOf&quot;</strong>. You can read the spcification <a href="https://tc39.es/ecma262/multipage/abstract-operations.html#sec-toprimitive" rel="noreferrer">here</a>. Again, if you don't implement any of these, you inherit one from Object.</li>
<li>Swift uses three different protocols, which you can read about <a href="https://developer.apple.com/documentation/swift/string/init(describing:)-67ncf" rel="noreferrer">here</a>. If you don't implement any of them, it uses reflection to generate a sensible default.</li>
</ul>
<p>What are some advantages and disadvantages to these methods, and what other methods exist?</p>

<strings><string-interpolation>
What ways exist for handling object to string conversion?
<h1>Rust's approach: <code>Display</code> and <code>Debug</code> traits</h1>
<p>Rust uses the trait system, through the <a href="https://doc.rust-lang.org/std/fmt/trait.Display.html" rel="noreferrer"><code>Display</code></a> and <a href="https://doc.rust-lang.org/std/fmt/trait.Debug.html" rel="noreferrer"><code>Debug</code></a> traits, to model types that may be converted to strings, with two possible and well-known uses (user-facing and developer-facing, respectively).</p>
<p>However, it statically ensures that values of a type that doesn't implement the <code>Display</code> trait may not be used to construct a string representation; the same for <code>Debug</code>. Hence, Rust doesn't have a <em>default</em> string representation for user-defined types.</p>
<p>Even though there is not a default string representation, the language offers a macro to derive the <code>Debug</code> implementation using a sensible default that follows the type's definition. However, the <code>Display</code> trait may <em>not</em> be derived, which forces the developer to implement the <em>user-facing</em> string conversion explicitly.</p>
<hr />
<p>Haskell has a similar mechanism via the <code>Show</code> type class, which may be derived.</p>

CC BY-SA 4.0
2023-05-17T22:51:39.450Z
2023-05-17T22:51:39.450Z
Luiz Felipe
<p>Another option is to simply forbid string coercion, as e.g. Python does. This makes it the programmer's responsibility to explicitly convert other values to strings where necessary, but also means there is no uniquely privileged way to convert to a string.</p>
<p>In Python for example, there are the <code>str</code> and <code>repr</code> functions which convert values to strings in different ways (and which can have user-defined behaviour via the <code>__str__</code> and <code>__repr__</code> dunder methods), but you can also convert to string through other means, such as by calling some other method defined by the object's class, or using string formatting or f-strings which may offer more options for how the conversion is done, such as <code>f'{x:.02f}'</code>.</p>

CC BY-SA 4.0
2023-05-18T04:15:29.987Z
2023-05-18T04:15:29.987Z
kaya3
<p>APL specifies a default display form for every array (all values are arrays). Dyalog APL augments this in two ways:</p>
<ol>
<li><p>Objects (classes, instances, and namespaces) have a built-in method that takes a character array which then overrides the default form. An object can also set its own display form.<pre>      a←⎕NS⍬  ⍝ new namespace
      a  ⍝ will show default display form
#.[Namespace]
      a.⎕DF'{myThing}'  ⍝ change it
      a
{myThing}
</pre></p>
</li>
<li><p>There is a printing event to which one can set a handler that in turn can do anything it wants based on the array that needs to be printed. The system comes with a bunch of ready-made handlers for various customisable effects.<pre>      MyPrinting←{⎕←⌽⍺}  ⍝ output normal display form, but reversed
      ⎕SE.onSessionPrint←'MyPrinting'  ⍝ assign event handler
      'Hello World'
dlroW olleH
</pre></p>
</li>
</ol>

CC BY-SA 4.0
2023-05-18T05:36:12.890Z
2023-08-26T20:40:44.767Z
2023-08-26T20:40:44.767Z
Adám
<p>C++ <a href="https://en.cppreference.com/w/cpp/io" rel="nofollow noreferrer">ostream/strstream</a>.</p>
<p>Using a C++-style stream approach is the only way I know for a language that allows users to define types that cannot implement interfaces, like myInt256, but allows these types to take part in the conversion API.</p>
<p>Splitting the stream API from the StringBuffer implementation allows moving the StringBuffer implementation to an optional module. The stream API itself must reside in the core part of the standard library if core types should provide implementations for it and extending types from other modules is not allowed.</p>
<p>A property that I consider neither pro or con is that the Stream API can have a <a href="https://en.cppreference.com/w/cpp/io/ios_base/flags" rel="nofollow noreferrer">state</a> telling inserted entities a desired format.</p>

CC BY-SA 4.0
2023-08-27T09:07:37.850Z
2023-08-27T09:07:37.850Z
feldentm
<p>C++ uses character-stream operators (<code>&lt;&lt;</code> and <code>&gt;&gt;</code>) for such conversions.</p>
<p>One advantage is that overloads can provide different representations for different kinds of stream (e.g. we might want a <code>debug_stream</code> class to be verbose, whereas a <code>datastore_stream</code> could be compact but must have full round-trip fidelity).</p>
<p>The disadvantage is that you might end up having to implement the overloads yourself, for libraries without the overloads you need for your stream types.  I often see code that provides streaming operators for plain-<code>char</code> streams, but not for <code>wchar_t</code> streams, as a common example.</p>

CC BY-SA 4.0
2023-09-01T09:28:50.410Z
2023-09-01T09:28:50.410Z
Toby Speight
<p>In Common Lisp, there are different ways of printing an object depending on what you want. Let's define a custom class first:</p>
<pre><code>USER&gt; (defclass abc () ((x :initarg :x :reader abc-x)))
#&lt;STANDARD-CLASS USER::ABC&gt;
</code></pre>
<p>The system can print any object, and converting to a string is done by printing to a string stream. Typically you call <a href="http://clhs.lisp.se/Body/f_wr_to_.htm" rel="nofollow noreferrer"><code>WRITE-TO-STRING</code></a> or <a href="http://www.lispworks.com/documentation/lw50/CLHS/Body/f_format.htm" rel="nofollow noreferrer"><code>FORMAT</code></a>.</p>
<h2>DESCRIBE</h2>
<p>Describe is intended for debugging. The default mechanism relies on introspection to print its slots.</p>
<pre><code>USER&gt; (describe (make-instance 'abc :x 10))
#&lt;ABC {1007819283}&gt;
  [standard-object]

Slots with :INSTANCE allocation:
  X                              = 10
</code></pre>
<p>Users can define their own methods for the <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_desc_1.htm" rel="nofollow noreferrer"><code>DESCRIBE-OBJECT</code></a> generic function, with the expectation that the string is only read by humans and does not need to be read back.</p>
<h2>PRINT-OBJECT</h2>
<p>There is a <em>generic function</em> named <a href="http://www.lispworks.com/documentation/lw50/CLHS/Body/f_pr_obj.htm" rel="nofollow noreferrer"><code>PRINT-OBJECT</code></a>. An important effort in Lisp has been made to handle the case where we want the printed representation of a value to be read back (this leads to a simple  serialization/deserialization mechanism, along with <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_mk_ld_.htm" rel="nofollow noreferrer"><code>MAKE-LOAD-FORM</code></a>).</p>
<p>The default behavior is implementation-dependent but here for example in SBCL the value is printed with the <code>#&lt;&gt;</code> syntax, displaying the class of the value and a unique identifier:</p>
<pre><code>USER&gt; (make-instance 'abc :x 3)
#&lt;ABC {101D6FD663}&gt;
</code></pre>
<p>The <code>#&lt;&gt;</code> syntax is specifically used to represent data that cannot be read back by Lisp (e.g. a thread, a process...). If you ask the runtime to print it readably, you have an error:</p>
<pre><code>USER&gt; (write (make-instance 'abc :x 3) :readably t)
; Evaluation aborted on #&lt;PRINT-NOT-READABLE {101D772E23}&gt;.
</code></pre>
<p>You can define a custom printer.</p>
<pre><code>USER&gt; (defmethod print-object ((o abc) stream)
        (if *print-readably*
            (format stream &quot;#.~s&quot; `(make-instance 'abc :x ,(abc-x o)))
            (call-next-method)))
#&lt;STANDARD-METHOD COMMON-LISP:PRINT-OBJECT (ABC T) {101FBDEA53}&gt;
</code></pre>
<p>Here I specifically handle the <code>*print-readably*</code> special variable, and fall back to the default method with <code>(call-next-method)</code> when that variable is nil.</p>
<p>I emit code that starts with <code>#.</code>, the syntax for calling the evaluator at read-time. When I give the resulting string to the Lisp reader, it will allocate a new object of type ABC with the same value for <code>x</code> when reading the string. The slot value <code>x</code> itself is printed readably.</p>
<p>Here below you can see both ways of printing the value, the first one is the result of printing the value to standard-output, followed by the REPL printing the result of the call using the standard (non-readable) way.</p>
<pre><code>USER&gt; (write (make-instance 'abc :x 5) :readably t)
#.(MAKE-INSTANCE 'ABC :X 5) 
#&lt;ABC {101FC00393}&gt;
</code></pre>
<p>Coercing to a string is never done automatically, you have to print the object to a string stream:</p>
<pre><code>USER&gt; (with-output-to-string (out)
        (write (list (make-instance 'abc :x 1)
                     (make-instance 'abc :x 2))
               :stream out
               :readably t))
&quot;(#.(MAKE-INSTANCE 'ABC :X 1) #.(MAKE-INSTANCE 'ABC :X 2))&quot;
</code></pre>
<p>Reading back from the string:</p>
<pre><code>USER&gt; (read-from-string *)
(#&lt;ABC {10070E8373}&gt; #&lt;ABC {10070E8423}&gt;)
57
</code></pre>
<p>If the actual identity of the objects matters, then it is not a good printing method, but in that case you would need to implement your own way of indexing objects and retrieving them later.</p>

CC BY-SA 4.0
2023-08-31T10:53:10.503Z
2023-09-01T14:16:09.140Z
2023-09-01T14:16:09.140Z
coredump
# What ways exist for handling object to string conversion?
Posted by: username_0 on 2023-05-17
For example:

* Java uses a `toString()` method, and calls that behind the scenes. If you don't implement one, you inherit it from the base class (`Object`).
* JavaScript uses the ToPrimitive abstract operation, which involves @@toPrimitive, **"toString"**, and **"valueOf"**. You can read the spcification [here](https://tc39.es/ecma262/multipage/abstract-operations.html#sec-toprimitive). Again, if you don't implement any of these, you inherit one from Object.
* Swift uses three different protocols, which you can read about [here](https://developer.apple.com/documentation/swift/string/init(describing:)-67ncf). If you don't implement any of them, it uses reflection to generate a sensible default.

What are some advantages and disadvantages to these methods, and what other methods exist?
**Tags:** strings string-interpolation
---
## 6 answers
### Answer by username_1 on 2023-05-17. Score: 9
# Rust's approach: `Display` and `Debug` traits

Rust uses the trait system, through the [`Display`](https://doc.rust-lang.org/std/fmt/trait.Display.html) and [`Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html) traits, to model types that may be converted to strings, with two possible and well-known uses (user-facing and developer-facing, respectively).

However, it statically ensures that values of a type that doesn't implement the `Display` trait may not be used to construct a string representation; the same for `Debug`. Hence, Rust doesn't have a *default* string representation for user-defined types.

Even though there is not a default string representation, the language offers a macro to derive the `Debug` implementation using a sensible default that follows the type's definition. However, the `Display` trait may *not* be derived, which forces the developer to implement the *user-facing* string conversion explicitly.

---

Haskell has a similar mechanism via the `Show` type class, which may be derived.
---
### Answer by username_2 on 2023-05-18. Score: 5
Another option is to simply forbid string coercion, as e.g. Python does. This makes it the programmer's responsibility to explicitly convert other values to strings where necessary, but also means there is no uniquely privileged way to convert to a string.

In Python for example, there are the `str` and `repr` functions which convert values to strings in different ways (and which can have user-defined behaviour via the `__str__` and `__repr__` dunder methods), but you can also convert to string through other means, such as by calling some other method defined by the object's class, or using string formatting or f-strings which may offer more options for how the conversion is done, such as `f'{x:.02f}'`.
---
### Answer by username_3 on 2023-05-18. Score: 4
APL specifies a default display form for every array (all values are arrays). Dyalog APL augments this in two ways:

Objects (classes, instances, and namespaces) have a built-in method that takes a character array which then overrides the default form. An object can also set its own display form.      a←⎕NS⍬  ⍝ new namespace
      a  ⍝ will show default display form
#.[Namespace]
      a.⎕DF'{myThing}'  ⍝ change it
      a
{myThing}


There is a printing event to which one can set a handler that in turn can do anything it wants based on the array that needs to be printed. The system comes with a bunch of ready-made handlers for various customisable effects.      MyPrinting←{⎕←⌽⍺}  ⍝ output normal display form, but reversed
      ⎕SE.onSessionPrint←'MyPrinting'  ⍝ assign event handler
      'Hello World'
dlroW olleH
---
### Answer by username_4 on 2023-08-27. Score: 3
C++ [ostream/strstream](https://en.cppreference.com/w/cpp/io).

Using a C++-style stream approach is the only way I know for a language that allows users to define types that cannot implement interfaces, like myInt256, but allows these types to take part in the conversion API.

Splitting the stream API from the StringBuffer implementation allows moving the StringBuffer implementation to an optional module. The stream API itself must reside in the core part of the standard library if core types should provide implementations for it and extending types from other modules is not allowed.

A property that I consider neither pro or con is that the Stream API can have a [state](https://en.cppreference.com/w/cpp/io/ios_base/flags) telling inserted entities a desired format.
---
### Answer by username_6 on 2023-08-31. Score: 3
In Common Lisp, there are different ways of printing an object depending on what you want. Let's define a custom class first:
USER> (defclass abc () ((x :initarg :x :reader abc-x)))
#

The system can print any object, and converting to a string is done by printing to a string stream. Typically you call WRITE-TO-STRING or FORMAT.
DESCRIBE
Describe is intended for debugging. The default mechanism relies on introspection to print its slots.
USER> (describe (make-instance 'abc :x 10))
#
  [standard-object]

Slots with :INSTANCE allocation:
  X                              = 10

Users can define their own methods for the DESCRIBE-OBJECT generic function, with the expectation that the string is only read by humans and does not need to be read back.
PRINT-OBJECT
There is a generic function named PRINT-OBJECT. An important effort in Lisp has been made to handle the case where we want the printed representation of a value to be read back (this leads to a simple  serialization/deserialization mechanism, along with MAKE-LOAD-FORM).
The default behavior is implementation-dependent but here for example in SBCL the value is printed with the #<> syntax, displaying the class of the value and a unique identifier:
USER> (make-instance 'abc :x 3)
#

The #<> syntax is specifically used to represent data that cannot be read back by Lisp (e.g. a thread, a process...). If you ask the runtime to print it readably, you have an error:
USER> (write (make-instance 'abc :x 3) :readably t)
; Evaluation aborted on #.

You can define a custom printer.
USER> (defmethod print-object ((o abc) stream)
        (if *print-readably*
            (format stream "#.~s" `(make-instance 'abc :x ,(abc-x o)))
            (call-next-method)))
#

Here I specifically handle the *print-readably* special variable, and fall back to the default method with (call-next-method) when that variable is nil.
I emit code that starts with #., the syntax for calling the evaluator at read-time. When I give the resulting string to the Lisp reader, it will allocate a new object of type ABC with the same value for x when reading the string. The slot value x itself is printed readably.
Here below you can see both ways of printing the value, the first one is the result of printing the value to standard-output, followed by the REPL printing the result of the call using the standard (non-readable) way.
USER> (write (make-instance 'abc :x 5) :readably t)
#.(MAKE-INSTANCE 'ABC :X 5) 
#

Coercing to a string is never done automatically, you have to print the object to a string stream:
USER> (with-output-to-string (out)
        (write (list (make-instance 'abc :x 1)
                     (make-instance 'abc :x 2))
               :stream out
               :readably t))
"(#.(MAKE-INSTANCE 'ABC :X 1) #.(MAKE-INSTANCE 'ABC :X 2))"

Reading back from the string:
USER> (read-from-string *)
(# #)
57

If the actual identity of the objects matters, then it is not a good printing method, but in that case you would need to implement your own way of indexing objects and retrieving them later.
---
### Answer by username_5 on 2023-09-01. Score: 1
C++ uses character-stream operators (`<<` and `>>`) for such conversions.

One advantage is that overloads can provide different representations for different kinds of stream (e.g. we might want a `debug_stream` class to be verbose, whereas a `datastore_stream` could be compact but must have full round-trip fidelity).

The disadvantage is that you might end up having to implement the overloads yourself, for libraries without the overloads you need for your stream types. I often see code that provides streaming operators for plain-`char` streams, but not for `wchar_t` streams, as a common example.
---
