babashka

https://github.com/babashka/babashka. Also see #sci, #nbb and #babashka-circleci-builds .
yubrshen 2020-11-21T01:59:30.408200Z

How can I access babashka.curl from emacs/cider/JVM environment? I now can access it in babashka repl, but I prefer the repl in emacs/cider. I guess that I need to add babashka/babashka {:mvn/version ???} to my project's deps.edn. What would be the version number, and if adding babashka/babashka {:mvn/version ???} is it the correct way? Do I need to perform a local installation of babashka in the local maven? Thanks!

yubrshen 2020-11-21T03:24:23.408300Z

Find the answer: borkdude/babashka {:mvn/version "0.2.3"}

yubrshen 2020-11-21T03:58:34.408900Z

What's the equivalent to

(clj-http.client/get "<https://api.stackexchange.com/2.2/sites>")
by curl? The following
(curl/get "<https://api.stackexchange.com/2.2/sites>")
seems resulting the zipp'ed:
Class: clojure.lang.PersistentArrayMap
Contents: 
  :status = 200
  :headers = { "content-encoding" "gzip", ... }
  :body = "�\b\0\0\0\0\0\0\n�]ko�8�+��Onb�z9����8�l�]��;���`d�bIIN�-���R�I���qb mM������#�b�M�{����&amp;��\b��O�q|���~~~��b�&lt;O8���+'�&amp;Ӽx����:�M'Q�rp�����$F...
  :err = ""
  :process = Process[pid=15826, exitValue=0]
  :exit = 0
Thank!

borkdude 2020-11-21T08:38:37.410400Z

@rahul080327 Do you think we could always add the --compressed flag or would this fail with websites that do not send compressed responses?

lispyclouds 2020-11-21T08:59:56.411Z

We could add it, it should work. My only reason of not adding it would be that there is an implicit behavior and since this is a thin wrapper over curl, its doing more than that and curl users might be surprised. If you ask me i would leave it out. What say?

lispyclouds 2020-11-21T09:00:59.411200Z

Ideally this should've been done by curl by inspecting the response header. HTTPie does it

lispyclouds 2020-11-21T09:01:57.411400Z

Even web browsers do that too

lispyclouds 2020-11-21T09:03:34.411600Z

another reason of not adding would be if later the meaning of --compressed changes based on the curl version, this would start failing for a non obvious reason

borkdude 2020-11-21T09:33:28.411800Z

@rahul080327 Yep, I agree. - We could support (get ... {:compress true}) to make this a little nicer. - Alternatively we could look at the content-encoding header ourselves and decompress but that may also fall into the category of doing too much.

borkdude 2020-11-21T09:33:47.412Z

I wonder if stackexchange should have sent the response in gzip encoding even when we didn't ask for that

borkdude 2020-11-21T09:35:03.412200Z

https://api.stackexchange.com/docs/compression

borkdude 2020-11-21T09:35:38.412400Z

> The likelihood of many applications not opting into compression, and being materially worse for it, is unacceptable.

borkdude 2020-11-21T09:39:21.412600Z

btw, if I start a server with:

$ bb -e '(org.httpkit.server/run-server (constantly {:body "hello"}) {:port 3000}) @(promise)'
and request that with --compressed it doesn't fail

lispyclouds 2020-11-21T10:02:31.413800Z

yeah curl's --compressed works even if the response isnt. maybe a fn to convert a map of opts -> cli args might be a good addition. But that should be about it, nothing more IMO

borkdude 2020-11-21T10:03:34.414Z

hmm, if it "always" works what's the reason not to enable it by default?

borkdude 2020-11-21T10:03:56.414200Z

is clj-http doing this by default?

borkdude 2020-11-21T10:04:10.414400Z

(i.e. requesting compressed responses)

lispyclouds 2020-11-21T10:04:22.414600Z

i would assume its inspecting the response header like HTTPie

lispyclouds 2020-11-21T10:04:47.414800Z

its Apache HTTP client under the hood, maybe thats doing it?

borkdude 2020-11-21T10:04:47.415Z

By default, clj-http will add the {"Accept-Encoding" "gzip, deflate"} header to requests, and automatically decompress the resulting gzip or deflate stream if the Content-Encoding header is found on the response. 

lispyclouds 2020-11-21T10:05:04.415200Z

yep

borkdude 2020-11-21T10:05:25.415400Z

we could also do that ourselves and not let curl do that

borkdude 2020-11-21T10:05:57.415600Z

> If this is undesired, the {:decompress-body false} option can be specified:

borkdude 2020-11-21T10:06:15.415800Z

but I guess it doesn't hurt to let curl do it

borkdude 2020-11-21T10:06:47.416Z

and it seems unlikely to me that --decompress will change its meaning. If curl will change the meaning of its args, then we're doomed anyway in other cases

lispyclouds 2020-11-21T10:06:55.416200Z

thats the thing, where do we draw the line between "a wrapper over curl" and "powered by curl".

borkdude 2020-11-21T10:07:38.416400Z

we're already using these sane defaults:

"curl" "--silent" "--show-error" "--location" "--dump-header"

borkdude 2020-11-21T10:07:55.416600Z

I would say powered by curl

borkdude 2020-11-21T10:08:31.416800Z

I think offering sane defaults with the possibility of opting out of it is a better way

lispyclouds 2020-11-21T10:08:48.417100Z

yeah if we are sending some flags already as some opinions, then yes we can do that

borkdude 2020-11-21T10:08:56.417300Z

follow locations is also not enabled by default, but this is the sane default

lispyclouds 2020-11-21T10:09:04.417500Z

makes sense

borkdude 2020-11-21T10:09:19.417700Z

I think curl is conservative because it was growing over time when compression maybe wasn't the default before

lispyclouds 2020-11-21T10:11:00.417900Z

or another thought process could be to implement clj-http's API as close as possible and say this is powered by curl. im of the opinion that if we follow one of the standards its more compilant?

borkdude 2020-11-21T10:12:02.418100Z

I think the more important thing is that it's useful for scripting and doesn't cause any beginner questions. Experts can dig into the docs to disable the defaults. I think this is what clj-http has done too.

borkdude 2020-11-21T10:12:30.418300Z

clj-http isn't a standard, it's a popular clojure lib that has influenced other libs

lispyclouds 2020-11-21T10:14:23.418900Z

right, i guess sticking with some sane defaults which most of us(beginners and others) may use and allow the internal knobs to be turned when needed would be a good choice.

borkdude 2020-11-21T10:15:14.419100Z

ok, I'll proceed with implementing this and documenting it

1
borkdude 2020-11-21T10:20:45.419500Z

Thanks for your input. Btw, I'm keeping some notes about http here: https://github.com/borkdude/babashka/wiki/HTTP-client-and-server-considerations

borkdude 2020-11-21T10:21:06.419700Z

I am contemplating of building a Java 11 based solution as babashka.http-client similar to what schmee has done.

borkdude 2020-11-21T10:21:26.419900Z

To have one canonical way of doing http in bb

lispyclouds 2020-11-21T10:25:26.420100Z

that would be super awesome. bb setting the standards 😎

lispyclouds 2020-11-21T10:26:40.420300Z

on a side note, had great success with java-http-clj. would love to see that being used. pretty much use it in all of my clj needs now 😄

borkdude 2020-11-21T10:27:30.420500Z

at the moment it's a bit messy having to choose between babashka.curl, org.httpkit or clj-http-lite, slurp, etc. once babashka.http-client is there, the other clients can slowly fade to the background and be deprecated over time. I want to give it some incubation time to flatten out bugs etc. of course

borkdude 2020-11-21T10:28:03.420700Z

yeah, that one looks great, but isn't yet feature complete.

borkdude 2020-11-21T10:28:14.420900Z

but it looks like a good start to build babashka.http-client on

lispyclouds 2020-11-21T10:28:29.421100Z

yep my vote would be there too

lispyclouds 2020-11-21T10:30:20.421300Z

also this is something we made based on OkHTTP: https://github.com/into-docker/unixsocket-http to be able to do UNIX sockets naturally. but quite skeptical for Graal due to the size bloat. Works on it fine though

borkdude 2020-11-21T10:30:42.421700Z

we can still use babashka.curl for that right?

borkdude 2020-11-21T10:31:05.421900Z

https://github.com/borkdude/babashka.curl#unix-sockets

lispyclouds 2020-11-21T10:31:36.422300Z

yep, in the off chance you dont have/want to install curl

borkdude 2020-11-21T10:31:37.422500Z

Since babashka.curl is so light-weight, I will forever keep it in bb. curl might cover some weird edge cases that Java doesn't out of the box, like this

lispyclouds 2020-11-21T10:32:47.422700Z

this lib is useful for embedding and lean docker deployments more i'd say.

borkdude 2020-11-21T10:34:10.422900Z

yeah

borkdude 2020-11-21T10:34:53.423100Z

(deftest compressed-test
  (is (-&gt; (curl/get "<https://api.stackexchange.com/2.2/sites>")
          :body (json/parse-string true) :items))
  (is (thrown?
       Exception
       (-&gt; (curl/get "<https://api.stackexchange.com/2.2/sites>"
                     {:compressed false})
           :body (json/parse-string true) :items))))

🎉 1
borkdude 2020-11-21T10:37:55.423400Z

### Compression

From babashka 0.2.4 onwards, this library will call `curl` with `--compressed`
by default. To opt out, pass `:compressed false` in the options.

lispyclouds 2020-11-21T10:39:27.423600Z

> Since babashka.curl is so light-weight, I will forever keep it in bb Just due to the target audience of bb and the use cases, the likely hood of curl being there along with bb is extremely high 😄

borkdude 2020-11-21T10:39:43.423800Z

yep. it's also part of the docker image

borkdude 2020-11-21T10:40:20.424Z

it's even on windows nowadays, along with tar

lispyclouds 2020-11-21T10:41:13.424200Z

windows is the new Mac. (shots fired) 😛

borkdude 2020-11-21T10:41:34.424400Z

it's kinda true ;)

borkdude 2020-11-21T10:42:06.424600Z

My next laptop might seriously be Windows, if not Ubuntu

borkdude 2020-11-21T10:42:26.424800Z

bozhidar is now running Windows as well

lispyclouds 2020-11-21T10:43:12.425Z

yep, was pleasantly surprised seeing the talk

borkdude 2020-11-21T10:44:01.425200Z

haven't watched it yet. any good?

borkdude 2020-11-21T10:44:09.425400Z

any new information I should see I mean

borkdude 2020-11-21T10:44:57.425600Z

Ugh, another github actions instability thing.

lispyclouds 2020-11-21T10:45:11.426Z

well some of the lesser known features of CIDER, some Emacs hiccups and the fact that Emacs running on Windows like magic

borkdude 2020-11-21T10:45:47.426200Z

I kinda found that out myself before bozhidar was on Windows with all the WSL2 stuff

lispyclouds 2020-11-21T10:45:55.426400Z

Use DeLaGuardo/setup-clojure@master

borkdude 2020-11-21T10:45:58.426600Z

is he running emacs on Windows natively or through wsl2?

lispyclouds 2020-11-21T10:46:08.426800Z

WSL2

borkdude 2020-11-21T10:46:16.427Z

yeah, I love it

borkdude 2020-11-21T10:46:56.427200Z

I'm eyeing this laptop: https://bestware.com/en/schenker-via-15-pro.html AMD + 64GB... if my current laptop stopped working.

🤯 1
lispyclouds 2020-11-21T10:47:01.427500Z

Fix for actions: https://github.com/bob-cd/bob/blob/main/.github/workflows/ci.yml#L33

borkdude 2020-11-21T10:49:33.428Z

Fix worked!

borkdude 2020-11-21T10:52:16.428200Z

## Java 11 client
Java 11 client. Based on <https://github.com/schmee/java-http-clj> minus the specs.
Adds 2.28MB to the binary. See branch `java-11-client`. 
This is pretty reasonable I'd say

lispyclouds 2020-11-21T10:53:09.428400Z

yeah i love the zero dependency aspect of it

borkdude 2020-11-21T10:53:28.428600Z

This is another one: https://github.com/exoscale/coax

borkdude 2020-11-21T10:54:01.429Z

whoops no sorry. This one: https://github.com/exoscale/telex

lispyclouds 2020-11-21T10:55:56.429400Z

quite extensive interceptors

borkdude 2020-11-21T10:57:08.429600Z

All of these things are still moving targets, so babashka.http-client will also probably take another year or so

borkdude 2020-11-21T10:57:16.429800Z

but the seeds are planted

borkdude 2020-11-21T10:58:07.430Z

anyway @yubrshen, I hope we answered your question :)

lispyclouds 2020-11-21T06:53:24.409300Z

you can pass args to the curl command using the :raw-args in an option map. This should work (curl/get "<https://api.stackexchange.com/2.2/sites>" {:raw-args ["--compressed"]})

lispyclouds 2020-11-21T07:01:17.409800Z

also, you could just add babashka.curl with borkdude/babashka.curl {:git/url "<https://github.com/borkdude/babashka.curl>" :sha "59e03c18c7f16f4d2328b52d37b988d6761d2a3d"} without needing to add the whole of babashka 😄

borkdude 2020-11-21T08:36:59.410200Z

@yubrshen This seems to be the way to do it with curl:

$ bb -e '(-&gt; (curl/get "<https://api.stackexchange.com/2.2/sites>" {:raw-args ["--compressed"]}) :body json/parse-string keys)'
("items" "has_more" "quota_max" "quota_remaining")

yubrshen 2020-11-23T02:23:11.453500Z

@borkdude Thanks for taking the time to help me. I'll study the documentation of curl/get to understanding the details of the parameters.

borkdude 2020-11-23T08:42:33.453700Z

So {:raw-args ["--compressed"]} is only necessary here because stackexchange always sends compressed responses, but curl does not decompress compressed responses automatically. In the next release of babashka I will pass this flag to curl by default, but for now you have to use it like this.

2020-11-21T09:53:51.413300Z

grasp is really cool by the way. Really helpful.

borkdude 2020-11-21T09:54:15.413700Z

Cool! There is also a #grasp channel now

borkdude 2020-11-21T11:23:49.430600Z

If someone is looking for some OSS issues to work on, here's a list: https://gist.github.com/borkdude/18af5d96c6465ce64144f03636fda3dc

nivekuil 2020-11-21T11:43:55.430700Z

maybe an odd request: has anyone used clara rules with babashka?

nivekuil 2020-11-21T11:47:39.430800Z

here's some context -- I'll share more about it if people are interested, but the gist of it is generating docker compose/swarm commands with nice reusable code https://gist.github.com/nivekuil/05f507d9e53cf53da6918728ece54b40 so I never have to write yaml again :)

borkdude 2020-11-21T11:48:42.431300Z

@kevin842 currently that doesn't work, it seems:

borkdude@MBP2019 /tmp $ export BABASHKA_CLASSPATH=$(clojure -Spath -Sdeps '{:deps {com.cerner/clara-rules {:mvn/version "0.21.0"}}}')
borkdude@MBP2019 /tmp $ bb -e "(require '[clara.rules :as cr])"
----- Error --------------------------------------------------------------------
Type:     clojure.lang.ExceptionInfo
Message:  Could not resolve symbol: load
Location: clojure/reflect.clj:123:2
Phase:    analysis

----- Context ------------------------------------------------------------------
119:   {:added "1.3"}
120:   [obj &amp; options]
121:   (apply type-reflect (if (class? obj) obj (class obj)) options))
122:
123: (load "reflect/java")
      ^--- Could not resolve symbol: load
We could look into this to make it work, possibly.

borkdude 2020-11-21T11:49:09.431600Z

I think we should then add clojure.reflect into bb

borkdude 2020-11-21T11:49:55.432100Z

oh hmm, clara also uses its own Java classes: https://github.com/cerner/clara-rules/tree/main/src/main/java/clara/rules so it won't work anyway, unless we add it into bb itself

borkdude 2020-11-21T11:50:29.432400Z

so for now I would say: use clojure on the JVM if you want clara

nivekuil 2020-11-21T11:52:20.432600Z

ah, but https://github.com/oakes/odoyle-rules/blob/master/src/odoyle/rules.cljc is pure clojure?

borkdude 2020-11-21T11:54:09.433300Z

You could try it out using the same thing as above and let us know what you run into

nivekuil 2020-11-21T11:55:27.433400Z

I think spec may be a problem

bb -e "(require '[odoyle.rules :as o])"             ----- Error -------------------------------------------------------------------- Type:     clojure.lang.ExceptionInfo Message:  Could not resolve symbol: clojure.lang.Compiler/demunge Location: clojure/spec/alpha.clj:128:16 Phase:    analysis  ----- Context ------------------------------------------------------------------ 124: (defn- fn-sym [^Object f] 125:   (let [[_ f-ns f-n] (re-matches #"(.*)\$(.*?)(__[0-9]+)?" (.. f getClass getName))] 126:     ;; check for anonymous function 127:     (when (not= "fn" f-n) 128:       (symbol (clojure.lang.Compiler/demunge f-ns) (clojure.lang.Compiler/demunge f-n)))))                     ^--- Could not resolve symbol: clojure.lang.Compiler/demunge 129:  130: (extend-protocol Specize 131:   clojure.lang.Keyword 132:   (specize* ([k] (specize* (reg-resolve! k))) 133:             ([k _] (specize* (reg-resolve! k))))

borkdude 2020-11-21T11:57:15.433900Z

ah right, that one again. you could request if doyle moved its specs into a different namespace perhaps so they become optional

borkdude 2020-11-21T11:57:33.434200Z

possibly starting out with your own fork

nivekuil 2020-11-21T11:58:19.434300Z

that makes sense, thanks

noprompt 2020-11-21T21:07:15.436200Z

Is there a ticket for this?

user=&gt; (reify clojure.lang.IFn (invoke [this x]))
clojure.lang.ExceptionInfo: No reify factory for: #{clojure.lang.IFn} [at &lt;repl&gt;:18:1]

borkdude 2020-11-21T21:10:48.437300Z

@noprompt why not: (fn [this x])?

noprompt 2020-11-21T21:16:34.437900Z

Here’s how I got to this

(defn factory [x]
  (fn [y] ,,,))

(let [a (factory 1)
      b (factory 1)]
  (assoc {} a 1 b 2))
;; =&gt; No
{#function[meander.interpreter.epsilon/factory/fn--26726] 1,
 #function[meander.interpreter.epsilon/factory/fn--26726] 2}

(defn factory [x]
  (reify
    clojure.lang.IFn
    (invoke [this y]
      ,,,)))

(let [a (factory 1)
      b (factory 1)]
  (assoc {} a 1 b 2))
;; =&gt; NO
{#object[meander.interpreter.epsilon$factory$reify__26734 0x814ee48e "meander.interpreter.epsilon$factory$reify__26734@814ee48e"]
 1,
 #object[meander.interpreter.epsilon$factory$reify__26734 0x9a1f044a "meander.interpreter.epsilon$factory$reify__26734@9a1f044a"]
 2}


(defn factory [x]
  (reify
    Object
    (hashCode [this]
      (hash x))

    (equals [this that]
      (and (identical? (class this) (class that))
           (= (hash this) (hash that))))

    clojure.lang.IHashEq
    (hasheq [this]
      (hash x))

    clojure.lang.IFn
    (invoke [this y]
      ,,,)))

(let [a (factory 1)
      b (factory 1)]
  (assoc {} a 1 b 2))
;; =&gt; YES
{#object[meander.interpreter.epsilon$factory$reify__26752 0x3ba9821a "meander.interpreter.epsilon$factory$reify__26752@53075d44"]
 2}

noprompt 2020-11-21T21:17:48.438900Z

I could make a record for this but, at the moment, I’d rather not.

noprompt 2020-11-21T21:19:29.440200Z

I could also make a protocol for semantic purposes instead of using IFn but I also wanted to avoid that for the moment.

borkdude 2020-11-21T21:19:41.440700Z

Due to limitations with GraalVM this is really hard to support.

borkdude 2020-11-21T21:19:52.441100Z

Can't you maybe just use maps?

noprompt 2020-11-21T21:20:00.441400Z

The heart of it really boils down wanting some leverage as a maintainer. 😛

borkdude 2020-11-21T21:20:01.441500Z

or functions with metadata on them

noprompt 2020-11-21T21:20:25.442100Z

Ah, good idea! Forgot about that trick. 😉

noprompt 2020-11-21T21:29:25.442400Z

(defn factory [x]
  (with-meta (fn [y] ,,,)
    {:id x}))

(let [a (factory 1)
      b (factory 1)]
  (assoc {}
         [(class a) (meta a)] 1
         [(class b) (meta b)] 2))
;; =&gt; Yes
{[clojure.lang.AFunction$1 {:id 1}] 2}
😎

noprompt 2020-11-21T21:29:50.442800Z

Not ideal but it works.

borkdude 2020-11-21T21:38:40.443400Z

There's currently a bug in bb so this doesn't work:

(meta ^:foo (fn [])) ;;=&gt; nil
but with-meta works. I'll make an issue for this.

borkdude 2020-11-21T21:43:04.445500Z

@noprompt fyi, in babashka / sci+graalvm I have to create classes ahead of time for all possible combinations of interfaces, because I can't create classes at runtime in GraalVM. So I end up with this macro that generates all combinations of subsets of interfaces that are supported with reify in bb: https://github.com/borkdude/babashka/blob/master/src/babashka/impl/reify.clj Then whenever someone calls reify, the interpreter looks at the reified interfaces and then picks the class that has the matching combination of interfaces.

borkdude 2020-11-21T21:44:41.446900Z

So theoretically what you want can be supported (it's not right now because there isn't an AOT-ed class with these combinations of classes), but if fns with metadata work equally well, I would prefer that

borkdude 2020-11-21T21:44:54.447200Z

it's also less code to process for the interpreter, so better initial load time

noprompt 2020-11-21T21:49:04.448300Z

Thanks for the details. 👍

borkdude 2020-11-21T21:49:10.448700Z

That, or you can just use :bb branches to make a little bit different logic for bb only

noprompt 2020-11-21T21:49:39.449200Z

I may have to use protocols/records in the end.

noprompt 2020-11-21T21:51:53.449600Z

Well, not quite.

noprompt 2020-11-21T21:52:24.450Z

Bit of a hack but it will suffice.