Fork me on GitHub
#clojure
<
2019-01-05
>
phill00:01:30

@trailcapital @hiredman case is different in Clojure and ClojureScript. Clojure case does not evaluate the stanza handles. CLJS does.

hiredman00:01:58

cljs also doesn't have clojure.lang.Keyword

phill00:01:17

well the trivia is the point

hiredman00:01:39

case is like the millionth place where clojure and cljs diverge, and if you complain about someone will popup and say "oh, its just an edge case"

hiredman00:01:47

"As discussed in that thread we're not re-breaking a thing we broke 2 years ago. It's simply not that important and far too late."

phill00:01:27

Hey that Jira issue has a link to a Google Group thread that I started!

phill00:01:05

Anyway opinions differ as to whether Clojure's case is the broken one.

hiredman00:01:42

that is nonsense

hiredman00:01:34

clojure's case behaves the way it does because it has to. case provides constant time dispatch, in order to compute the jump table it has to have the values on hand, which in the general case means they have to be unevaluated constants

mfikes00:01:22

ClojureScript’s case is constant-time as well.

mfikes00:01:40

(For most uses.)

hiredman00:01:28

hilariously the non-constant time cases are the cases where its semantics match clojure's, if I recall

hiredman00:01:26

oh right, that is exactly what the ticket I linked is for

hiredman00:01:50

the cond fallback the clojurescript macro uses treats symbols differently then the constant time dispatch part

mfikes01:01:43

Yeah, there’s also the unfortunate handling of const vars http://blog.fikesfarm.com/posts/2015-06-15-clojurescript-case-constants.html

hiredman01:01:13

basically clojurescript's case was implemented by someone who didn't entirely understand case, and now it is enshrined forever as an asterisk on any discussion of clojurescript / clojure portability

mfikes01:01:50

Right. A similar thing happened with binding (the parallel aspect) but that was fixed.

mfikes01:01:54

It feels like we are down to this hastening only once or twice a year now (where a mistaken difference is discovered and a decision needs to be taken on whether to fix it).

mfikes01:01:42

Another recent one sitting there is the handling of empty regex s, but those differ anyway

hiredman01:01:08

I just remember arguing with dnolen on irc way back because he insisted that clojurescript just be referred to as clojure, and clearly there are differences enough that the distinction must be made

mfikes01:01:44

Hah, yeah. The same concept arises with self-hosted ClojureScript. Ideally you could just pretend it is the same language, but perhaps it is another (albeit close) dialect

hiredman01:01:21

clojure, clojure', clojure'', ...

mfikes01:01:56

Yep. Three targets.

mfikes01:01:16

0h forgot about CLR

noisesmith01:01:01

CLR is the most compatible with the JVM version

dpsutton01:01:08

who works on that version these days?

noisesmith01:01:07

David Miller

noisesmith01:01:20

if CLR wasn't a microsoft project and there was a tool like lein for clojure-clr, I bet Clojure-CLR would see a lot more use

dpsutton01:01:21

wow. that is a ton of work. and largely thankless i suppose

mfikes01:01:27

I thought I read somewhere that, back in the day, you would upgrade from one version of Lisp to another by running your source through some Lisp designed to translate :)

noisesmith01:01:19

@dpsutton from what I gather the similarities between CLR / JVM and C# / Java mean it's not as bad as you might think

mfikes01:01:26

You could have like Swift 1, Swift 2, Swift 3, or whatever it is up to now

dpsutton01:01:32

i like everything about C# except how tied to windows it was when i used it. winforms, etc, visual studio, etc

dpsutton01:01:44

haven't followed along with the core stuff.

hiredman01:01:00

there is a very interesting paper somewhere about doing that with sbcl

noisesmith01:01:03

mono is usable, but I think MS' reputation still holds CLR back

hiredman01:01:27

it is kind of tricky to think about and do in a clean away

hiredman01:01:00

because it is very easy to confuse the source and the target language when you are using the same language and are generating an in memory image to dump to disk

mfikes01:01:19

Well we could have a spec for Clojure, confirm the code, and then unform it to a spec for Clojure2

dpsutton01:01:37

doesn't elm kinda do this when they do compiler upgrades? you can run something over your code to get the mechanical stuff

mfikes01:01:26

Oh wow 😮 gotta read that

dpsutton01:01:30

just downloaded that. thanks!

didibus01:01:27

I mean, porting Clojure to other platforms is tricky both because Clojure leaves some semantics up to Java, adopts some weirdnesses because of Java, and also lacks a formal spec or a cross target test suite.

mfikes01:01:25

Maybe it is a bit like Unix / C, with just the right amount of looseness at the bottom to let it spread easily.

mfikes01:01:24

In 2026 we will be coding to the CLOJIX standard.

hiredman01:01:16

just don't forget to run ./configure before you build

noisesmith01:01:52

and google how the hell to run autotools and then run them before running configure

dpsutton01:01:03

that paper describes kinda self hosted cljs right? it grabs a js environment, feeds it enough info to become a cljs environment and then compiles the cljs compiler?

pauld09:01:28

Can anyone here point me to any documentation on the meaning of ::keys for destructuring?

pauld09:01:36

Notice the second colon.

pauld09:01:35

I am reading code that uses this and I don't understand how it works.

schmee10:01:36

@pauld that is called a “namespaced keyword”: https://clojure.org/reference/reader#_literals

schmee10:01:01

basically, ::foo expands to :the-namespace-your-are-in/foo

pauld10:01:09

Ok, I know the difference now via some experimentation. I just thought it weird I could not find any documentation. I did find this: https://clojureverse.org/t/poll-destructuring-namespaced-keywords/1388/5

pauld10:01:44

though that page still doesn't mention "::keys"

pauld10:01:22

The meaning is sort of obvious I guess once you try it out.

borkdude10:01:29

so (pop []) errors, but (peek []) and (pop nil) just work… what’s the reasoning behind that?

kah0ona10:01:24

hello friends, Question about concurrency: if i have two http requests (ring) coming in, in quick succession, I want to make sure that the first one can do some work, and block the second request for a little bit, and after the first request is done, the other can continue (it’s ok if the 2nd one takes a few milliseconds longer to succeed) How should I go about this? I started out with a global atom, for simplicity let’s say it’s a boolean busy? which is true when it’s busy, and false otherwise but how to ‘wait’ for the atom? I could of course make a spinning loop but that feels very dirty. Which construct from clojure would be suitable?

borkdude10:01:05

why not make one endpoint for it, that synchronizes the work?

kah0ona10:01:06

more context: I am integrating with an oauth2 API, which requires me to refresh access tokens

kah0ona10:01:15

the refreshing is done in a ring middleware

kah0ona10:01:37

but if two calls to this api follow each other up quite quickly (which could happen) for the same user

kah0ona10:01:57

and the first one is busy refreshing the token via a HTTP POST call to the external API

kah0ona10:01:18

then the second one will fail because the external api says: ‘you are using an outdated token’

phil10:01:56

I think you're setting yourself up for a world of hurt trying to block that.

borkdude10:01:16

you could use locking if the user’s token is refreshing

kah0ona10:01:27

aah thanks

borkdude10:01:30

but I’m not sure if that’s a good idea

phil10:01:55

I'd say just let them receive the 401 and do the refresh dance.

borkdude10:01:12

it would be better to make it idempotent. so a second refresh just gets you the same token as the first refresh

borkdude10:01:41

you could make a local atom keeping the tokens and return the same token if it’s still valid

kah0ona10:01:47

ah yea so move the problem to my clojurescript client

borkdude10:01:30

I mean, cache the tokens server side. the refresh function goes through the cache

phil10:01:18

Yea, basically, catch the "invalid token" message at the clojurescript, do the dance to refresh it with the refresh token, then continue with whatever it was the client was actually requesting.

kah0ona10:01:02

ah yeah ok

phil10:01:22

Avoid trying to do clever things with locking though, I bet it's a total minefield.

kah0ona10:01:25

one idea i also have, come to think of it, is to just retry. catch the refresh token outdated exception server-side, re-look in the database (which has a new token in a very short while) and retry

kah0ona10:01:34

ok but i can work from here

kah0ona10:01:01

thanks, i hoped there was some construct that did this for me, so that I came to check here.

phil10:01:50

I'm assuming you know about https://github.com/funcool/buddy ?

kah0ona10:01:58

how would that help? i ’m integrating with an external api here that i don’t control

kah0ona10:01:49

but i just realize the simple solution is staring me in the face: - catch the ‘outdated token’ refresh action, and just proceed; there must ’ve been a previous request which done that

borkdude10:01:06

@kah0ona I have a similar construct with futures. I’m getting some data from a future and keep those futures in an atom. It doesn’t matter if the future is already realized or still running. If there is already a future, I just wait for that one. If there isn’t one, I create one.

borkdude10:01:39

if the future is not valid anymore, I remove or cancel it

kah0ona10:01:18

Decided to ‘keep it simple’. If the refresh failed due to outdated token (ie. race condition), wait 200ms, assume by now the new tokens are stored in the database by the previous request, re-fetch those (they should be valid anyway, since validity is 10minutes), and proceed the handler.

kah0ona10:01:13

the only thing is the thread/sleep… but at least it’s very clear what’s happening. Not sure if this gives lots of problem when i have a gazillion concurrent users doing things like this, but I’ll come back here when that happens 😉

kah0ona11:01:44

as an aside: again realizing the middleware pattern is awesome. the business logic ring handlers are now simplified so much.

schmee12:01:20

here’s a non-blocking way to do it: a function called is-about-to-expire and an atom is-updating, then in your request have something like this:

(if (and (is-about-to-expire token) (compare-and-set! is-updating false true))
  (try
    (swap! token-atom (get-new-token)))
    (finally
      (reset! is-updating false)))

schmee12:01:04

that means the first thread that sees that the token is about to expire will fetch a new one, but since the old one hasn’t expired yet everyone can keep making requests

schmee12:01:35

when the refresh has happened, the old token is atomically replaced with the new one

caleb.macdonaldblack12:01:15

What does a la carte mean? Im having trouble trying to find a good definition on google.

schmee12:01:02

it’s a restaurant term, it means that you can pick and choose what dishes you want instead of getting a pre-composed menu

👍 5
absolutejam13:01:52

How do deftest and spec differ?

hoynk15:01:09

Does anybody know if there are performance improvements or other good or bad side effects in moving from openjdk 8 to 11?

vlaaad16:01:43

Hi! I'm doing a library that provides optional functionality that depends on clojure.core.cache. But since this is optional functionality, I don't want to introduce dependency on core.cache, and thinking how can I make this dependency a "soft" one. Initially I tried using own protocol similar to core.cache and extend it to core.cache's one if it exists like that:

vlaaad16:01:52

(when (try
        (require 'clojure.core.cache)
        true
        (catch Exception _
          false))
  (extend-protocol Cache
    (:on-interface clojure.core.cache/CacheProtocol)
    (lookup [this k]
      (clojure.core.cache/lookup this k))
    (has? [this k]
      (clojure.core.cache/has? this k))
    (hit [this k]
      (clojure.core.cache/hit this k))
    (miss [this k v]
      (clojure.core.cache/miss this k v))
    (evict [this k]
      (clojure.core.cache/evict this k))))

vlaaad16:01:27

But I think this solution sucks, because I can't extend protocol to another protocol, only to interfaces

vlaaad16:01:28

I just realized I can check if supplied cache satisfies clojure.core.cache/CacheProtocol and wrap it with my cache

potetm16:01:01

You can optionally run some code, depending on whether you’re able to load clojure.core.cache

vlaaad16:01:35

Yeah, I decided to do it like that:

(def core-cache-protocol
  (try
    @(requiring-resolve 'clojure.core.cache/CacheProtocol)
    (catch Exception _
      nil)))

(defn ->cache [x]
  (if (and core-cache-protocol (satisfies? core-cache-protocol x))
    (let [core-cache-lookup @(resolve 'clojure.core.cache/lookup)
          core-cache-has? (resolve 'clojure.core.cache/has?)
          core-cache-hit (resolve 'clojure.core.cache/hit)
          core-cache-miss (resolve 'clojure.core.cache/miss)
          core-cache-evict (resolve 'clojure.core.cache/evict)]
      (reify Cache
        (lookup [_ k] (core-cache-lookup x k))
        (has? [_ k] (core-cache-has? x k))
        (hit [_ k] (core-cache-hit x k))
        (miss [_ k v] (core-cache-miss x k v))
        (evict [_ k] (core-cache-evict x k))))
    x))

madstap17:01:27

I want do serve files from the file system, like with python -m SimpleHTTPServer, but from inside a clojure process. I think saw a tweet the other day about a(n old, "done") library for this, but I can't seem to find it. Does anyone know which library I'm talking about?

kwladyka18:01:07

How to run sh in Clojure to run wkhtmltopdf with input stream and output stream if possible? I want to do in from in-memory data instead of files.

kwladyka18:01:34

So as inputs I will have long HTML / js / css code and as output PDF file.

kwladyka18:01:08

ok I think the only one way is to use - instead of file name for output. Then I will have output in :out.

kwladyka18:01:58

so I have to figure out how to make input from memory. Probably with :in but this will be long input string.

kwladyka18:01:41

hmm looks like it is pretty easy, string is fine

kwladyka18:01:59

(sh "wkhtmltopdf" "-B" "0" "-L" "0" "-R" "0" "-T" "0" "--page-height" "58mm" "--page-width" "43mm" "--disable-smart-shrinking" "-" "foo.pdf"
      :in (slurp "resources/index.html"))

kwladyka19:01:58

Does it have size limit?

potetm18:01:06

If you know what ring is and how to use it.

potetm18:01:55

If not, it’s good to learn. Ring is the standard clj http api.

madstap18:01:10

Thanks, I think it was https://github.com/tailrecursion/lein-simpleton that I saw. As it turns out it's not exactly what I wanted, but it looks easy enough to just use ring directly.

kwladyka19:01:26

https://github.com/cognitect-labs/test-runner - this library for tests is be default included to clj template for new project. Does it have option to watch and test again after each change? Or should I use something else?

kwladyka20:01:57

Did somebody read from bardcode reader devices and can share experience how to do it in Clojure / Java?