Fork me on GitHub
#vim
<
2022-11-05
>
Chase01:11:44

This is timely, I've been wanting to explore taps. Do you happen to have a resource explaining them? It's a tool for data inspection right? And other tools like Portal and rebl provide a graphical interface for these taps or what? What is your workflow with this? Hope that's not too much questioning haha

walterl12:11:25

That's a fair point. It took me a while to figure out what they are and how to use them too. Taps by themselves are really just junction points for data flowing through a program. By itself calling tap> does nothing. It's only when you hook something up to it, with add-tap, that that "something" (a function) gets called with the tap>ped data. What you connect to your taps are up to you. As https://clojuredocs.org/clojure.core/tap%3E#example-612f72eee4b0b1e3652d7538 shows, it could be as simple as clojure.pprint/pprint. In practice it will often be a data exploration tool like https://github.com/vlaaad/reveal, https://github.com/djblue/portal, or Cognitect's https://docs.datomic.com/cloud/other-tools/REBL.html. These tools make it a lot easier to navigate data, especially deeply nested data structures that we often find in the Clojureverse. I've only used Reveal and now giving Portal a shot. Both of them have, for example, table renderers for "sequence of maps" kinds of data, which is incredibly useful for inspecting data. Here's an exception's stack trace being rendered in a table, just as a (rather bad) example:

walterl12:11:07

Re workflow: I tend to use taps only for debugging and data exploration, using Reveal or Portal, during development. It's a great companion to REPL driven dev, allowing you a more efficient view of the data you're working with. That's where this plugin comes in: if you quickly want to tap a var/form to inspect its value, it's just a little quicker to use a mapping, than to manually wrap the var/form in (tap> ,,,) and eval it.

walterl12:11:11

I know some people like to leave tap>s in their code, but I tend to remove them before committing. It could be useful to have "standard" tap points, but one of the drawbacks of taps is that you get no information about where the tapped value comes from. Unless, that is, you add it to the tap: (tap> {::frobnicator thing}).

walterl14:11:31

> "one of the drawbacks of taps is that you get no information about where the tapped value comes from" Looks like Portal has that covered 💪 https://github.com/djblue/portal/blob/master/doc/guides/portal-console.md

Chase17:11:37

That was an awesome breakdown, thank you!

Chase17:11:23

I installed the plugin but when I open up neovim now I get these errors:

Chase17:11:49

I haven't made the switch to a lua (or fennel) config so not sure if that is affecting this

walterl19:11:36

Do you have Conjure installed?

walterl19:11:58

Still, I think I'm using aniseed from Conjure rather than directly. Lemme check...

walterl19:11:40

OK, it's updated to use the local Aniseed. You'll still need Conjure installed, though.

walterl19:11:47

It does all the heavy lifting.

walterl19:11:03

> "I haven't made the switch to a lua (or fennel) config so not sure if that is affecting this" You don't need to.

Chase20:11:11

Hmmm, I got a lot of errors when just installing the plugin now. So when you are saying it is changed to use the local aniseed, I assume that means I have to have aniseed installed? I definitely have Conjure installed in the sense that I use it daily. Not sure if I should have something else going on though.

Chase20:11:56

It's probably user error on my side though, so please don't worry about it. Maybe we can see if any others have issues.

Chase20:11:18

I think I solved it. I had { 'for': 'clojure'} in my Conjure plugin settings. Removing that (or adding the same for your plugin) seems to have fixed it.

Chase20:11:29

The cool thing about that little adventure of mine is that I had installed Portal and was playing around with it too so now that I have your plugin working I got to see firsthand the convenience it provides! haha

🎉 1
walterl21:11:31

Thanks for the report on the source of your errors. One could probably jump through an extra few hoops to check if Conjure libraries can be loaded, but that'll require quite a bit of shuffling. I'll do so if this comes up often. 🙂

walterl11:11:31

Oh, and allow me to ruin my own newborn plugin with a Portal feature: https://github.com/djblue/portal/blob/master/doc/guides/nrepl.md. With that enabled every REPL eval's return value is automatically loaded into Portal. No tap required. 😅

🥲 1
Chase18:11:54

lol, yep, that is quite convenient

Proctor14:11:11

We have used taps with integrant for local development fakes for methods that would call out to AWS Services in prod-like environments. e.g.

(defmethod ig/init-key :aws.sns/tap! [_ _]
  (fn [message] (tap> {:my-ns.tap/type :aws.sns/publish ::value message})))
by tapping a map with a namespaced type key and a namespaced value, it allows us to have taps to listen to the various types of events that we have.

Proctor14:11:16

so there are things like SNS, SQS, and some others, where we could add-tap a general pprint , or have other taps that pick things up and feed to channels that represent our SQS Queues or SNS Topics that invoke other local running lambdas in the same project.

Proctor14:11:08

e.g. if we want to do local dev testing of a HTTP request comes in, invokes a lambda that puts a message on a queue to work, and watch the other lambda do its thing

Proctor14:11:00

the tap by default is off so we don’t get that behavior, but by using the tap, we can do an add-tap that will look at data an do a dispatch to a channel that we started that acts like the AWS queue processor that feeds data from that channel to the lambda’s handler function

walterl14:11:28

Another (less impressive) usage I just employed: Using a tap>-based log publisher with mulog, to get all my (structured) logs in Portal.

Proctor14:11:24

@UJY23QLS1 are you doing tap> calls directly, or do you structure the output of the tap so you can tell that it came from a call by your plugin vs something else?

walterl14:11:42

Directly. Mulog already adds a few keys with the :mulog/ ns, so it's pretty clear.

Proctor14:11:43

was part of the reason we put the value in a map, so in our calls it gives us “metadata” around the source of the tap

walterl14:11:47

Yeah, that makes a lot of sense for how you're using it 🙂

Proctor14:11:46

wasn’t sure if you distinguished between this is a tap> call in the code, vs this is a manual intervention of a tap> call

Proctor14:11:09

e.g. if you call it with this plugin, you might want to easily filter out, or know that it was a debug/interactive invocation vs ones that happened in the course of the system running “naturally”… for some definition of naturally 😉

walterl14:11:34

The plugin doesn't wrap the data in anything other than a tap> call, no, but that's quite an interesting idea. Shouldn't be too hard to wrap it in a map with a user configured key too. So it becomes (tap> {:user.configured/key tapped-value})

martinklepsch13:11:30

This is pretty cool. I’ve been using tap> mostly for promises in node REPLs because otherwise there’s not an easy way to view the results of a promise (async println is wonky). Would there be a way to do something like #(.then % tap>) but only if the value returned is a promise?

Abhinav05:11:47

This is cool. I wrote something similar, it’s a macro that injects inline defs in functions. I’ve been using it as my sole debugger on neovim. https://github.com/AbhinavOmprakash/snitch

❤️ 1