Fork me on GitHub

the last place I worked use chef, but you don't need to install lein


build a jar, deploy the jar

Javier Gonzalez-Compte00:06:17

gotcha so no need for jenkins as well to deploy


oof, well, that is a whole other thing, if you are building a ci box, for whatever reason our "devops" team refused to take care of our ci box at the last job so it wasn't managed using chef, just a shell script


so I have very little good advice there

Javier Gonzalez-Compte00:06:51

yea i'm in the process of putting our clojure app on a server so looking for best practices since its a new job and the previous place i worked we only used the jar method you said but the devops want to use jenkins and chef

Javier Gonzalez-Compte01:06:16

nvm I was thinking it the wrong way just have jenkins do the lein test, uberjar and deploy the uberjar into the server

Javier Gonzalez-Compte01:06:14

idk why I was thinking I would need lein in the server for the chef recipe to build since the chef recipe is being used just to automate building the server


create a lein uberwar and drop it on a java container


(s/gen (aws/response-spec-key secretsmanager :GetSecretValue))

Execution error (FileNotFoundException) at clj.dal/eval18601 (REPL:1).
Could not locate clojure/test/check/generators__init.class, clojure/test/check/generators.clj or clojure/test/check/generators.cljc on classpath.


am I missing an import?


You are missing a dep on test.check


... but the stuff generated isn't super helpful


thanks though


I have a map: '({:a 1 :b2}) collection .. I want to take the first map and get all the keys ... but what I'm tryin' ain't workin'


(-> '({:a 1 :b 2}) first keys)

👍 4

my uuid-string function looks identical to this one ... does this mean I need to add the APACHE license to my project?


@johnjelinek I would expect everyone who needs a UUID string has written that exact same function independently (so, no, you only need to keep the license when you copy in code from an OSS project).


so ... it won't look like I copied uuid-string if I rename the function and then I don't have to adopt the license?


You pretty much can't write that function any other way.


For example, I've never looked at that code and here's what's in our (proprietary) code at work

(defn uuid
  "Return a standard Type 4 (random) UUID."
  ^java.util.UUID []

(defn uuid-as-string ^String []
  (str (uuid)))

👍 4

what gives? my :gen-class from yesterday doesn't work today when I AOT?


clojure -A:aot
Exception in thread "main" Unexpected error macroexpanding clojure.core/gen-class at (clj/core.clj:1:1).
        at clojure.lang.Compiler.macroexpand1(
        at clojure.lang.Compiler.analyzeSeq(
        at clojure.lang.Compiler.analyze(
        at clojure.lang.Compiler.analyze(
        at clojure.lang.Compiler$BodyExpr$Parser.parse(
        at clojure.lang.Compiler$TryExpr$Parser.parse(
        at clojure.lang.Compiler.analyzeSeq(
        at clojure.lang.Compiler.analyze(
        at clojure.lang.Compiler.analyze(
        at clojure.lang.Compiler$BodyExpr$Parser.parse(
        at clojure.lang.Compiler$FnMethod.parse(
        at clojure.lang.Compiler$FnExpr.parse(
        at clojure.lang.Compiler.analyzeSeq(
        at clojure.lang.Compiler.analyze(
        at clojure.lang.Compiler.analyzeSeq(
        at clojure.lang.Compiler.analyze(
        at clojure.lang.Compiler.analyze(
        at clojure.lang.Compiler$InvokeExpr.parse(
        at clojure.lang.Compiler.analyzeSeq(
        at clojure.lang.Compiler.analyze(
        at clojure.lang.Compiler.analyze(
        at clojure.lang.Compiler.compile1(
        at ...
Caused by: java.lang.ClassNotFoundException:
        at java.base/


I've confirmed the dep is still there:

{:deps {com.amazonaws/aws-lambda-java-events {:mvn/version "2.2.6"}}}


and I see the classes that sit adjacent to the interface in the repl



(ns clj.core
   :implements [])
  (:require [ :as json]
            [clojure.string :as s]
            [ :as io]
            [clojure.pprint :refer [pprint]]))

(defn handle-event [event]
  (pprint event)
  {:who-done-it (get-in event [:records 0 :request-parameters :source-ip-address])
   :bucket-owner (get-in event [:records 0 :s3 :bucket :owner-identity :principal-id])})

(defn- key->keyword [key-string]
  (-> key-string
      (s/replace #"([a-z])([A-Z])" "$1-$2")
      (s/replace #"([A-Z]+)([A-Z])" "$1-$2")

(defn -handleRequest [this is os context]
  (let [w (io/writer os)]
    (-> (json/read (io/reader is) :key-fn key->keyword)
        (json/write w))
    (.flush w)))


here's my deps.edn:

{:deps {org.clojure/clojure {:mvn/version "1.10.0"}
        org.clojure/data.json {:mvn/version "0.2.6"}
        com.amazonaws/aws-lambda-java-events {:mvn/version "2.2.6"} {:mvn/version "0.8.305"} {:mvn/version ""} {:mvn/version "707.2.405.0"}
        http-kit {:mvn/version "2.3.0"}
       ;  org.clojure/test.check {:mvn/version "0.10.0-alpha4"}
 :aliases {:pack {:extra-deps {pack/pack.alpha {:git/url ""
                                                :sha "81b9e47d992b17aa3e3af1a47aed1f0287ebe9b8"}}
                  :main-opts ["-m"]}
           :aot {:extra-paths ["classes"]
                 :main-opts ["-e" "(compile,'clj.core)"]}}}


# tree src/
└── clj
    ├── core.clj
    ├── dal.clj
    └── util.clj


maybe it doesn't like the new files I introduced into the namespace


nope, took those files out and no change


You're missing at least one dep...


(! 513)-> clj -Sdeps '{:deps {com.amazonaws/aws-lambda-java-events {:mvn/version "2.2.6"} com.amazonaws/aws-lambda-java-core {:mvn/version "1.2.0"}}}'
Downloading: com/amazonaws/aws-lambda-java-core/1.2.0/aws-lambda-java-core-1.2.0.pom from 
Downloading: com/amazonaws/aws-lambda-java-core/1.2.0/aws-lambda-java-core-1.2.0.jar from 
Clojure 1.10.1
user=> (import ' )


You need aws-lambda-java-core for RequestStreamHandler


(I tried with just aws-lambda-java-events and got the same error you saw)


I'm not sure how I would've figured that out


@seancorfield: thanks for your help. Worked great


I searched for that full class name. Looked in the Github repo where it showed up. That listed for libraries to depend on. Events, Core, and two logging adapter libraries.


You actually posted the link to that interface -- in aws-lambda-java-core

😮 4
Cora (she/her)04:06:10

in core.async, do go ... threads? coroutines? not sure what to call them, but do they park for any reason other than channel operations?

Cora (she/her)04:06:44

what do the API docs mean by "any visible calls"

Cora (she/her)04:06:51

I feel like I need a list of gotchas

Cora (she/her)04:06:23

"go is a macro that takes its body and examines it for any channel operations. It will turn the body into a state machine. Upon reaching any blocking operation, the state machine will be 'parked' and the actual thread of control will be released"

Cora (she/her)04:06:45

so yeah, the channel operations have to be in the location the go block is called

Cora (she/her)04:06:28

so it can definitely block the threads pretty badly still

Cora (she/her)04:06:36

project loom can't come quick enough


@corasaurus-hex CSP -- Communicating Sequential Processes -- is the background to read for this.

Cora (she/her)04:06:30

I've used the very similar mechanisms in golang and I rewrote this ruby library which adds channels+select to ruby


Yeah, Go comes from CSP as well.


Yeah, I learned it via Occam in the early 80's.

Cora (she/her)04:06:40

I was just bummed that the go blocks weren't actually some variation of coroutine, parked on a wider variety of blocking operations

Cora (she/her)04:06:11

I can't wait for project loom to land and for this to become a reality

Cora (she/her)04:06:18

if it ever lands


A lot of solid production code is built on core.async...

Cora (she/her)04:06:31

I don't mean to slight it, I can see that it's going to be useful to me

Cora (she/her)04:06:08

I just want coroutines since there's a lot lower risk of blocking threads

Cora (she/her)04:06:31

looks like there was a fosdem talk on loom this year


I wouldn't worry too much about futures, given what you can already do 🙂

Cora (she/her)05:06:42

ehhh I'm used to erlang/elixir-land, where I can basically have as many processes as I could possibly have of file descriptors open. you more or less don't ever have to worry about blocking threads

Cora (she/her)05:06:22

I'm not really worried I just think it would be rad if I never had to worry

Cora (she/her)05:06:21

that video more or less explains the dream in the first five minutes

Cora (she/her)05:06:40

I didn't know that it included tail call elimination, too


BEAM is an interesting environment. I took a workshop back at an FP conference years ago and was impressed by what Erlang can do. But I've been on the JVM for... 22 years at this point... so I both love it and hate it and I've learned to live with its limitations in order to take advantage of everything else.

Cora (she/her)05:06:29

oh my god they want to serialize continuations, so you could serialize it, send it to another machine, and then continue it there :shocked_face_with_exploding_head:

Cora (she/her)05:06:09

I like both beam and the jvm, I just see the lack of lightweight threads as a modeling weakness on the jvm. you have to resort to some nasty stuff if you want to take advantage of the maximum number of tcp connections on a box

Cora (she/her)05:06:52

but also just how you model a problem. if you can spin up a lightweight thread for every damn thing and don't have to worry about exhausting a pool, that's really nice

Cora (she/her)05:06:49

fibers on the right, threads on the left. fibers need less memory and will end up switching much faster than threads


I've never felt the loss of that -- in 22 years on the JVM. Make of that what you will.


Hum.. interesting aside, Java actually used to have only green threads


Like in Java 1 or some old version like that


But it later moved to OS threads, because they are better for true parallelism and thus performance


And I guess convenient


Shows how the world goes in circle


That said, I think you're missing the point of CSP

Cora (she/her)06:06:11

pretty sure they meant me

Cora (she/her)06:06:48

which is a little funny to me, but whatever


OK. I mean, CSP was the fundamental stuff I studied at uni in my postgrad days back in the very early 80's, so I don't think I was missing the point of it 🙂


Ya, I meant Nate. Also, I had misunderstood the statement. So disregard. I thought we were wondering why the channel parks on put/take


Okay, fair enough. Context is everything 🙂


Or I don't know about CSP, but the point of the "go" macro


Blocking is a feature


Its how you synchronize producers and consumers and coordinate the concurrent processes


From my understanding, you have to go to great lengths of additional complexity in Actor models to perform such coordination


And I think that was one reason Rich Hickey went with CSP over Actors

Cora (she/her)05:06:02

blocking is fine so long as the execution is parked during the blocking and the OS thread is available to run other go blocks. this is only true if the blocking is due to channels and not anything else


Also, isn't Erlang a preemptive scheduler? Are you sure its threads are lightweight?


Oh. Sorry, I thought you meant parking, or blocking the go-process


Ya, unfortunately, Clojure can't prevent the actual thread running the process from being accidentally blocked


So you just have to know what you're doing

Cora (she/her)06:06:06

right, and project loom has the potential to change that


Is project loom implementing a preemptive scheduler?

Cora (she/her)06:06:31

beam schedules what it calls processes, lightweight threads, across a thread pool. the processes are preempted all the time based on some calculation of the amount of work they've done or if blocked so you end up with a lot of consistent forward progress across all the processes

Cora (she/her)06:06:53

check the vid or links above for more on loom

Cora (she/her)06:06:51

it's a similar deal though, coroutines with custom schedulers that execute the coroutines on a thread pool

Cora (she/her)06:06:28

right now they're using fork join as the scheduled

Cora (she/her)06:06:32

also has the potential to give us tail call elimination, meaning we could do unlimited recursion without risk of blowing the stack

Cora (she/her)06:06:58

the possibilities are amazing


So, that's exactly what core.async does


I don't think the issue has anything to do with that, but all to do with blocking IO


Does erlang have blocking io?


If so, it would also block one of the threads in its pool. Meaning there is one less thread to run the processes on.


So unless Project Loom can magically rewrite calls to Blocking IO or other blocking operations, it would suffer the same fate.


Well... except core.async isn't preemptive. So your go block won't park on its own


Erlang has its own IO implementations so that you can do IO without blocking the underlying OS thread.


Ya, so, that's really the difference here (minus preemption)


The issue is just Java legacy blocking operations


And how so many things rely on them


When using core.async go block, you just need to make sure you're only using non blocking IO


That’s the appeal of the Loom - it aims to make the legacy IO stuff just work (tm)


Okay, that's where it gets interesting


I guess a virtual layer that handles the callback for you

Cora (she/her)06:06:12

read the implementation section


That, I don’t remember. But the videos that they have put out so far are really interesting and full of the nitty gritty.


> The implementation of the networking APIs in the and java.nio.channels packages have as been updated so that fibers doing blocking I/O operations park, rather than block in a system call, when a socket is not ready for I/O. When a socket is not ready for I/O it is registered with a background multiplexer thread. The fiber is then unpacked when the socket is ready for I/O. These same blocking I/O are also updated to support cancellation. If a fiber is cancelled while in a blocking I/O operation then it will abort with an IOException.

Cora (she/her)06:06:20

I posted one of the most recent videos above


Hum... still requires the use of nio then


But I guess ya, they can just update all IO code and have a if called by Fiber.. then park instead of blocking


Ya, that be neat


It’s a huge undertaking but they seem to be chipping at it and making good progress.

Cora (she/her)06:06:50

I imagine you could have different schedulers for different groups of fibers, which would make divvying up of execution resources pretty interesting


Regarding blocking IO, isn’t it true that most file access IO is blocking at the OS level and it’s hard to overcome that? That’s what I’ve been hearing for years. So if you make socket calls parking you get huge practical value.

Cora (she/her)06:06:58

in that last video they said they had parking working with all the IO APIs except files

Cora (she/her)06:06:41

so yeah, you might be onto something


I had gone down a big rabbit whole about this a while ago


It seems only Windows can do non blocking file IO


Yay for Windows? 🙂


Ya, it is really advanced in its async IO


In this case


You can actually hand off a callback to the OS and register a user thread with it. And on interrupt from the peripheral, it'll switch to your registered user thread, hand of execution to your callback and pass you the IO result


So it's truly push all the way down


Where as linux only really supports multiplexed io


Does this low-level stuff really matter tho' in the grand scale of things? (serious question)


But it provides an API called like Asyncio which abstract it away, but it's still multiplexed under the hood


I mean, we build production scale stuff on Windows, Linux... without worrying about this...?


Well, if you wanted to go from say 10k requests/s to 100 000k maybe 😋


I.mean, probably not, probably the gain are minor in performance but wtv


But how many people need to do that?


I think the vast majority "don't care". Regular JVM stuff is "fast enough", right?


Ya, I'd agree


I worry that we go down rabbit holes in the name of (unnecessary) performance.


That's why I'm with you, for all practical purpose, core.async solves all the use cases I have. When I need it, I'm just careful not to block


Google, Facebook, Twitter... they care but most of us don't.


(and I'm not even sure they care really, in all of these cases)


I doubt they even care. All this is about scaling single host concurrency, and for most things, especially big tech, adding a few more hosts is way cheaper then the maintenance cost of more complex software


I actually think startup care more


I see some people's "obsession" with async I/O and I'm like "really?". I get asked about it with and next.jdbc for example...


They often have the assumption it's faster.. I don't really get it, especially not with Java. You can easily do multiple things in parallel in different threads if you need to.


Yeah, and in the real world they'd be surprised how often it isn't faster to do stuff in parallel. Tuning is hard.


True.. recently did some benchmarks with rust where the same obsession lives. Because they decoupled getting data from Kafka, and processing it async, it was putting unnecessary stress on Kafka, making the performance worse..


Shops trying to run everything on like 1 box


Cause they can't afford more servers


Async JDBC is horribly non-portable. Why would anyone inflict that pain upon themselves?


I have no experience with it. Is that just an issue with async JDBC kinda just sucking? Or the async-ness makes it inherently less portable?


Only some DBs have drivers that support async.


(and each of them are different)


So when folks ask me to support async in or next.jdbc I'm like, yeah, send me a PR for portable async 🙂


PostgreSQL has a fairly well-established async driver I believe... and some (many?) PG users tend to assume the world is like PG 🙂


Hum... I mean. I can imagine how in 10 years, everything will just have moved to async IO, and it'll just be the new io standard


And everything might be a little faster.


Yup, and in ten years, I'll be happy to release jdbc.async 🙂


I've actually never used async IO in Java and/or Clojure yet teehi


I wonder if the APIs for it are simple


I'm hoping it's just like takes a callback which is a callable


And you could just therefore give it a Clojure fn which puts to the channel when called back


Actually that's the part I can't quite imagine for Loom


Could it park and like... block the return, hum. I guess it could. So you'd basically be doing a blocking call, but the thread is a lightweight one. Ya that's nice


I guess it could be leveraged by http servers and what not


But, really, these are implementation details in the grand scale of things...


async io just sounds like another horrible trap for hard to reproduce, non deterministic, bugs


Ya, I mean, Java can already scale. So all it could further do is save you a few extra hosts


If you look at this from a higher level, you can't tell how a lot of the low-level stuff actually operates, and that's a good thing.


We should be focusing on higher-level stuff, not lower-level stuff.

👍 4

That's true.


I think that would be the defining factor for Loom as well. If its invisible, then great. You'll save some memory and shave of a few milliseconds in context switches without having to do anything. But if it's like a whole buy in, redesign everything, etc. Then meh

✔️ 4

I wonder how many people use go blocks in Clojure (not ClojureScript). I've used thread, but never had a real need for go.


We have a few things that are very heavy on async and rely on core.async


But out of 80k+ lines it's not much


Cool! I guess I did maybe have a use case for it recently. Had to process a millions+ csv file and make a request to an API for each row.


And then aggregate back their results


And that's probably not a good use case for it


Used an executor for it. But I guess async IO and go could have worked


Well.. actually no lll


Frankly an executor is better


Because its not like I can call the API with a million concurrent requests


The fleet maybe handles a few hundreds max concurrent, of which other prod systems call into. That's the thing with real life, you always need throttling, back offs, circuit breakers, etc.


Seriously, Executors are great. There's one for every use case too. That with just futures take you really far already


I'm always sad when beginners just jump on core.async. And don't start with future, executor and pmap

Ahmed Hassan10:06:45

Where do I start from while trying to learn these?

Ahmed Hassan10:06:23

I think you meant java.util.concurrent stuff should be use prior to using core.async.


I mean that core.async is a pretty complicated tool, which shines only for particular advanced scenarios


And yes, learning future, promise with normal threads, agents, pmap and the java.uitl.concurrent stuff should happen first in my opinion


They will be easier and take you quite far on their own

Ahmed Hassan21:06:30

@didibus Thanks. What are examples of particular advanced cases?


There's two scenarios in my mind. The first one is when you want to wait on a number of things to complete, which could complete in any order, and want to process them as they complete.


The second one is when you have a producer/consumer scenario


Where you have multiple producers and multiple consumers


But in either case, it can be possible to do it with futures and executors as well. So you need to first make sure that doing it with those would either not be as fast as you want, like limit the concurrency you actually need (very rare that you need a lot of concurrency), or because core.async would actually result in simpler code for the use case.


That's why some people say: "use core.async when you want to trade maintainability for extreme concurrency"

Ahmed Hassan05:06:46

And does it shine in browser? There's Promesa library for promises too. Which works with both Clojure and ClojureScript.


Oh,yes. I'm talking about Clojure JVM here


In browser and NodeJs, you have a lot less options for concurrency, so core.async is more helpful more often there


One challenge I've had with Promesa is that it's not using native ES6 promises.


Hum... but you know, it seems they changed to native promise now


That said, I'm not an expert on ClojureScript and don't use it actively. So I'm the wrong person to ask for it


But I think in general core.async is more useful in it, and it's also less tricky to use, because of the single threaded nature of JS and the fact that most IO is non-blocking

Ahmed Hassan05:06:45

Question arising in my mind is where one should choose Promesa vs core.async. Kind of best scenarios and tradeoffs in ClojureScript and browser for both libraries.


So, I think in general you should choose Promesa


Or even just use built in promises with JS interop


That's my intuition.


The basic difference between core.async and Promesa isnthe underlying primitive


Promesa uses promise, and core.async uses channel


A promise is like a variable, its single element only. Where a channel is like a sequence, it's a stream of elements


So core.async shines in scenarios where you have streams of concurrent things.


And promise shines when you have a few one off tasks to run concurrently which only return 1 thing and are done


Does that make sense?


In Java though, executors can also be used for processing streams concurrently. And are often a simpler tool for that. But there is no such thing in ClojureScript. Similarly, core.async in ClojureScript has less gotchas.

Ahmed Hassan05:06:24

Great. @didibus Thanks. Now, I think it's better to start from Promesa and upgrade to core.async if needed.


Yes, I believe so


But in Clojure it's best to start with futures and promise from Clojure


And then you can try promesa after


What's an example use case you use go blocks for?


@corasaurus-hex By the way, you should also have a look at At this point, you might know more about the whole space of coroutines vs fibers vs green threads vs cps, etc. But apparently core.async aren't full generalized coroutines and cloroutine implements a full genral coroutine in Clojure.


@corasaurus-hex And for completeness since this is the beginner channel. In ClojureScript land, when targeting NodeJS, you don't have to worry about accidentally blocking the go-blocks, since Node only supports async non-blocking IO. Some people do prefer NodeJS over the JVM as their backend runtime and use ClojureScript for that reason instead.


Hi , My map is like {2004 [{:id 1, :name water, :attribute_name Color, :attribute_value blue} ,{:id 2, :name water, :attribute_name formula, :attribute_value H2O} ,{:id 3, :name water, :attribute_name State, :attribute_value liquid}] } i want to create following map like this {2004 [{:Color blue, :formula H2O ,:State liquid}]} Please guys help me


towards something like this?

  [{:id 1, :name "water", :attribute_name "Color", :attribute_value "blue"}
  ,{:id 2, :name "water",  :attribute_name "formula", :attribute_value "H2O"}
  ,{:id 3, :name "water",  :attribute_name "State", :attribute_value "liquid"}]
  (map #(hash-map (:attribute_name %) (:attribute_value %)))
  (into {}))
=> {"Color" "blue", "formula" "H2O", "State" "liquid"}


It worked .... thanks in ton 🙂


Hello, has anyone used Cucumber with Clojure? If so, which command do I have to use to run a tagged Test Case?


Hey, does anyone have any resources on how to include, import, and wrap homegrown Java classes in Clojure projects? I'm going to start working on wrapping an object model that a colleague wrote in Clojure. The classes are pretty involved, they're used to model CME market data for live feeds, so I'm looking for some resources on how to import the jar from the Java project in my Clojure project and how to properly implement a wrapper so that we can work with it more idiomatically on the Clojure side. Any resources would be a huge help! Thank you! Edit: I'm reading through the following things for a primer as well 1. 2. 3. 4.


If you can get the jar in the class path (differs based on your build tool, I guess?) then you’re off to the races, everything should work with the Java interop syntax.


As for more idiomatic, most Clojure apis would like to deal with maps, collections and seqs - so are if you can write some wrappers that do this kind of translation.

Cora (she/her)15:06:41

@didibus I did run across cloroutine but didn't have much time to dive into it


is there a macro to aesthetically improve this (foo value (:bar (baz x y))) ?


What's wrong with that?


nothing, wanted to play with -> ->> and cousins 😉


I actually prefer the nested one showed above but almost every clojure I see in the wild is obsessed with -> ->>


the straightforward translation via arrows is (-> x (bar y) (:bar) (->> (foo value)))


Or maybe with as->.


or alternately (->> y (bar x) (:bar) (foo value)) - it really depends on what x and y are and what they mean


Speaking for myself, -> or ->> in the context of some other threading operator gives me the heebie jeebies.


the other threading macros are designed to be nested under ->


is this specific to -> or applies to ->> too?


it's specific to ->, because all the other arrow macros take a value as the first arg


so any other arrow macro can be used as a child of ->


got it, thanks!


I think the fact that that doesn't work in reverse is part of the reason why I avoid doing it at all.


but it never needs to work in reverse - with -> as the parent, all you need to do is fall out the end of another form, and you're back in a position where all the other threading types are available


all that's required is that -> be used at the top with the other threading types inside it


I think I might need to see an example to understand this last bit.


because of how arrow macros turn nesting into sequential forms, there's never a need for -> to be inside ->>

(quux (bar baz (dog foo cat)))

;; becomes

(-> foo
    (dog cat)
    (->> (bar baz))


it might be helpful to try to come up with a case where it looks like -> needs to be inside ->> - in every case there's a trivial way to just put ->> inside ->, and fall out the end of ->>, instead


Oh yeah? That's interesting.


that's why as-> takes a value first then a binding name for example


and the semantics are simple - if -> is the parent, you just transition from the -> rule to the threading of the child threading macro


line breaks help


x and y are single values. For small compositions stuff I guess its personal preference but seem handy for much bigger comps


@lockdown- everything is a value - what I mean is in (bar x y) are you conceptually doing something to x that passes forward, or are you doing something to y that passes forward, or is bar something that combines x and y as "peers"


yeah, the latter


x and y end with the call to bar


is this specific to -> or applies to ->> too?

Dave Goodchild18:06:45

So are threading macros akin to the pipe operator in Elixir and easy to overuse?


there is also comp which I like but don't see it used much


@lockdown- in that case (->> (bar x y) (:bar) (foo value)) is how I'd do it, since neither x or y is conceptually carried forward to the next step


@buddhamagnet they are not an operator, they are a syntax transform


now that I look at the elixir docs yes - they directly do what |> does in elixir, perhaps a difference being that they are strictly a syntactic transform that happens before compilation, so they can do things that don't make syntactic sense


user=> (->> (+ x y) (let [x 11 y 31]))

Dave Goodchild18:06:09

thanks for the clarification


x and y are being used "illegally" here because they are syntactically outside the block that defines them, but it works because the form is rearranged before evaluation to something that is valid


I don't think elixir allows this


(also don't use code like that, it's to demonstrate that it's a syntax transform :D)


they can be overused, but usually (when combined with good formatting) they make code clearer

Suni Masuno20:06:52

I have a string iso8601 date, and wanna add or subtract from the year ending up with an otherwise identical string. What's the best path there? I'm worried parse/format could get caught in funny formatting problems. String manip makes me feel guilty. Is there already a function for that somewhere? I see clj-time.core and clj-time.format in a few places... so maybe I should go learn those?


if it is really iso8601, the format is standard and well supported, it should be pretty straightforward

Suni Masuno20:06:43

I don't know if I've ever seen dates straightforward... -_- But yeah, ok, how would you do that?


I would unequivocally recommend clj-time - it’s great.


I would anti recommend clj-time because it's based on a deprecated library (Joda time)

☝️ 4

I'll second that and I'm (one of) clj-time's maintainer! 🙂


user=> (str (.plusYears (java.time.ZonedDateTime/parse "2019-06-12T20:18:17.744-04:00" java.time.format.DateTimeFormatter/ISO_OFFSET_DATE_TIME) 5))


^^^yes do that


the new time stuff built into java 7+ (if I recall, maybe 8+?) is very good

💯 8

I'll second that and I'm (one of) clj-time's maintainer! 🙂


@seancorfield have you considered re-implementing clj-time in terms of Java’s built-in time classes?


Requiring beginners to learn Java interop simply to handle basic data types (i.e. dates / times) seems a bit naff.


There was a very long discussion thread about that in an issue over several years -- and the consensus was that it would not be possible without breaking backward compatibility.


Makes sense.


clj-time's readme suggests looking at if you want syntactic sugar.


There's also a new cljc-based wrapper for Java Time that has a compatible JS implementation inside it. I have not looked at that yet.


Nice - was not aware of that until now.


(the discussion took nearly two years!)


I wonder if clojars supports a deprecated flag, at the library level? That would be quite handy for situations like this.


It does not, unfortunately.


(although I guess updating the project's name/description which would end up in pom.xml and therefore display on Clojars might be a good idea)


Yeah - shame there isn’t something that build tools (`lein`, tools.deps, etc.) can consume.


Also, is the cljc library you mentioned ?


Yes, thank you! Launched recently at Clojure/North.


By the way, beginners should learn Java interop, and they should do so relatively early on.


Sure, but I don’t think it’s controversial to state that being required to learn Java interop before learning about basic data types in Clojure is indeed too early.


Though perhaps one counter argument is that because the JVM’s date/time data types have been so fscked for so long, beginners should go through that hazing ritual as early as possible… (I’m not sure I buy that argument mind you, given how much Clojure sanitises the JVM’s behaviour in other areas)


Ya, I'm not saying that's the first thing you want to learn. But you should learn interop early on. Clojure's date data type for example is java.util.Date

(type #inst "2019-01-01")
;> java.util.Date


If you look at the Clojure standard library, you'll see that there are many omissions and holes for what would count as complete. These were not accidental. Whatever Java provided which was good enough and didn't need improving was left to Java interop, on purpose


If you look at the rationale for Clojure you'll see: > because I wanted A Lisp for Functional Programming *symbiotic with an established Platform*


Emphasis is mine


That symbiosis is key to Clojure and rooted in its design choices


As a beginner, or someone coming to Clojure, you have to be onboard with this.


If you reject it, and reject Java and the JVM platform, Clojure will just be extra painful and will seem odd, incomplete, not batteries included.


Also read the Languages And Platforms section


FWIW I no longer consider myself a beginner (7 years with Clojure) - just here to help other beginners out.


And Java for a bit over a decade before that, so well aware of the strengths and weaknesses of the underlying platform. I just don’t think that arguments defending Clojure’s uneven sanitisation of the JVM hold much water (beyond the usual realities of finite time).


How do you interpret the rationale? I always interpreted it as not trying to abstract away the JVM, but be symbiotic with it, as an explicit design goal.


So, basically accepting that trying to not be leaky is too problematic. And accepting the leaky abstraction, but in a controlled and purposeful way was better


Specifically this part: Language built for platform vs language ported-to platform Many new languages still take 'Language as platform' approach When ported, have platform-on-platform issues Memory management, type-system, threading issues Library duplication If original language based on C, some extension libraries written in C don’t come over


If Clojure had it's own date time library in core, it seems it would fall prey to the "library duplication"


We’re talking about fundamental data types here, and I don’t think it’s appropriate to require beginners to detour via Java interop if they’re at that basic level of learning about the language.


And it’s not like Clojure doesn’t already sanitise other basic Java data types (e.g. numerics, data structures).


Ya that's fair. I was talking in a broader sense.


Java interop is not like an esoteric thing that you almost never reach for. It's one of the main component of Clojure


Wrappers over it should only be used when they add additional value. Like make it considerably easier to use, provide additional functionality on top, etc.


Or if you need a common abstraction between dialects like Clojure and ClojureScript


Or learn Java and interop will come naturally 😉

Alex Sumner22:06:17

This is my first post here, so hello all. Talking of Java interop. I was looking at the examples from the Java Interop chapter of Programming Clojure and playing around with them. I came across something I don't understand. One example given is the following:


Hi 👋 I'm trying to use the following lib in a sample project. However, I got an error about:

1. Unhandled
   Could not locate embodie/core__init.class, embodie/core.clj or
   embodie/core.cljc on classpath.
My project.clj contains the following info:
(defproject hello_world_embody "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url ""
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url ""}
  :dependencies [[org.clojure/clojure "1.10.0"]
                 [defunkt/embodie "1.0.0"]]
  :repl-options {:init-ns hello-world-embody.core}
  :main ^:skip-aot hello-world-embody.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})
Someone knows what my problem is? Thanks!


did you restart your repl after adding the dependency?


In addition, I got the same error when I ran lein run


I can reproduce the error in a fresh project, it seems like they broke their build(?)


I just looked in the 1.0.0 jar - it contains no clojure code or class files, and the pom file describing its deps pulls in no embodie projects


they need to fix their release, they built it wrong


Thanks for the help!


lein caches your deps into $HOME/.m2/repository/ by default, and you can look up the jar files there. A good editor can open a jar file as if it were a directory - they are just zip files with some extra rules (fixed trivial typos)

👀 4

@noisesmith Thank you. Sincerely I didn't keep in mind to look up the jar content That's a good idea 🙂

Alex Sumner22:06:16

trying again, haven't got the hang of entering new lines in Slack... This is my first post here, so hello all. Talking of Java interop. I was looking at the examples from the Java Interop chapter of Programming Clojure and playing around with them. I came across something I don't understand. One example given is along the lines of the following: (defn sax_parse [source handler] (.. SAXParserFactory newInstance newSAXParser (parse (InputSource. (StringReader. source)) handler))) that works if called with an XML string as source and something extending org.xml.sax.helpers.DefaultHandler as handler. If I expand the .. member access threading macro I get: (defn sax_parse [source handler] (. (. (. SAXParserFactory newInstance) newSAXParser) (parse (InputSource. (StringReader. source)) handler))) which also works. But if I try to rewrite the last line using the -> thread-first macro then whether I try: (defn sax_parse [source handler] (.. SAXParserFactory newInstance newSAXParser (-> source StringReader. InputSource. (parse handler)))) or: (defn sax_parse [source handler] (. (. (. SAXParserFactory newInstance) newSAXParser) (-> source StringReader. InputSource. (parse handler)))) then it doesn't work. The defn can't be evaluated, I get Syntax error (ClassNotFoundException) compiling at (test.clj:16:3). StringReader. It seems to be taking StringReader. as a class name. Can anyone tell me why (please)?


try wrapping it in parens


(-> source (StringReader.) etc.


yeah, StringReader. without parens will not read properly

Alex Sumner22:06:47

OK, thanks, but if I try: (defn sax_parse [source handler] (. (. (. SAXParserFactory newInstance) newSAXParser) (-> source (StringReader.) InputSource. (parse handler)))) I get a different error: Syntax error (IllegalArgumentException) compiling new at (test.clj:17:17). No matching ctor found for class


or you can use (new StringReader) which is what (StringReader.) expands to when read

Alex Sumner22:06:38

What's confusing me is that (macroexpand '(-> source StringReader. InputSource. (parse handler)))

Alex Sumner22:06:50

(macroexpand '(-> source StringReader. InputSource. (parse handler)))gives


also this would be much easier to read with sugared . forms - so (.newSAXParser (SAXParserFactory/newInstance) ...)`

Alex Sumner23:06:16

<sorry keep forgetting about shit enter> What's confusing me is that (macroexpand '(-> source StringReader. InputSource. (parse handler))) gives (parse (InputSource. (StringReader. source)) handler) which is what worked in the first place?


yes, that's actually weird - that should work even without the parens actually


are you sure you are evaluating that code in the right environment?

Alex Sumner23:06:28

I think so, everything else works. I'm just using C-c C-e in Emacs CIDER


@alex.sumner you might want to look at Slack's formatting with triple-backticks so you get

code that looks like this

Alex Sumner23:06:10

Yes, that would be better! Thanks.


And under Preferences > Advanced, check the option that pressing enter inside triple-backticks does not send the message. That way you can type three backticks, press enter, type lots of code with newlines, then three more backticks (and then enter will send the message).

Alex Sumner23:06:36

OK, so like this? `


You need that preferences change too.


` to start


and ` to end


triple-backslash CODE CODE CODE triple-backslash


you can also use control-enter to make a new line without sending a message

Alex Sumner23:06:51

OK, now we're getting somewhere
Still don't know about the problem with ->
but getting somewhere with Slack!

🎉 4

I think in

(defn sax_parse [source handler]
 (. (. (. SAXParserFactory newInstance) newSAXParser)
    (-> source (StringReader.) InputSource. (parse handler))))
that first line is a problem. I think
(.newSAXParser (SAXParserFactory/newInstance))
makes part of it more readable but you still have that outer (. which would seem to be trying to process the whole (-> ...) form as a method call?


oh yeah, that is an issue


Oh, .parse is a method on the parser created?


the issue is the latter


(defn sax_parse [source handler]
 (. (. (. SAXParserFactory newInstance) newSAXParser)
    (-> source (StringReader.) InputSource. (parse handler))))
parse handler is in the innermost form accidentally. but anyways this is impossible to read and would never appear in practice


(defn sax-parse [source handler]
  (.parse (.newSAXParser (SAXParserFactory/newInstance))
          (-> source (StringReader.) (InputSource.))
Is that what you're looking for?


(with kebab-case name instead of snake_case BTW for idiomatic Clojure)

Alex Sumner23:06:38

What I actually wanted to try was

(defn sax_parse [source handler]
  (.. SAXParserFactory newInstance newSAXParser
      (-> source StringReader. InputSource. (parse handler))))


(let [factory (SAXParserFactory/newInstance)
      parser (.newParser factory)


naming things will bring some clarity, too


Mixing .. and -> can be pretty confusing to read (and, as we see here, often confusing to write).


(defn sax-parse [source handler]
  (let [factory (SAXParserFactory/newInstance)
        parser (.newParser factory)
        input (-> source (StringReader.) (InputSource.))]
    (.parse parser input handler)))


(whatever the method signature is for parse, I can't remember)


.. is good for method chains in Java:


(.. Foo getOrder (getLineItem 2) getPrice)

Alex Sumner23:06:12

OK, thanks. I am still worried that

(A (B))
works but
(A (C))
does not, when
(macroexpand '(C))
evaluates to
Where A is either
.. SAXParserFactory newInstance newSAXParser
or its macroexpansion `

Alex Sumner23:06:29

. (. (. SAXParserFactory newInstance) newSAXParser)


(hit up arrow to edit the last thing you wrote, btw)

Alex Sumner23:06:42

B is

parse (InputSource. (StringReader. source)) handler
and C is
-> source StringReader. InputSource. (parse handler)


(-> source StringReader. InputSource. (parse handler))
doesn't work because the last form becomes:
(parse PREVIOUS handler)


where you need


(.parse PREVIOUS handler)


note the .


(assuming that parse is a java method)


this is a difference between .. and other forms

Alex Sumner23:06:12

Yes parse is a method


so without the leading . it thinks it's either a clojure function or a local in a let-binding


@ghadi But he has an outer (. parser (-> ,,,)) form in the above


SAXParser.parse takes an input and a handler, so if you want to translate this to the "fancy style"


it would become:


(defn sax-parse
  [source handler]
  (let [input (-> source (StringReader.) (InputSource.))]
    (.. (SAXParserFactory/newInstance)
        (newParser)   ;;  no parens necessary here, but I'm including them
        (parse input handler))))


sorry, edited a typo

Alex Sumner23:06:25

OK thanks. That looks better. I am still confused as to why

(defn sax_parse [source handler]
 (.. SAXParserFactory newInstance newSAXParser
     (parse (InputSource. (StringReader. source)) handler)))
does work however, and it does (and it is basically what's in the Programming Clojure example)


I think it could have to do with .. being expanded before -> ?


if you think about it - .. can't be clever about the transform it does, if it finds a form starting with -> it would need to find a method named -> right?


there are two differences between that and what I pasted: 1) the first argument to parse (the input thingy) is inlined


2) (.. SAXParserFactory newInstance) is equivalent to (. SAXParserFactory newInstance) which is equivalent to (SAXParserFactory/newInstance)


in practice I never see the first two styles for calling a static method -- always (Class/staticmethod)


If it helps

user=> (macroexpand-all '(.. SAXParserFactory newInstance newSAXParser (-> source StringReader. InputSource. (parse handler))))
(. (. (. SAXParserFactory newInstance) newSAXParser) (parse (new InputSource (new StringReader source)) handler))
user=> (macroexpand '(.. SAXParserFactory newInstance newSAXParser (-> source StringReader. InputSource. (parse handler))))
(. (. (. SAXParserFactory newInstance) newSAXParser) (-> source StringReader. InputSource. (parse handler)))
I think the . special form is treating the (-> ,,,) form as a literal form to turn into a method call?


right, that's my theory as well - it can't be clever about expanding its method forms, because it doesn't expect them to be expandable, it expects method names to turn into method invocations


I wish there was a way to ask the compiler what it turns (. object (-> arg-1 (method arg-2))) into...


but the middle style (. Class methodName) is the "final" macroexpansion that the compile operates on


user=> (macroexpand '(java.util.Objects/nonNull a b c))
(. java.util.Objects nonNull a b c)


I think this is an issue too

user=> (macroexpand '(.. foo (-> x (bar y) (someMethod))))
(. foo (-> x (bar y) (someMethod)))
- I don't expect . to be clever about discovering someMethod


user=> (. "hello" (-> "l" (replace "L")))
Execution error (IllegalArgumentException) at user/eval163 (REPL:1).
No matching method __GT_ found taking 2 args for class java.lang.String
user=> (. "hello" (replace "l" "L"))
The . form doesn't know what to do with ->


and it definitely shouldn't try to invoke the initial symbol in a form


(. "hello" (-> "l" (replace "L"))) doesn't trigger macroexpansion


on the arguments, that is


yeah, there's no sane way it could, for the operator position at least


So it turns it into (.-> "hello" "l' (replace "L")) which is munged to (.__GT_ "hello" "l" (replace "L"))


So that's why you can't mix .. and -> like this.


i don't think it does yes, misread you Sean


those are the parsed forms for lists beginning with .


Yup. And going back to .. to be more like @alex.sumner’s original code

user=> (.. "HELLO" toLowerCase (-> "l" (replace "L")))
Execution error (IllegalArgumentException) at user/eval170 (REPL:1).
No matching method __GT_ found taking 2 args for class java.lang.String
user=> (.. "HELLO" toLowerCase (replace "l" "L"))


(because you get (.__GT_ (.toLowerCase "HELLO") "l" (replace "L")) because -> is munged to __GT_ in JVM code)


extra dot at the beginning of the last line^ reading skillz no good right now


Thank you for the question/puzzle @alex.sumner -- it's been a great learning experience for us old 'uns too!

Alex Sumner23:06:22

OK, thanks all of you! So, to check my understanding, you can't put macros inside

forms (and therefore inside
forms either) because they won't be expanded? I was working on the basis that if your macro A expands to B then anything with A in it iis equivalent to the same thing with A replaced by B. That's clearly not always the case though.


you can, but not in the method position


because it intentionally doesn't expand that position - you don't expect it to look up parse and replace it right?


it's used as is


you can safely use -> to construct args to one of the methods

Alex Sumner23:06:25

OK, thanks again.


this all makes me wonder if explicit message passing OO isn't a lot more sane, I'm sure it doesn't optimize as nicely as what the JVM does, but it would make higher order code much simpler to construct


Going back to your original code @alex.sumner this should work:

(defn sax-parse [source handler]
  (.. SAXParserFactory newInstance newSAXParser
      (parse (-> source StringReader. InputSource.) handler)))


(but I haven't tested it)