Skip to content

Commit

Permalink
Improve documentation for Object#to_s and #inspect
Browse files Browse the repository at this point in the history
This patch changes the method definitions as proposed in crystal-lang#9966 and adds some
additional notes.

The part about ambigouity of collection members is removed because that's not
generically relevant for `#inspect` but for individual implementations such as
`Array#inspect`. So I added a short note about it there.
It does not need to be repeated for every collection type.
  • Loading branch information
straight-shoota committed Nov 26, 2020
1 parent 1a97e71 commit 09e6691
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 47 deletions.
11 changes: 11 additions & 0 deletions src/array.cr
Expand Up @@ -1930,6 +1930,17 @@ class Array(T)
self
end

# Prints a nicely readable and concise string representation of this array
# to *io*.
#
# The result resembles an array literal but it does not necessarily compile.
#
# It invokes `#inspect(io)` on each element to avoid ambiguity.
#
# ```
# ["one", "two, three"].inspect # => ["one", "two, three"]
# # vs [one, two, three]
# ```
def to_s(io : IO) : Nil
executed = exec_recursive(:to_s) do
io << '['
Expand Down
87 changes: 40 additions & 47 deletions src/object.cr
Expand Up @@ -86,72 +86,65 @@ class Object
hash(Crystal::Hasher.new).result
end

# Returns a string representation of this object.
# Returns a nicely readable and concise string representation of this object,
# typically intended for users.
#
# Descendants must usually **not** override this method. Instead,
# they must override `to_s(io)`, which must append to the given
# IO object.
# This method should usually **not** be overridden. It delegates to
# `#to_s(IO)` which can be overridden for custom implementations.
#
# See `#inspect` for related method.
def to_s : String
String.build do |io|
to_s io
end
end

# Appends a `String` representation of this object
# to the given `IO` object.
#
# An object must never append itself to the io argument,
# as this will in turn call `to_s(io)` on it.
abstract def to_s(io : IO) : Nil

# Returns a `String` representation of this object suitable
# to be embedded inside other expressions, sometimes providing
# more information about this object.
# Prints a nicely readable and concise string representation of this object,
# typically intended for users, to *io*.
#
# `#inspect` (and `#inspect(io)`) are the methods used when
# you invoke `#to_s` or `#inspect` on an object that holds
# other objects and wants to show them. For example when you
# invoke `Array#to_s`, `#inspect` will be invoked on each element:
# This method is called when an object is used in string interpolation:
# In `"foo #{bar} baz"`, the value interpolated for `#{foo}` is the result of
# `foo.to_s`.
#
# ```
# ary = ["one", "two", "three, etc."]
# ary.inspect # => ["one", "two", "three, etc."]
# ```
# Implementations must not append `self` to *io* which would lead to an
# endless loop.
#
# Note that if Array invoked `#to_s` on each of the elements
# above, the output would have been this:
#
# ```
# ary = ["one", "two", "three, etc."]
# # If inspect invoked to_s on each element...
# ary.inspect # => [one, two, three, etc.]
# ```
#
# Note that it's not clear how many elements the array has,
# or which are they, because `#to_s` doesn't guarantee that
# the string representation is clearly delimited (in the case
# of `String` the quotes are not shown).
# See `#inspect(IO)` for related method.
abstract def to_s(io : IO) : Nil

# Returns an unambiguous and information-rich string representation of this
# object, typically intended for developers.
#
# Also note that sometimes the output of `#inspect` will look
# like a Crystal expression that will compile, but this isn't
# always the case, nor is it necessary. Notably, `Reference#inspect`
# and `Struct#inspect` return values that don't compile.
# This method should usually **not** be overridden. It delegates to
# `#inspect(IO)` which can be overridden for custom implementations.
#
# Classes must usually **not** override this method. Instead,
# they must override `inspect(io)`, which must append to the
# given `IO` object.
# See `#to_s` for related method.
def inspect : String
String.build do |io|
inspect io
end
end

# Appends a string representation of this object
# to the given `IO` object.
# Prints an unambiguous and information-rich string representation of this
# object, typically intended for developers, to *io*.
#
# The representation should be self-contained with the end being clearly
# identifiable.
# A Crystal expression that recreates an object with the same value (given an
# identical environment) is an ideal representation. It doesn't need to be
# actual code that compiles, but a resemblance is recommended.
# When this is not possible (for example for non-printable internal state)
# another useful description will do.
#
# It is advisable to have an appropriate `#inspect` implementation on every
# type. Default implementations are provided by `Struct#inspect` and
# `Reference#inspect`.
#
# It is similar to `#to_s(IO)`, but often provides more information.
# For types that don't provide a custom implementation of this method,
# default implementation delegates to `#to_s(IO)`.
#
# Similar to `to_s(io)`, but usually appends more information
# about this object.
# See `#inspect`.
# `::p` and `::p!` use this method to present an object.
def inspect(io : IO) : Nil
to_s io
end
Expand Down

0 comments on commit 09e6691

Please sign in to comment.