Fork me on GitHub

inspired by Clojure's new iteration fn, I've been thinking through consuming paginated APIs in ClojureScript, and came up with this as an idea:


the idea is a seq-like abstraction that represent sequential async processes, allowing transformations of them just like seqs


I want to use bundle target with webpack, and target to node. My code depends on a library which requires fs in its namespace, like this

(ns macchiato.fs
  (:refer-clojure :exclude [exists?])
  (:require ["fs" :as fs]))
When compile with cljs.main, it said
No such namespace: fs, could not locate fs.cljs, fs.cljc, or JavaScript source providing "fs" in file /home/tianshu/.cljs/.aot_cache/1.10.914/A5608AE/macchiato/fs.cljs
But I think fs is builtin for node. What happened here and how can I make it to work?


@doglooksgood Hrm. If you just do (require 'fs) at a Node REPL, you generally get access to the fs builtin


Hunch is that you need to specify the node target in order to make this work... (this has been a while for me)


I think I just made it work. I don't have to use bundle target, node target works well for my case.

Colin P. Hill21:02:16

Does the arity of macros behave differently in cljs than it does in clj? I tried to define a macro with a [s] overload and a [s & ss] overload (typical pattern where the latter just invokes the former in a loop), and when I tried to invoke it with multiple arguments, I got an error saying Wrong number of args (1). Best I can figure is that it successfully expanded the 1+n-ary overload, which then invokes the 1-ary overload, and it complained because the 1-ary overload got clobbered or something.


What does the macro look like?

Colin P. Hill21:02:06

(defmacro my-macro
  ;; proprietary stuff here, seems to work fine)
 ([s & ss]
  (for [s2 (cons s ss)]
     (my-macro s))))

Colin P. Hill21:02:18

I suppose I might be blundering my reasoning about compilation steps somewhere in that second overload...


It won't work on CLJ either. A nice explanation:

Colin P. Hill22:02:30

Oh right okay. What I attempted to write amounts to attempting to evaluate the first overload while the second is still expanding. If it ran, it would execute the for loop immediately, yielding a lazy sequence as the "syntax". Which clearly isn't right. I need to yield the code that will invoke the first overload, so that it expands on the next pass. Is that right?

Colin P. Hill22:02:56

Ah, and the reason it blew up instead of doing that wrong thing is that it lacks those hidden arguments.

Colin P. Hill22:02:04

Okay, got it. Thank you!

👍 1
Colin P. Hill22:02:18

I'm mildly curious why it can't insert those arguments in this context the way it would in other contexts, but the answer to that probably involves a deeper dive into macro internals than I want to wrap my head around right now 😆


I'd love to answer but I myself don't know. :)

Colin P. Hill22:02:55

Happen to know any good resources that elaborate on this bit? Feels like a good idea to familiarize myself with other macro pitfalls before I learn about them the hard way, and while I can see an intuitive sense to this I struggle to see the concrete reasons. > Instead, consider a helper function to do most of the work on behalf of the macro. Indeed, this is often a good practice for other macros as well.


Mm, not really sure why that would be a good practice for regular macros. clojure.core itself doesn't use that approach.


Off the top of my head, I know only of one other pitfall that's not immediately obvious with its fix - if you need your macro to generate code with specific symbols, you have to quote-unquote them. So e.g. to end up with just a after macro expansion, you'll have to use ~'a within a syntax quote.