Joël
Joël

Reputation: 2822

Why does `read` return a symbol when `read-line` returns a string

I'm learning Clojure by following the Hackerrank 30 days of code, and lost some hours due to a behavior I neither understand nor found any documentation or explanation about:

As a result, parsing a line with (read) (read) to get map keys and values results in the keys to be symbols, than will never be matched by a further (read-line).

Why is this so? And also, where can I find the return value? (this is not documented in (doc read)).

Upvotes: 2

Views: 261

Answers (3)

cfrick
cfrick

Reputation: 37008

TL;DR

  • clojure.core/read is used to read "code" by Clojure itself
  • clojure.edn/read is used to read "data" (EDN)
  • read-line is used to read text lines as string; it's your problem to decipher them

What can read do for you

read does not only read symbols, but anything, that Clojure uses to represent code. If you give it a symbol to parse, it will give you symbol back:

(type (read))
test
clojure.lang.Symbol

But also other things

(type (read))
5
java.lang.Long

(type (read))
{:a 42}
clojure.lang.PersistentArrayMap

(type (read))
"hello"
java.lang.String

So you can get back a string with read too, if you feed it a string.

real-world use of read

Usually read is used by Clojure itself and that's it. Reading EDN is usually done using clojure.edn/read, which does not allow code execution and therefor is no security risk if handling EDN from untrusted sources.

docs

For good measure, here are the docs:

(doc read)
-------------------------
clojure.core/read
([] [stream] [stream eof-error? eof-value] [stream eof-error? eof-value recursive?] [opts stream])
  Reads the next object from stream, which must be an instance of
  java.io.PushbackReader or some derivee.  stream defaults to the
  current value of *in*.

  Opts is a persistent map with valid keys:
    :read-cond - :allow to process reader conditionals, or
                 :preserve to keep all branches
    :features - persistent set of feature keywords for reader conditionals
    :eof - on eof, return value unless :eofthrow, then throw.
           if not specified, will throw

  Note that read can execute code (controlled by *read-eval*),
  and as such should be used only with trusted sources.

  For data structure interop use clojure.edn/read

(doc read-line)
-------------------------
clojure.core/read-line
([])
  Reads the next line from stream that is the current value of *in* .

Upvotes: 7

Alan Thompson
Alan Thompson

Reputation: 29958

The other 2 answers are good. I didn't even know that clojure.core/read existed!

I only wanted to add in a list of my favorite documentation sources. Please review & study the Clojure CheatSheet, which links to examples on clojuredocs.org.

Unfortunately, the API docs at clojure.org are not as descriptive and it is harder to find things unless you already know the name and location.

Upvotes: 1

Biped Phill
Biped Phill

Reputation: 1281

You can find the Clojure API documentation at https://clojure.github.io/clojure/clojure.core-api.html. Both read and read-line are there.

Your specific goal isn't quite clear, but in general, application software prefers read-line and parses the results in whatever way makes sense... perhaps with re-matches for regular expressions. Clojure itself reads program code with read.

Upvotes: 1

Related Questions