str vs. pr-str - humans vs. machines
source link: https://blog.klipse.tech/clojure/2016/11/24/stringify-clojure.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
This post is an interactive adaptation of a detailed explanation of Alex Miller in the Clojure Google group about Clojure print system.
Clojure’s print system has two families of functions:
- one for human consumption
- and one for machine consuption
Many kinds of Clojure data print the same in either mode (strings are one exception).
The human printing functions
The human printing is most commonly encountered with functions like println
, print
and str
. It’s designed to print things to the repl or to the console for a person to read:
Strings print without the surrounding quotes:
xxxxxxxxxx
(str "Hello World!")
the evaluation will appear here (soon)...
Newlines are really printed as newlines:
xxxxxxxxxx
(str "Hello\nWorld!")
xxxxxxxxxx
the evaluation will appear here (soon)...
Tabs are really printed as tabs:
xxxxxxxxxx
(str "Hello\tWorld!")
xxxxxxxxxx
the evaluation will appear here (soon)...
, etc…
Dear reader, what other examples could you think about? Please add a comment below…
str
doesn’t directly use either Clojure printing mode but instead returns the toString()
of each object (this is Java[script]’s built-in printing system).
xxxxxxxxxx
(.toString "Hello\nWorld!")
xxxxxxxxxx
the evaluation will appear here (soon)...
For strings, both Clojure’s printing and str
wind up just relying on Java[script] to print a string in a “human-readable” way.
The problem with humans
The problem with humans is that they don’t like to read like machines. In other words str
and read-string
do not always play well together. Let’s see it in action:
(In clojurescript, read-string
is not part of the core so we have to explicitly require it.)
xxxxxxxxxx
(require '[cljs.reader :as r :refer [read-string]])
xxxxxxxxxx
the evaluation will appear here (soon)...
xxxxxxxxxx
(-> (str "Hello World!")
read-string)
xxxxxxxxxx
the evaluation will appear here (soon)...
Oh oh…
What’s happened there? Where is the World?
In order to discover it, let’s check what is the type of the object returned by read-string
:
It is not a string:
xxxxxxxxxx
(-> (str "Hello World!")
read-string
string?)
xxxxxxxxxx
the evaluation will appear here (soon)...
But it is a symbol:
xxxxxxxxxx
(-> (str "Hello World!")
read-string
symbol?)
xxxxxxxxxx
the evaluation will appear here (soon)...
The reason is because str
omits the surrounding quotes - in order to be human friendly:
xxxxxxxxxx
(str "Hello World!")
xxxxxxxxxx
the evaluation will appear here (soon)...
So, for read-string
, this string is in fact composed of two symbols. And read-string
reads only one object.
xxxxxxxxxx
(read-string "Hello World!")
xxxxxxxxxx
the evaluation will appear here (soon)...
In order to be read-string
, one has to not-omit the surrounding quotes:
xxxxxxxxxx
(read-string "\"Hello World!\"")
xxxxxxxxxx
the evaluation will appear here (soon)...
This is exactly the purpose of clojure’s data printing functions…
The data printing functions - for machine consumption
The data printing functions are things like:
pr
- likeprint
, but for dataprn
- likeprintln
, but for datapr-str
- likestr
, but for data
The idea with the data printers is that the thing you print should be readable by Clojure. So pr-str
etc… will print a string as the actual characters Clojure would need to read that string back as data.
Strings print with the surrounding quotes:
xxxxxxxxxx
(pr-str "Hello World!")
xxxxxxxxxx
the evaluation will appear here (soon)...
Newlines are printed as \n
:
xxxxxxxxxx
(pr-str "Hello\nWorld!")
xxxxxxxxxx
the evaluation will appear here (soon)...
Tabs are printed as \t
:
xxxxxxxxxx
(pr-str "Hello\tWorld!")
xxxxxxxxxx
the evaluation will appear here (soon)...
Clojure types
Clojure types (like maps) implement toString()
to route back into the Clojure printing system.
So str
and pr-str
prints the same:
xxxxxxxxxx
(str {:first "Hello"
:second "World!"})
xxxxxxxxxx
the evaluation will appear here (soon)...
xxxxxxxxxx
(pr-str {:first "Hello"
:second "World!"})
xxxxxxxxxx
the evaluation will appear here (soon)...
What else?
This is the big picture. I have left the even more complicated pretty printing (pprint
) and cl-format
(following CommonLisp) parts.
You are in a maze of twisty little passages, all alike. If you look too hard at it, you are likely to be eaten by a grue.
The print
and pprint
systems also have many dynamic vars to influence behavior and a number of multimethods intended for extension or modification.
In particular, you can provide your own printers for either built-in types or custom records or types by extending things like print-method
(human) or print-dup
(data).
If anyone wanted to write a mini Clojure book, this would be a killer topic.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK