[Proposal] Debug macro like dbg! in Rust 1.32

139 views
Skip to first unread message

Роман Смирнов

unread,
Jan 23, 2019, 3:06:46 PM1/23/19
to elixir-lang-core
Hi,

Recently I read release notes of Rust 1.32 and was impressed of dbg macro convenience. Therefore I decided to implement the same concept in Elixir.
At the moment I've implemented it as a library: https://github.com/romul/dbg_inspect

But maybe it would be useful to implement it in the Elixir itself to avoid extra require calls each time. What do you think?

Wojtek Mach

unread,
Feb 15, 2019, 7:56:06 AM2/15/19
to elixir-lang-core
I just wanted to add that I've been using such macro from the day I saw that Rust blog post [1]  and it had replaced all my usages of `IO.inspect` ever since (and I did use it a lot) and it proved really helpful to me.

To try moving this forward, I'd like to try fleshing out a concrete proposal. It has a bit different feature set than https://github.com/romul/dbg_inspect mentioned above. 

My proposal is to add `Kernel.dbg(expr, options \\ [])` which prints file, line, expression, pretty prints expression result, and prints how long evaluating the expression took.
I've literally added the time measurement feature today so still getting a feel for it; perhaps it should be optional and not be enabled by default if it's not important most of the time.

The options are:

- :label - useful for pipes, see below
- :inspect - passed to Kernel.inspect

Examples

    $ cat lib/a.exs
    dbg Time.utc_now
    $ elixir lib/a.exs
    lib/a.exs:1: Time.utc_now() #=> ~T[12:42:40.809472] (1ms)

    $ iex
    iex(1)> dbg Time.utc_now()
    iex:1: Time.utc_now() #=> ~T[11:52:07.744334] (1ms)
    ~T[11:52:07.744334]

    iex(2)> 1..5 |> Enum.map(&dbg(&1 * 2)) |> Enum.sum()
    iex:2: x1 * 2 #=> 2 (2µs)
    iex:2: x1 * 2 #=> 4 (1µs)
    iex:2: x1 * 2 #=> 6 (1µs)
    iex:2: x1 * 2 #=> 8 (1µs)
    iex:2: x1 * 2 #=> 10 (1µs)
    30

    iex(1)> 1..5 |> Enum.map(&dbg(&1 * 2, inspect: [syntax_colors: [number: :blue]])) |> Enum.sum()
    iex:3: x1 * 2 #=> 2 (6µs)
    iex:3: x1 * 2 #=> 4 (1µs)
    iex:3: x1 * 2 #=> 6 (10µs)
    iex:3: x1 * 2 #=> 8 (1µs)
    iex:3: x1 * 2 #=> 10 (1µs)
    30

Since pipes are expanded we'll get this:

    iex(4)> Time.utc_now() |> to_string() |> String.slice(0, 5) |> dbg()
    iex:4: String.slice(to_string(Time.utc_now()), 0, 5) #=> "11:53" (28µs)
    "11:53"

We can overwrite the label as following:

    iex(5)> Time.utc_now() |> to_string() |> String.slice(0, 5) |> dbg(label: :pipe)
    iex:5: pipe #=> "11:54" (34µs)
    "11:54"

I think it needs to be a function on Kernel to be the most easily accessible, e.g. we wouldn't need to require it.

I've started with calling it `Kernel.debug` however, that could introduce breakage if projects were already importing modules with that function, e.g.:

    ** (CompileError) lib/strategy/dns_poll.ex:121: function debug/2 imported from both Cluster.Logger and Kernel, call is ambiguous

Of course `dbg` suffers from the same problem although it's probably less likely to happen. It might be confused with OTP's :dbg module too.
If that's a deal breaker, or explicitly requiring it would be preferred anyway, I'd propose to call it `IO.debug` instead.

See [2] for example implementation.

Thanks
Wojtek

Chris Keathley

unread,
Feb 18, 2019, 9:38:18 AM2/18/19
to elixir-l...@googlegroups.com
This seems like a really useful addition to the core language. I like the idea of including the time measurements but I don't have a strong opinion on whether they should be shown by default or hidden behind an option. As far as the name goes I don't have a problem with `dbg` or `debug`. Both names communicate the same thing to me personally. I do think it makes sense to put this function in the `IO` module since it's used to output debug information. If it was on `Kernel` it may be more likely to be confused as a function to invoke a breakpoint or debugger or something similar. Thats just my intuition though. Overall this seems great.

- keathley

--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/593aaf8a-7ca4-46ba-81ad-5ab2f9b39954%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


--
Chris
Reply all
Reply to author
Forward
0 new messages