Why does IO.inspect prints literally "\n" instead of a return line?

When executing the following:

IO.inspect Inspect.Ecto.Query.to_string(from u in User, where: u.id == ^1)

special characters such as \n are printed literally.

"from u0 in MyApp.User,\n  where: u0.id == ^1"

However, if I use IO.puts:

IO.puts Inspect.Ecto.Query.to_string(from u in User, where: u.id == ^1)

the output is as needed:

 from u0 in MyApp.User,
   where: u0.id == ^1

Anyone has any idea why?

1 Like

inspect/2 and its friend IO.inspect/2 are intended as debugging aids. So its very helpful to encode non-printing characters so a developer can see clearly the content. Noting that the Inspect protocol is implemented per data type - what you are describing is the behaviour for a String.t().

As you are seeing, the String.Chars protocol is intended to produce a string for “human” consumption hence implementations of the protocol (including for String.t() doesn’t encode non-printing characters.

Also you should be just able to do:

IO.inspect from(u in User, where: u.id == ^1))

since IO.inspect/2 will leverage the Inspect protocol implementation defined for %Ecto.Query{}.

3 Likes

The reason I do not write IO.inspect query is because it will then print:

ecto.Query<from u0 in MyApp.User, where: u0.id == ^1>

That’s why I rely on Inspect.Ecto.Query.to_string

The reason I like IO.inspect is because it returns the inspected value + I find its :label very useful. I guess I’ll stick with IO.puts in order to print the return lines, but I will write a utility function wrapping it, to make it behave like inspect (return the query instead of :ok and allow to add a label).

Let me know if you think it’s nonsense :flushed:

Also I tried the :pretty option:

IO.inspect Inspect.Ecto.Query.to_string(from u in User, where: u.id == ^1), pretty: true

But it doesn’t print return lines:

“from u0 in MyApp.User,\n where: u0.id == ^1”

Isn’t :pretty supposed to render the return lines?

In that case I think you would find IO.puts query would do what you want rather than calling the protocol implementation directly.

IO.puts query gives the following error:

** (Protocol.UndefinedError) protocol String.Chars not implemented for ecto.Query<…> of type Ecto.Query (a struct). This protocol is implemented for the following type(s): Postgrex.Copy, Postgrex.Query, Decimal, Atom, BitString, Date, DateTime, Float, Integer, List, NaiveDateTime, Time, URI, Version, Version.Requirement
(elixir) lib/string/chars.ex:3: String.Chars.impl_for!/1
(elixir) lib/string/chars.ex:22: String.Chars.to_string/1
(elixir) lib/io.ex:654: IO.puts/2

We will stick with a util function for now.

def inspect_query(query, label \\ nil)

def inspect_query(query, nil) do
  IO.puts(Inspect.Ecto.Query.to_string(query))
  query
end

def inspect_query(query, label: label) do
  IO.puts("#{label}:\n#{Inspect.Ecto.Query.to_string(query)}")
  query
end
query
|> by_author(author_id)
|> inspect_query(label: "does this query look good?")
|> Repo.one!()

That function is not part of the publicly documented API and therefore might be removed at any time.

1 Like

In that case, I have no idea how to pretty-print a query as Inspect.Ecto.Query.to_string does.

For debugging purposes inspect should be sufficient.

Though you shouldn’t rely on the exact output format of either, as it’s meant for debugging only, or not documented at all, formats can change without a warning as they might be considered necessary.

Perhaps tell us why you need them pretty printed?

Because it’s easier to read. When there is an error with your query, Ecto also pretty-prints them (that’s where I got that function from).

1 Like

@kip @NobbZ guys, thank you for the help, IO.inspect query is actually enough, you were right. It even does print return lines, just a little less pretty than Inspect.Ecto.Query.to_string; I was being too difficult :smiley:
Thank you for your help!