cherry

borkdude 2022-08-01T08:26:10.467179Z

Today I'll take a crack at macros I think

πŸ’ 2
borkdude 2022-08-01T10:57:53.476809Z

Got this example working locally now:

$ cat corpus/macros.cljs
(ns macros)

(defn do-twice [_f _e x]
  `(do ~x ~x))
$ cat corpus/macro_usage.cljs
(ns macro-usage
  (:require-macros ["./macros.mjs" :refer [do-twice]]))

(do-twice (prn :hello))
$ node corpus/macro_usage.mjs
:hello
:hello

πŸ’ͺ 2
borkdude 2022-08-01T10:58:38.247199Z

(didn't implement defmacro yet, this is why the do-twice macro uses defn )

borkdude 2022-08-01T10:59:23.149189Z

This is what macro_usage.mjs looks like:

import { prn, keyword } from 'cherry-cljs/cljs.core.js'
prn(keyword("hello"));
prn(keyword("hello"));

export {  }

mkvlr 2022-08-01T11:48:20.319339Z

> Macros need to run in the same environment as the transpiler. Therefore it makes sense to force users to declare macros in separate .cljc files: .cljc so the cherry transpiler can run in both clojure/bb and JS environments. this would be doing it how normal (JVM-compiled) ClojureScript does it today, right?

mkvlr 2022-08-01T11:50:05.573669Z

also just came across https://github.com/mfikes/chivorcam

borkdude 2022-08-01T11:50:26.824299Z

yes. but the above approach is different and only works in JS. This is the "transpiled macro" approach. First transpile the macro .cljs file. Then load the JS in the compiler and use the macros during compilation.

mkvlr 2022-08-01T11:51:06.705859Z

ah so you went with the second option in the issue

borkdude 2022-08-01T11:51:39.604709Z

yes, exploring that right now

πŸ‘ 1
borkdude 2022-08-01T11:52:45.879349Z

You could transpile your macros and distribute them along your libraries and it would "just work" TM with the normal cherry compiler which is small

mkvlr 2022-08-01T11:54:59.883869Z

sounds good, I guess the put them into a different file question is independent of this, right?

borkdude 2022-08-01T11:55:55.084689Z

if you don't put them in separate files, it becomes more complicated (like in .cljs). it could be made to work, but you'll have to maintain some bookkeeping in a separate .edn file about which vars are macros, etc.

mkvlr 2022-08-01T11:56:31.944129Z

hmm, but self-hosted cljs can just declare them in the same file using defmacro

borkdude 2022-08-01T11:56:48.360019Z

and a single file would them compile down to two separate files: one .mjs and one $macros.mjs.

borkdude 2022-08-01T11:57:26.270809Z

> but self-hosted cljs can just declare them in the same file using This is what you think, but actually it doesn't work like "just declare them in the same file"

πŸ€” 1
borkdude 2022-08-01T11:58:16.028149Z

cljs.user=> (defmacro defp [name & body] (def name (do @body)))` #'cljs.user/defp cljs.user=> (defp x 1) ^ WARNING: Use of undeclared Var cljs.user/x at line 1 (def nil (do))

borkdude 2022-08-01T11:58:51.049529Z

self-hosted CLJS compiles a single namespace also into two separate namespaces: one macro namespace and one normal .cljs namespace

borkdude 2022-08-01T11:58:58.959349Z

it's exactly the same problem

mkvlr 2022-08-01T11:59:50.830169Z

ok, but as a user I can just declare them in the same file

borkdude 2022-08-01T12:00:24.144769Z

yes, with caveats

πŸ‘ 1
borkdude 2022-08-01T12:01:19.272819Z

as I said here: https://clojurians.slack.com/archives/C03QZH5PG6M/p1659354955084689 it's a similar problem, CLJS has done extra work to enable this. cherry could do too, but first gotta decide which approach is the most promising

πŸ‘ 1
mkvlr 2022-08-01T12:02:14.355069Z

thanks for explaining

borkdude 2022-08-01T12:02:53.800289Z

If you write a .cljc file and put a println in there, and run it through self-hosted CLJS, you will see it printed twice because first it evaluates the whole thing as a macro namespace

borkdude 2022-08-01T12:04:08.794169Z

(ns foo
  (:require-macros [foo]))

(defmacro foobar [])

(prn :foo)
$ plk /tmp/foo.cljc
WARNING: foo is a single segment namespace at line 1
WARNING: foo$macros is a single segment namespace at line 1
:foo
:foo

mkvlr 2022-08-01T12:04:15.495569Z

oh, I thought you could write defmacro in a .cljs in self-host

borkdude 2022-08-01T12:04:27.676859Z

you can but that doesn't work like you think it does

borkdude 2022-08-01T12:04:37.145689Z

in #nbb however it does work like you think it does ;)

🀌 1
πŸ™Œ 1
borkdude 2022-08-01T12:05:48.932129Z

(ns foo)

(defmacro foobar [x]
  `(do ~x))

(prn (foobar 1))
$ plk /tmp/foo.cljs
WARNING: foo is a single segment namespace at line 1
(do nil)

borkdude 2022-08-01T12:07:07.060309Z

So with an interpreter approach (vs the transpiled macro approach), you could do the above, but then you would have to evaluate the .cljs namespace first in the interpreter so macros are loaded, and then you can transpile the .cljs file (while ignoring the macro definition but expanding the calls)

borkdude 2022-08-01T12:07:31.638609Z

But this complicates sharing the macro between other files

borkdude 2022-08-01T12:08:01.870269Z

In the transpiled approach you could also run through the .cljs file once looking for macros, emit only the macro stuff to a separate .mjs file, then load that, etc.

borkdude 2022-08-01T12:08:12.054699Z

This is where the trade-offs currently are

borkdude 2022-08-01T12:09:09.519159Z

But supporting "inline" macros is a bit tricky as you now understand, so we can defer that as an optimization: it's not impossible but too early to optimize for

borkdude 2022-08-01T12:09:49.090999Z

I'd say we push the transpiled macro approach further so cherry becomes a more standalone thing vs relying on SCI

borkdude 2022-08-01T12:10:53.757599Z

If that doesn't work out, we can always evaluate the SCI route then

mkvlr 2022-08-01T12:10:58.195629Z

yeah, sounds good, especially if you think you can still add support for inline macros down the road

borkdude 2022-08-01T12:11:19.849299Z

yeah, that's unrelated to either direction

borkdude 2022-08-01T12:12:02.637849Z

btw, this approach also allows you to write macros in JS files. I don't know why you would do that, but you could ;)

borkdude 2022-08-01T12:12:22.124129Z

Maybe pull in preact for server side rendering in a macro or so

borkdude 2022-08-01T12:21:15.939629Z

By convention we could have

foo.mjs
foo$macro.mjs
and when you (:require ["./foo.mjs" :refer [bar]) then foo$macros would be loaded in the compiler and there would be a check if bar is macro

borkdude 2022-08-01T12:23:08.149109Z

likewise, inline macros would be emitted to foo$macros.mjs instead of foo.mjs

mkvlr 2022-08-01T13:40:15.212939Z

https://github.com/aidenybai/million is the new preact πŸ™ƒ

mkvlr 2022-08-01T13:41:20.770289Z

conincidentally also works well with a compiler: > While alternative libraries like https://preactjs.com/ reduce bundle sizes by efficient code design, Million takes it a step further by leveraging compilation to make a quantum leap in improving bundle size and render speed.

borkdude 2022-08-01T13:42:18.801809Z

Yeah, this is why maybe compiling to .jsx (see channel) might be better, then the JS tooling can just do whatever like they like this week

borkdude 2022-08-01T12:14:52.295919Z

Off topic: a different thought I had was that cherry could directly compile to .jsx so your JS tools could process the "HTML" stuff

mkvlr 2022-08-01T13:43:18.188209Z

or have a macro that compiles to jsx?

borkdude 2022-08-01T13:43:39.924139Z

yes, exactly, but a macro is part of the compilation :)

mkvlr 2022-08-01T13:43:49.781349Z

yep

mkvlr 2022-08-01T13:43:59.347979Z

sorry, talking about the same thing then

mkvlr 2022-08-01T13:45:21.267709Z

btw @jackrusher and @mhuebert did some interesting experiments a while back using toxi’s umbrella libs like https://github.com/thi-ng/umbrella/tree/develop/packages/hiccup-html

πŸ‘€ 1
2022-08-01T14:08:28.726419Z

These are very good libraries. It would be nice to have an elegant way to use them from CLJS πŸ™‚

2022-08-01T13:45:26.834649Z

@mhuebert has joined the channel

borkdude 2022-08-01T19:37:08.648209Z

Demo of macro support: https://twitter.com/borkdude/status/1554189378464915460 and found 1 bug in cljs proper while implementing this :) https://clojurians.slack.com/archives/C07UQ678E/p1659381397764059

πŸ‘ 5