Fork me on GitHub
#babashka
<
2019-11-22
>
jeroenvandijk12:11:24

I'm not sure if I read it correctly, but would porting boot-clj to babashka be a similar effort?

borkdude12:11:02

I think boot-clj has graalvm plans of their own and will be a massive undertaking for babashka, probably not going to try it

borkdude12:11:23

But the uberjar script is just a small shell script that could be implemented using babashka, not inside babashka

jeroenvandijk12:11:36

ok cool, yes at the very minimum a good start indeed

jeroenvandijk12:11:22

i agree that boot-clj would be a very complex undertaking, but I can see some benefits of something in this direction

borkdude12:11:16

yeah, I was thinking about the immutable fileset thing which could be complex, maybe a similar thing like tasks which can be invoked from the command line could be nice

borkdude12:11:51

it can already be done right now with functions + clojure.tools.cli though in babashka

borkdude12:11:08

and macros also work

jeroenvandijk12:11:22

yeah exactly I think the functional approach could work nicely. I'm experimenting with a first tool. Let's see how it evolves

borkdude12:11:41

yeah, it's always good to base such things on real use cases

borkdude12:11:25

maybe a makefile-ish thing in babashka could work. drawing inspiration from mach? https://github.com/juxt/mach it doesn't seem they are actively using it anymore though

jeroenvandijk12:11:24

Yeah I don't like the npm part, but given it's in clojurescript. would be nice to port to babashka

borkdude12:11:11

seems doable

jeroenvandijk14:11:22

I just noticed some-> is missing as macro. And then I was thinking can we not re-reuse clojure's macro code somehow e.g. via clojure.repl/source-fn. I tried something like this

(defn defmacro [mname args]
  (let [f (eval `(defn (symbol (str (name mname) "*")) [email protected]))]
    (macrofy @f)))

(defn extract-macro [supported-macro]
  (let [source-fn (fn
            [x]
            (when-let [v (resolve x)]
              (when-let [filepath (:file (meta v))]
                (when-let [strm (.getResourceAsStream (RT/baseLoader) filepath)]
                  (with-open [rdr (LineNumberReader. (InputStreamReader. strm))]
                    (dotimes [_ (dec (:line (meta v)))] (.readLine rdr))
                    (let [text (StringBuilder.)
                          pbr (proxy [PushbackReader] [rdr]
                                (read [] (let [i (proxy-super read)]
                                           (.append text (char i))
                                           i)))
                          read-opts (if (.endsWith ^String filepath "cljc") {:read-cond :allow} {})]
                      (if (= :unknown *read-eval*)
                        (throw (IllegalStateException. "Unable to read source while *read-eval* is :unknown."))
                        (read read-opts (PushbackReader. pbr)))
                      (binding [*read-eval* false]
                        (read-string (str text)))))))))]
    (eval (source-fn supported-macro))))
But this has some issues. @borkdude did you already consider something like this? Maybe I should just stick to copy pasting the code instead 🙂

borkdude14:11:18

@jeroenvandijk this worked for me:

(defmacro some->
  [expr & forms]
  (let [g (gensym)
        steps (map (fn [step] `(if (nil? ~g) nil (-> ~g ~step)))
                   forms)]
    `(let [~g ~expr
           [email protected](interleave (repeat g) (butlast steps))]
       ~(if (empty? steps)
          g
          (last steps)))))

(println (some-> {:a 1} :a))
(println (some-> {:a 1} :a ::c))

borkdude14:11:25

$ bb /tmp/foo.bb
1
nil

borkdude14:11:23

I actually typed : a : b : c without spaces, but slack screws up the code

jeroenvandijk14:11:47

cool, yes it seems to work in a test file

jeroenvandijk14:11:19

I guess my magic approach is a bit too lazy and unnecessary complexity anyway

borkdude14:11:52

but feel free to PR the some-> macro in sci, of course it should be added

jeroenvandijk14:11:40

yes, i'll do that. Thank you!

jeroenvandijk14:11:28

btw, do you want a (simple) test case for this as well?

borkdude14:11:49

it's always good to have more test cases

jeroenvandijk14:11:02

cool, i'll look into it

borkdude14:11:26

I haven't got test cases for all macros, because often it's just a 1-1 copy from clojure core

borkdude14:11:43

but if you're insisting that's fine 🙂

borkdude14:11:03

I've thought a little bit more about adding spec. I made a note in the issue saying that spec relies a lot on namespaces, so that might not fit well currently with babashka and sci, because these don't have namespaces yet

jeroenvandijk14:11:04

maybe just so it doesn't get removed by accident as sanity check

borkdude14:11:11

yes, very good

jeroenvandijk15:11:46

yeah i was actually thinking about namespaces too. I noticed that I need to do something like this in order to make it work in both clojure and babashka

#?(:clj (ns adgoji.application.cli
        ))

(require '[clojure.tools.cli :as cli])
maybe it's worth to implement as simple version of the ns macro just to get rid of the reader conditionals like this?

borkdude15:11:47

I can easily add support for ::foo keywords though

borkdude15:11:23

yes, but then I might just go all the way, since naming a namespace in the namespace form implies that you can have multiple

jeroenvandijk15:11:59

true. I don't know how complicated a namespace implementation would be

borkdude15:11:45

but since vars and namespaces are not real in sci (because sci doesn't mutate the host environment) I expect this to not work with spec out of the box

borkdude15:11:03

I'll probably have to make a sci-compatible version of clojure spec

jeroenvandijk15:11:36

sounds plausible. I guess this holds for many libraries

borkdude15:11:18

maybe, remains to be seen

borkdude15:11:36

we can just support a subset of spec, e.g. not fdef

borkdude15:11:47

which is very dependent on re-defining vars

jeroenvandijk15:11:48

ah that's what you mean

jeroenvandijk15:11:32

yeah I actually don't use fdef much. That could be implemented later if someone misses it?

jeroenvandijk15:11:21

multispec would be complicated as well I guess

jeroenvandijk15:11:46

That's something I do use a lot

jeroenvandijk15:11:06

If you ever want to implement multimethods or protocols, you also depend on redefining vars

jeroenvandijk15:11:35

I'm not sure what my use case would be for protocols in Babashka, but I can imagine it being nice for not having to deal with the reader conditional part

borkdude15:11:38

yeah well actually it could magically work out, since you can just do def in babashka and it will define something in the local environment

borkdude15:11:30

Probably something for the christmas break: https://github.com/borkdude/sci/issues/169

jeroenvandijk15:11:47

btw, I added some-> https://github.com/borkdude/sci/pull/168 but not sure why it doesn't work in babashka locally

borkdude15:11:29

does it work in the sci repo when you run lein run "(some-> {:a 1} :a)"

jeroenvandijk15:11:16

yep

➜  sci git:(feature/add-some-arrow) lein run "(some-> {:a 1} :a)"
1

borkdude15:11:32

and what about the same with lein bb?

jeroenvandijk15:11:19

➜  sci git:(feature/add-some-arrow) lein bb "(some-> {:a 1} :a)"
'bb' is not a task. See 'lein help'.

Did you mean this?
         do
➜  sci git:(feature/add-some-arrow) cd ..
➜  babashka git:(master) ✗ lein bb "(some-> {:a 1} :a)"
1
➜  babashka git:(master) ✗

jeroenvandijk15:11:28

guess that's good?

borkdude15:11:42

yes. don't know what you tried before, but this is good

jeroenvandijk15:11:01

I tried to compile with script/compile Not sure, ah well

borkdude15:11:45

maybe you didn't do ./bb but bb?

borkdude15:11:56

works here:

$ ./bb "(some-> {:a 1} :a)"
1

borkdude15:11:11

added to master

jeroenvandijk15:11:41

ok great, not sure. I'm doing too many things at once 🙈

borkdude19:11:47

details you find out when you start thinking about namespaces too deeply:

$ clj
Clojure 1.10.1
user=> (def x 1)
#'user/x
user=> (ns foo {:x x})
Syntax error compiling at (REPL:1:1).
Unable to resolve symbol: x in this context
foo=> (ns bar {:x user/x})
nil

borkdude19:11:02

actually this came up in clj-kondo, but might be nice to know with babashka too