waseigo

waseigo

DoTrace - Provides a trace/1 macro to trace function calls at runtime, mimicking Clojure's dotrace output

I saw this LinkedIn post:

*Can your programming language do this?

This is a macro in Clojure called `dotrace`. When you surround a piece of code with it, it pretty prints the call tree along with its inputs/outputs, which is incredibly useful for debugging recursive functions.

Many other languages cannot do this with user-level code.

Clojure can because its syntax is “just” data structures and can be processed and rewritten on the fly by code of your own choosing. Like `dotrace`!

Macros effectively extend the compiler into userland, and it’s one of the reasons Lisp is so easy to fall in love with it.*

I don’t know about Lisp, but here’s an attempt in Elixir:

iex> defmodule Math do
       def factorial(0), do: 1
       def factorial(n) when n > 0, do: n * factorial(n - 1)
     end
iex> require DoTrace
iex> DoTrace.trace(Math.factorial(5))
TRACE t1028: (factorial [5])
TRACE t1092: | (factorial [4])
TRACE t1156: | | (factorial [3])
TRACE t1220: | | | (factorial [2])
TRACE t1284: | | | | (factorial [1])
TRACE t1348: | | | | | (factorial [0])
TRACE t1348: | | | | | | => 1
TRACE t1284: | | | | | => 1
TRACE t1220: | | | | => 2
TRACE t1156: | | | => 6
TRACE t1092: | | => 24
TRACE t1028: | => 120
120

https://github.com/waseigo/do_trace

Most Liked

mudasobwa

mudasobwa

Creator of Cure

That looks great, but why is it not shaped as a custom backend to Kernel.dbg/2?

Eiji

Eiji

Very cool feature! I would like to suggest few ideas:

  1. Add the module name and arity to trace logs
  2. Use :owl package to generate links that opens specific function source code
  3. Add option to disable trace log prefix to ensure output is clear
  4. In other case I would use :owl again to have borderless table with prefix as first column and log as the second one.

Look that in my case the output looks like:

TRACE t515: (factorial [5])
TRACE t643: | (factorial [4])
TRACE t771: | | (factorial [3])
TRACE t899: | | | (factorial [2])
TRACE t1027: | | | | (factorial [1])
TRACE t1155: | | | | | (factorial [0])
TRACE t1155: | | | | | | => 1
TRACE t1027: | | | | | => 1
TRACE t899: | | | | => 2
TRACE t771: | | | => 6
TRACE t643: | | => 24
TRACE t515: | => 120

t before number is rather useless I think, but it’s not important. Some integers have 4 digits and some only 3. Because of that the output could be more or less harder to read in some cases.

# version 1 (with option to disable prefix set to true - default?)
(Math.factorial/1 [5])
| (Math.factorial/1 [4])
| | (Math.factorial/1 [3])
| | | (Math.factorial/1 [2])
| | | | (Math.factorial/1 [1])
| | | | | (Math.factorial/1 [0])
| | | | | | => 1
| | | | | => 1
| | | | => 2
| | | => 6
| | => 24
| => 120

# version 2 (with option to disable prefix set to false)
TRACE t515:  (Math.factorial/1 [5])
TRACE t643:  | (Math.factorial/1 [4])
TRACE t771:  | | (Math.factorial/1 [3])
TRACE t899:  | | | (Math.factorial/1 [2])
TRACE t1027: | | | | (Math.factorial/1 [1])
TRACE t1155: | | | | | (Math.factorial/1 [0])
TRACE t1155: | | | | | | => 1
TRACE t1027: | | | | | => 1
TRACE t899:  | | | | => 2
TRACE t771:  | | | => 6
TRACE t643:  | | => 24
TRACE t515:  | => 120

Edit: Oh, looks like there is a bug with pipes:

iex> 5 |> Math.factorial() |> IO.puts() |> DoTrace.trace()
** (UndefinedFunctionError) function IO.puts/0 is undefined or private. Did you mean:

      * puts/1
      * puts/2

    (elixir 1.19.0-rc.0) IO.puts()
    iex:13: (file)
dimitarvp

dimitarvp

Well, if only Common LISP had the OTP runtime. The LISP folks practically beat most of the computer science game ages ago, including allow one to easily devise business-specific DSLs that compile to quite well optimised LISP.

mudasobwa

mudasobwa

Creator of Cure

Nah, c’mon. I just wanted to suggest that embedding into an existing ecosystem (especially if provided by the language core team) might save you few keystrokes, because Kernel.dbg/2 might actually offload a ton of work from you, providing a common mechanism for fancy output and/or real debugging with pry.

Still, it looks already very good, but a bit alien, that’s what was my humble comment about :slight_smile:

mudasobwa

mudasobwa

Creator of Cure

No macros no fun, if I recall this verse by Bob Marley correctly.

Where Next?

Popular in Announcing Top

metehan
ExTerm is a minimal Elixir library that lets you expose your terminal in a web browser. It’s mostly a toy project for learning and experi...
New
rodloboz
I’ve started working on a new library to run SQL queries and do basic business intelligence. Think “Blazer for Elixir.” Currently it fe...
New
fuelen
Ecto.DevLogger is an alternative logger of SQL queries in development It inlines bindings into the query, so it is easy to copy-paste lo...
New
roriholm
Hi! I just wanted to share my public release of something I’ve been working on. The Paradigm library provides some core utilities for a d...
New
tugayac
Hey Elixir Community! A few months back, I open sourced a link management system we had built into an app. It’s now got enough features ...
New
LostKobrakai
I’ve recently created a small library phoenix_vite integrating the vite build tooling with phoenix. It provides an igniter.installer t...
New
Mati365
Phoenix has great tooling overall, but one thing that’s still lacking is a solid, plug-and-play integration with a modern WYSIWYG editor....
New
waseigo
I saw this LinkedIn post: *Can your programming language do this? This is a macro in Clojure called `dotrace`. When you surround a pie...
New
sodapopcan
I’ve heard that if you’re not embarrassed by v1 of your product Vim plugin then you’ve released too late. I’ve been sitting on this the ...
New
trisolaran
Hi! :waving_hand: I would like to present LiveSelect, a little library that I wrote to easily add a dynamic selection input to your LV f...
194 10054 106
New

Other popular topics Top

Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 35421 110
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New

We're in Beta

About us Mission Statement