trace cli

This is essentially a demo on a single cli tool, trace. It allows you to inspect the stream at every point around the (likely tangled) cli operations. Let's get into it.

Let's start simple. Say you have an operation that calculates square of a list of numbers, like this:

For more complex operations, it can be easy to lose track of what's what, and how the stream should look like at a particular point. So, you can just slide a trace() in the middle:

With no arguments, this will get the shape of the stream. You can of course, pass in other displaying functions that you like. I typically just use iden():

Notice how I left out the last deref(). It's because it's only there to display the final value to us. But we already got that data from trace(), so we don't have to anymore. Let's transform it one step further:

Sweet. Let's combine the operations first, before feeding it input:

It knows, and automatically box the operations in. You can also put trace() wherever you like:

You can go wild with this. This is an example with .all(), |, and op():

You can limit the depth of the graph, like this:

If the graphs are too small/big, you can adjust the settings, like this:

Let's see some more complex examples:

As you can see, this can be extremely useful in debugging stuff.

Infinity and nested traces

The way trace() works is by deref-ing every part of the stream. This means you can't really handle infinite streams. There is a workaround for this though. Say we have this infinite stream:

So, we can set what infinity means inside cliSettings, which will be picked up by clis that can potentially produce infinite streams and limit them:

Because trace objects only displays their graph through calling __repr__, the code above wouldn't display anything because it's inside a block. To get the graph, do this:

This is sort of messy I get it, but there doesn't seem to be a way to robustly track infinite streams.

You can also put trace() inside of a relatively complex block. Let's grab an example from before:

Now let's try to intercept in the middle:

It works! If for some reason, it errors out, you can always do trace.last to see the best trace attempt. This is rare and I haven't been able to make it errors out, but logic says that it can in weird circumstances. Also, unlike trace() at the top level, notice how nested trace() are a part of the derefed object? This means that without actually forcing the execution to happen, you won't have any trace:

Therefore, it's my recommendation to always do deref(), ignore the output (because it can potentially be long), then see the last trace:

Also very surprisingly, this works too, while I reason it shouldn't really work!

No idea how that works, but nice

Gotchas

There aren't any other gotchas, as far as I'm aware of. Sometimes the clis will be replaced by a version that is guaranteed to return the same result, but are slightly different, to make tracing code simpler. Examples may include applyMp changing into apply (will not work if trace() is nested inside of applyMp tho, and there be dragons if you try to do so). But you need not worry about this too much.