Fork me on GitHub
#clojure
<
2017-11-28
>
noisesmith00:11:24

@bravilogy you don’t have to use it, but it prevents problems caused by global mutable singletons for application resources

Bravi00:11:50

that’s what I don’t really understand. how is a server instance mutable?

noisesmith00:11:21

@bravilogy at one moment in time, it’s running and you can use it to listen for connections, at another moment in time it isn’t

noisesmith00:11:50

at one moment, it uses your handler to serve requests - at another moment it has a different handler, or different middleware

noisesmith00:11:54

it’s a resource that has state

noisesmith00:11:25

how do we avoid the situation where your server is still running and you want a different one?

Bravi00:11:42

different server?

noisesmith00:11:55

it owns a resource that comes from the OS, what if you replace the global reference before stopping it?

noisesmith00:11:28

component helps streamline these things, not just for your server, but for other things that own limited or unique resources, or manage a mutable internal state

noisesmith00:11:24

including database connections, or user state, or even graphic windows

noisesmith00:11:53

what if one mutable or resource owning thing needs to access another one? component helps

Bravi00:11:21

hmm so it can be used on front end as well? in cljs?

noisesmith00:11:33

I meant OS windows, but yes, I also use it in cljs

Bravi00:11:57

do you use it instead of re-frame?

Bravi00:11:14

doesn’t it have just start and stop methods?

noisesmith00:11:15

@bravilogy another important usage is with tests - if your code is made to use components you don’t have to go redefining things in other namespaces to run tests - all you need to do is pass in data that is usable in the right way

noisesmith00:11:04

@bravilogy I use it to control the websocket connection, and ensure that when things try to send to the server via websocket and the socket is closed, the messages get saved to send later, and that all the messages go through once the handshake is done

noisesmith00:11:39

@bravilogy it ensures that the r/atoms that we create to manage state don’t have to be globals, instead they can be passed in from the component that initializes them on startup and owns them

Bravi00:11:47

if you have any public repos that show some simple usages, it would be very helpful to see please

noisesmith00:11:05

similarly the router that acts on address bar changes - it gets passed routing rules via components, so that it doesn’t need to use globals and be tied to implementation details of the rest of hte app

noisesmith00:11:28

@bravilogy sadly this is a closed source app - it’s the product we sell

noisesmith00:11:17

but there are definitely examples of components out there, and they work on the same principles: if something is mutable or owns a limited resource, make sure it has a specific owner, isn’t global, and gets passed as an argument to things that need it

Bravi00:11:07

ok no problem 🙂 thanks for explanation. I think I’ll just need to see in action to actually understand what it does. I’m trying to understand it in other languages I know. perhaps it’s very Clojure related

noisesmith00:11:31

how do the other languages work with limited or mutable resources?

Bravi00:11:42

maybe I should build this app from scratch and perhaps I’ll stumble upon the issue where I’ll have that ‘aah I see’ moment and use it

noisesmith00:11:24

@bravilogy in my experience making something around a shared mutable global value, then trying to convert to use an explicit argument later, is a huge pain - it requires a lot of work

noisesmith00:11:51

if you start with explicit arguments, it’s very easy to make it implicit (or even act like it’s global) in specific scopes if you later decide you need that

noisesmith00:11:00

it’s not a two way thing - one way is easy, the other is hard

Bravi00:11:41

you mentioned the Websocket connection, so in simple terms do you mean that a mutable resource would be a global variable in js for example, that holds the connection instance?

noisesmith00:11:10

yes - and attached logic that ensures that if some other part of the app starts to send, and we are not connected, the message is queued for later

noisesmith00:11:23

and some data unpacking / packing, stuff like that

noisesmith00:11:38

but when you use component, it’s not global any more

noisesmith00:11:53

which means if you need to test it, or use things differently later, you can just replace an argument

noisesmith00:11:01

instead of rewriting code or faking other namespaces or such

Bravi00:11:56

the way I’ve seen it written in some examples, it was assigned to something.. like (def system (create-system)) where create-system returned a component/system-map or component/Lifecycle

noisesmith00:11:20

that’s convenient during development but isn’t recommended in an actual application

Bravi00:11:21

doesn’t that mean that system is global here ?

noisesmith00:11:27

your code should not rely on that global

noisesmith00:11:35

but it’s very useful to have that global in your repl

Bravi00:11:09

ah so in a production - ready application, I would have something like

(defn -main [& _]
  (let [config (config)]
    (-> config
        app-system
        component/start)))

noisesmith00:11:11

in fact, it’s recommended to only create that def in dev mode, and simply anonymously create the system in prod, without leaving any handle to it anywhere

Bravi00:11:15

(copied from chestnut)

Bravi00:11:31

so basically it becomes just an argument to an application

noisesmith00:11:59

component/start automates the start up order of the parts of the app, and makes sure they can see the other parts they need

Bravi00:11:23

damn 😄 I get it now

noisesmith00:11:32

that’s another advantage, instead of having to mentally juggle the order that things start up because of functional deps, it can just do a topo sort of the tree of deps

Bravi00:11:38

you should write a blog post on this!

noisesmith00:11:42

glad I could help!

noisesmith00:11:47

maybe I will some day, heh

Bravi00:11:55

thank you so much

Bravi00:11:17

I guess I was complicating things in my mind

noisesmith00:11:37

if you think about it, it’s providing the same advantages that fp does over imperative code, on an app architecture level

noisesmith00:11:59

immutable arguments instead of mutable resources (or at least pretend, as much as you can 😄)

seancorfield00:11:17

@bravilogy See if this simple example helps: https://github.com/seancorfield/clojure-webapp-live-demo/blob/master/src/webapp/core.clj -- it uses Component to start/stop a Jetty web server.

qqq00:11:39

I know about Integer. and Float. Is there a clojure function of type "string -> number or nil" which does if full string parses to a number, return number else, return nil

noisesmith00:11:36

I always use #(try (Long/parseLong %) (catch Exception _))

noisesmith00:11:45

or Double/parseDouble as needed

qqq00:11:37

I was under the impressions that exceptions are slow and should be avoided for simple tasks, but I hnever benchmaked it tobe sure

Alex Miller (Clojure team)01:11:27

Exceptions are not slow

noisesmith00:11:01

catching that exception and returning nil is much simpler than a reliable maybe-read-a-number

noisesmith00:11:22

and honestly since I only do that parsing at system boundaries, it is guaranteed to never be in a hot loop

noisesmith00:11:35

so I haven’t had to think about its performance

Bravi00:11:53

I’ve got this 6 page SPA that I decided to re-build using clj + cljs. At the moment I’m using Node.js + Elm on that one and I’m so motivated to do it 😄 I’ve been playing around with clojure for a while now but haven’t built anything significant yet

noisesmith00:11:12

if you think that your communication with the outside world is in a hot loop you need to optimize - statistically speaking it’s unlikely you shoudl be using clojure

qqq03:11:12

is there a builtin for 'apply this function to last element of this vector' or is it a matter of peek, pop, and conj

qqq03:11:59

is count on a vector guarnttted to be constant time ? https://clojuredocs.org/clojure.core/count says nothing

qqq03:11:32

If I'm trying to compute f^n(x), is the best way simply (first (drop n (iterate f x))) ?

andy.fingerhut03:11:10

@qqq Here is the implementation of the method count() for objects of type PersistentVector in the Java source code for Clojure, which is the type that Clojure vectors are: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/PersistentVector.java#L205-L207

andy.fingerhut03:11:00

That is the method that clojure.core/count calls when you call count on a Clojure vector, because PersistentVector implements the Counted Java interface (through several intermediate subclass/implements relationships that I won't bore you with unless you want to know).

Alex Miller (Clojure team)03:11:36

you can see this in Clojure by using counted? which indicates a coll can be counted in constant time

Alex Miller (Clojure team)03:11:16

the fastest way to do this is going to be peep/pop, then transform, then push

andy.fingerhut03:11:25

So for probably all versions of Clojure up to 1.9, you are guaranteed it is constant time. Future versions? Only Rich Hickey knows 🙂

Alex Miller (Clojure team)03:11:45

but really if you are asking this question, you are probably using the wrong data structure in the first place

Alex Miller (Clojure team)03:11:15

if you’re doing something stack-like, use a list (and pop/peek/conj)

qqq03:11:22

so I'm building a parser, where the tokens are stored in a vector

qqq03:11:39

most of the time, it's just conj to end of vector, but occasionally I have to modify the last conjed token

Alex Miller (Clojure team)03:11:02

well, I’d recommend: (defn transform-last [v f] (conj (pop v) (f (peek v))))

Alex Miller (Clojure team)03:11:50

peek is definitely the fastest way to get the last element of a vector

qqq03:11:24

got it; thanks

kauko07:11:47

I'm trying to download a dependency, jai_core, from here https://repository.jboss.org/nexus/content/repositories/thirdparty-releases/javax/media/jai-core/1.1.3/. I added the repository to my project.clj like this :repositories [[["jboss-third-party" ""]]]. I still get an error message like this though Could not find artifact javax.media:jai_core:jar:1.1.3 in jboss-third-party ()

kauko07:11:53

What am I doing wrong?

danielcompton07:11:01

You need to request jai-core not jai_core

kauko07:11:53

I can't find any uses of jai_core though, I'm thinking this is a transitive dependency

kauko07:11:29

Thanks for the tip

qqq10:11:53

what's the cljs equiv of clojure.edn ?

qqq10:11:04

do I need a separate library, or is this part of clojure?

bronsa10:11:19

it's transitively part of cljs

bronsa10:11:25

cljs.tools.reader.edn

bronsa10:11:32

or clojure.tools.reader.edn, as you prefer

qqq10:11:54

#?(:cljs [cljs.tools.reader.edn :as edn]
               :clj [clojure.edn :as edn])

is there a better way to write this, or do I need the conditional ? (this is for a cljc file)

bronsa10:11:22

you can use clojure.tools.reader.edn in both clj and cljs

bronsa10:11:35

otherwise what you have is ok

pesterhazy10:11:04

yay for tools.reader!

pesterhazy12:11:03

I couldn't figure out how to start clojure 1.9. from the command line (sans boot tools) using java -cp because of the additional dependency (now that java -jar clojure.jar is not sufficient anymore). Specifically - which version of spec is needed for clojure-1.9.0-rc2? - is java -cp clojure.jar:spec.jar clojure.main the recommended way to start this? - is this documented somewhere? This would be a good candidate for the release notes as it might catch users switching to 1.9 unaware

Alex Miller (Clojure team)13:11:30

This will be documented on the Getting Started page on release but there are several answers. 1) use a build tool like lein, boot, maven, etc. If you include Clojure 1.9 as a dependency, it will also pull in its dependencies automatically (spec.alpha and core.specs.alpha). 2) use the new Clojure cli scripts doc’ed at https://clojure.org/guides/deps_and_cli. If you’re on a Mac this is as simple as brew install clojure then clj. 3) build a stand-alone jar from the Clojure git repo - git clone the repo and then follow the directions in the readme. 4) Download the jars for Clojure, spec.alpha, and core.specs.alpha and manually create the classpath. This is probably the most cumbersome

pesterhazy14:11:36

thanks for the reply! building the classpath manually is fine by me - looks like you can see the correct spec.alpha and core.spec.alpha versions here: https://mvnrepository.com/artifact/org.clojure/clojure/1.9.0-RC1

Alex Miller (Clojure team)14:11:01

Yep, but note that the reason for breaking these out is to allow them to change more quickly than Clojure so there won’t be one single version that is applicable necessarily

pesterhazy14:11:56

right, so the version in clojure's pom is essentially the minimal version of those extra deps

jereme12:11:00

hello, channel

henrik12:11:21

> If you’ve spent years learning tricks to make your [multi-threaded] code work at all, let alone rapidly, with locks and semaphores and critical sections, you will be disgusted when you realize it was all for nothing. If there’s one lesson we’ve learned from 30+ years of concurrent programming, it is: just don’t share state. It’s like two drunkards trying to share a beer. It doesn’t matter if they’re good buddies. Sooner or later, they’re going to get into a fight. And the more drunkards you add to the table, the more they fight each other over the beer. The tragic majority of MT applications look like drunken bar fights. ~ http://zguide.zeromq.org/page:all

plins12:11:30

hello everyone, is there a function equivalent of haskells partition :: (a -> Bool) -> [a] -> ([a], [a]) in clojure?

souenzzo13:11:49

+1 to cloogle with spec

rauh13:11:09

That's very different I think.

rauh13:11:38

Many programming languages (also e.g. Kotlin) output only 2 vector for partition

henrik13:11:20

Alright, my bad

rauh12:11:44

@plins Not really, but group-by is close

plins13:11:28

@henrik, im not sure if ive understood the docs right

henrik13:11:59

My suggestion is incorrect for what you have in mind, I misunderstood the purpose of partition in Haskell.

henrik13:11:47

Give the group-by suggestion by @U051SA920 a whirl, it’s more likely what you’re looking for.

Empperi13:11:21

`(defn haskell-partition [f coll] (map (group-by f coll) [true false]))`

Empperi13:11:29

(haskell-partition even? (range 10)) => ([0 2 4 6 8] [1 3 5 7 9])

Empperi13:11:45

(threads do not support inline code blocks, which sucks, try to bear with me)

Empperi13:11:15

and I’m not 100% certain if that is actually what that haskell function does but I think it gets close enough

bronsa13:11:33

@pesterhazy two options: 1- use the new clj script 2- the just released 1.9.0-RC2 has a standalone jar, it's documented in the readme

plins13:11:25

(partition-by even? [1 2 3 4 5 6 7 8])
;=> ((1) (2) (3) (4) (5) (6) (7) (8))

plins13:11:53

id like 2 lists as a result

plins13:11:30

found the solution, ended up with (vals (group-by even? [1 2 3 4 5 6 7 8]))

bronsa13:11:05

@plins note that using vals is probably incorrect

bronsa13:11:14

group by returns an unordered map

bronsa13:11:26

no guaranteed that you'll get [truthy falsy] vs [falsy truthy]

Empperi13:11:32

let me put my suggestion here, it’s now hidden inside a thread:

(defn haskell-partition [f coll]
  (map (group-by f coll) [true false]))

(haskell-partition even? (range 10))
=> ([0 2 4 6 8] [1 3 5 7 9])

Empperi13:11:29

this way order is guaranteed

plins13:11:38

thank you!

Empperi13:11:12

it is not always obvious that you can use maps as functions 🙂

pesterhazy14:11:23

@bronsa, do you know if the standalone jar is published somewhere?

bronsa14:11:43

it's just a local build

bronsa14:11:49

as the build profile names suggests

Alex Miller (Clojure team)14:11:22

Ah yeah, I need to update that page. This just changed as of yesterday.

len14:11:53

We have a weird issue where some go routines stop working after about 5hrs - any ideas what we can look at ?

pesterhazy14:11:30

when I try to build the classpath manually I get Could not locate clojure/spec/alpha__init.class or clojure/spec/alpha.clj on classpath

bronsa14:11:31

look for blocking IO inside go blocks

len14:11:55

will that hold up all the go routines though ?

mpenet14:11:55

blocking io is almost always the culprit

bronsa14:11:15

@len go routines execute in a shared threadpool

bronsa14:11:31

if you block all the threads available in that threadpool, nothing will gets executed anymore

mpenet14:11:45

it's just 8 threads even so it's quite easy to break if you're not careful

bronsa14:11:14

@pesterhazy how are you bulding that cp?

len14:11:35

thanks folks - off to debug

pesterhazy14:11:39

it works now - here's a simple script to download clojure manually: https://gist.github.com/pesterhazy/afdfa47ac04d94ee16b5f2e8ddbb0bb1

bronsa14:11:40

don't do that

bronsa14:11:15

use the new clj script instead

bronsa14:11:33

why? it basically does that for you

pesterhazy14:11:37

I understand, but this is simple and I know exactly what it does - it's the moral equivalent of the standalone jar in clojure 1.8

pesterhazy14:11:08

clojure is just a jar, remember?

pesterhazy14:11:13

(or three jars)

bronsa14:11:24

feels like NIH tbh

bronsa14:11:42

I don't see any point in reinventing something that has now an official impl

bronsa14:11:02

but you're free to do as you think best :)

bronsa14:11:38

all clj does is download your artifacts and build a cacheable classpath for you, nothing more than that or magical

pesterhazy14:11:57

clj is wonderful, but that doesn't mean that it's not useful to build a shell script to understand how it works

bronsa14:11:28

I'm not saying it's not useful to build a simplified version of X to understand how it works, I'm saying if you need to use it, it's probably better to use X instead. in this case, your script will break when clojure adds a new dependency. clj would handle that automatically

pesterhazy14:11:21

it won't break so long as I don't update the clojure version 🙂

decoursin15:11:34

Hi, is ex-info json serializable?

pesterhazy15:11:18

@decoursin ex-info can contain any java objects, so it depends on the contents

decoursin15:11:28

This doesn't seem to work:

(try (throw (ex-info "testss" {:a 3}))
                      (catch Exception e
                        (clojure.data.json/write-str (Throwable->map e))))
=> Exception Don't know how to write JSON of class java.lang.Class  clojure.data.json/write-generic (json.clj:385)

decoursin15:11:33

What about that case though ^

pesterhazy15:11:51

get it using ex-data?

decoursin15:11:07

Yeah, if I did that, I would lose the Message.

decoursin15:11:22

which in this case above is "testss".

arttuka15:11:53

it says right in the error message what the problem is

arttuka15:11:15

you’re trying to serialize an object of the class java.lang.Class, and your json writer doesn’t know how to do that

decoursin15:11:08

Ah thank you, it's a macro so I must evaluate it?

(try (throw (ex-info "testss" {:a 3}))
                      (catch Exception e
                        `(json/write-str (Throwable->map ~e))))

arttuka15:11:12

no, being a macro doesn’t have anything to do with it. (Throwable->map e) produces something that has a java.lang.Class object inside it.

decoursin15:11:10

Ok but then how can you json serialize a generic ex-info?

ghadi15:11:58

Serialization libraries generally have a way to teach them how to serialize opaque objects. cheshire allows you to extend a protocol to achieve that, not sure what c.data.json gives you as an extension point

decoursin15:11:05

Thank you, I see now that even a generic exception fails with the same issue:

(try (throw (Exception. "hi"))
                      (catch Exception e
                        (json/write-str (Throwable->map e))))
=> Exception Don't know how to write JSON of class java.lang.Class  clojure.data.json/write-generic (json.clj:385)

ghadi15:11:45

looks like they allow a protocol too

ghadi15:11:42

(extend-protocol JSONWriter
  java.lang.Class
  (-write [c out]
    (let [representation (do something to the class c)]
      (.print ^PrintWriter out representation))))

ghadi15:11:18

you'll need to import JSONWriter from c.d.json and PrintWriter from http://java.io

ghadi15:11:44

> clojure.core/Throwable->map formerly returned StackTraceElements which were later handled by the printer. Now the StackTraceElements are converted to data such that the return value is pure Clojure data, as intended.

ghadi15:11:52

^ Tidbit from the Clojure 1.9.0 release notes

decoursin15:11:47

Sounds good, thank you.

borkdude20:11:47

Is there something like promise where delivery can happen multiple times and deref just returns the last delivered value?

borkdude20:11:04

maybe core.async

borkdude20:11:30

but I want everybody to be able to deref, without emptying

hiredman20:11:30

atom and reset!

hiredman20:11:47

or any mutable value

hiredman20:11:54

mutable reference

hiredman20:11:02

that is just last write wins

hiredman20:11:32

the volatile reference might work great for that too

borkdude20:11:19

but atom reads don’t block and I need that too

bfabry20:11:22

I think the implications behind "delivery can happen multiple times" rule out volatile. sounds like an atom though

plins20:11:36

hello everyone, id like to to spawn 4 threads in paralallel to do some IO, i dont need to get data back from those threads, whats the best way to achieve this besides

(thread ....)
(thread ..)
...
(thread ...)

hiredman20:11:37

"delivery" just means a write

borkdude20:11:52

I need the deref to block

borkdude20:11:58

like with promise

borkdude20:11:37

I could use a promise with an atom, that would work

borkdude20:11:44

and double deref

bfabry20:11:46

I meant that "delivery can happen multiple times" says to me that it's multithreaded, meaning volatile is a bad choice 🙂

hiredman20:11:52

if you are already using core.async (which I guess you are from the use of thread) I would look at pipeline-blocking

plins20:11:56

@hiredman, my mistake, i was talking about clojure.core/future which spawns a thread .. do you think its worth to turn my vector to a core.async queue and use pipe-line blocking

hiredman20:11:10

it depends, I might start looking at using Executors directly too instead of using clojure.core/future

hiredman20:11:09

like, do you want to have 4 threads that a re-used for doing io, or do you want to spin up 4 threads each time, does the io return a result, and do you need those results in order or not

plins20:11:05

i trully dont care about the result, and i wont reuse anything, those threads perform their work and die silently

gonewest81821:11:55

might be worth looking at Claypoole even if your current requirements are minimal right now…. https://github.com/TheClimateCorporation/claypoole

borkdude20:11:04

This works I think.

(let [p (promise)]
  
  (defn last-val [] @@p)
  
  (defn reset-val! [v]
    (or (deliver p (atom v))
        (reset! @@p v))))

(future (println (last-val)))

(reset-val! 10)
(reset-val! 11)

cap10morgan20:11:33

I feel like I'm missing something simple here, but has anyone ever implemented a file-tailing algorithm in Clojure? That is, as new lines are written to a file, you process them in your Clojure code? It seems that just opening a reader gets me a snapshot of the file at opening time.

cap10morgan20:11:03

ah, so basically I need interop. I guess I should go ask the planck folks then since I'm in a planck / cljs context. 🙂 Thanks for the pointer!

ghadi20:11:25

no problem. cljs should be similar, but obviously asyncio

ghadi20:11:01

there are promise-chans in core.async that kinda resemble this

ghadi20:11:15

you can fulfill them once and they provide their value immediately forever

borkdude20:11:47

@ghadi I need to ‘fill’ them multiple times

ghadi20:11:33

I'm with hiredman, sounds like last-write-wins with an atom

borkdude20:11:21

@ghadi I need to block on the first deref when it’s still not initialized.

ghadi20:11:33

promise an atom?

borkdude20:11:08

@ghadi See my above code. It’s the other way around. Promise in atom doesn’t work, because you can only delivery to a promise once.

ghadi20:11:34

I guess that's your example above. Seems like the xy problem -- I need to know why

borkdude20:11:16

Yeah, I might not need it, I’ll think about it some more. thanks.

noisesmith21:11:28

@borkdude what about checking if the atom is nil, and if so putting a watch on the atom that triggers your thing if the new value is non-nil, and then removing the watch inside the watch callback?

noisesmith21:11:52

or maybe you even want to always want to ignore the current value, and only run your action when the value changes, and add-watch is what you want directly

borkdude21:11:46

> what about checking if the atom is nil this is hard to make thread safe without compare-and-set! I don’t need a watch.

borkdude21:11:46

the xy about this: I need to prepare some data in the background and I want the first web request to wait for this thing to be ready. It might be refreshed later.

noisesmith21:11:23

with a watch, you can ensure your code doesn’t return until the value is ready

noisesmith21:11:39

or, use an agent instead of an atom and use await

noisesmith21:11:13

(well, watch plus delay or promise you can block on that is)

noisesmith21:11:45

and really, if all you need is to prepare data in the background, why not just use future ?

borkdude21:11:24

> just use ‘future’ a future cannot be updated

noisesmith21:11:44

why does it need to be updated? why can’t you just make a new value?

noisesmith21:11:26

and if you have async updates multiple threads wait on, that would happen repeatedly, await on an agent sounds like it maps directly to that

borkdude21:11:52

(def a (agent)) (await a) ;;=> nil

noisesmith21:11:01

right, there are no pending actions on it

Empperi21:11:34

atom and swap! with clojure.lang.PersistentQueue

Empperi21:11:46

oh crap, I was on history

Empperi21:11:49

forget that

borkdude21:11:07

Maybe we should switch to a thread as to not flood this channel with this topic, it’s getting kind of long

noisesmith21:11:43

what I don’t understand is how you would know when a is “ready” and when it isn’t

borkdude21:11:57

@U051SS2EU Everything after the first init is fine

borkdude21:11:59

This works: https://clojurians.slack.com/archives/C03S1KBA2/p1511901724000313 Just wondered if there is some other primitive I could use.

borkdude21:11:43

But I’m not even sure anymore if I want to do it like this, so it’s ok, I’ll think about it some more

tbaldridge21:11:55

I think you're on the right track

tbaldridge21:11:28

maybe something with a count-down-latch? (I may be thinking of the wrong primitive)

noisesmith21:11:59

I think you have an extra @ on that reset! - but otherwise that is sensible

tbaldridge21:11:32

Or if you want to drop to Java you could due this with Thread.wait, notify and clojure's locking

tbaldridge21:11:14

1) get the value (non locking) 2) if the value is not a sentinel return it 3) Lock and check if the value is still a sentinel 4) If so, add yourself to a pending threads queue and sleep

tbaldridge21:11:43

the producer then simply sets the value to != a sentinel and wakes all the pending threads. Future writers can ignore the pending threads list

borkdude21:11:01

thanks! some stuff to consider

kwladyka21:11:59

(.setLevel (Logger/getLogger "org.flywaydb") Level/WARNING) this works (silent DEBUG/INFO logs) unless add to project.clj [org.slf4j/slf4j-simple "1.7.25"]. How can i make silent this logs?

borkdude22:11:15

Is there a reason why zipmap doesn’t a transient under the hood?

tbaldridge22:11:56

probably not, there's a few places in Clojure that could still use some transient love

bfabry22:11:11

yeah I think a patch with benchmarks for that would probably be accepted

borkdude22:11:45

I ran a quick benchmark and for 1000 keys there’s only a drop from 250 to 213 ns

borkdude22:11:10

Probably because creating a new map from an existing map using assoc is very efficient already

bfabry22:11:47

yeah I got a measurable difference but not a particularly important one

borkdude22:11:12

I tested this because I wanted to know the performance difference between using map literals and zipmap:

(def f (fn [[a b num]]
    #_=>         {:a a
    #_=>          :b b
    #_=>          :num num}))
(def g (partial zipmap [:a :b :num]))
(quick-bench (f ["a" "b" 1])) ;;=> 22 ns
(quick-bench (g ["a" "b" 1])) ;;=> 250 ns

ghadi22:11:47

there's a ticket for this already

ghadi22:11:56

There was a ticket for making merge faster CLJ-1458 but it could use some love, starting with a decent set of benchmarks

ghadi22:11:33

I would like someone to give that a whack. Keeping in mind that merge 1) preserves metadata 2) and because it is based on conj, happens to take either a mapentry, vector, or maps as arguments

ghadi22:11:41

(merge {} [:a :b] {:c :d}) => {:a :b :c :d}

ghadi22:11:21

need to go back to basics with that ticket and just start with a benchmark harness.

ghadi22:11:49

Oh why am I talking with prose: @alexmiller do you have a spec lying around for c.c/merge?

gdeer8122:11:27

wow, I'm pulling my hair out over this issue, when I create a reader at the repl this form parses correctly, but when I call a function on the form it throws an error. I'm about to hit the road so hopefully the comments on the ticket are clear https://github.com/gdeer81/marginalia/issues/165

gdeer8122:11:29

it's such an edge case but I can't let it go

noisesmith23:11:14

;; A symbol string begins with a non-numeric character and can contain
;; alphanumeric characters and *, +, !, -, _, and ?.  (see
;;  for details).

noisesmith23:11:39

I would expect : inside a symbol to work by accident, and any bugs caused by having : in a symbol to be the fault of the one naming the symbol

noisesmith23:11:29

Symbols beginning or ending with ':' are reserved by Clojure. A symbol can contain one or more non-repeating ':'s.
ugh - never mind then - that seems like a terrible choice to me, but it’s allowed

hiredman23:11:50

well, time and again, in these kind of discussions a distinction has been made between legal symbols and readable symbols

noisesmith23:11:58

right, but those docs seem to say that a single : in a symbol is readable

noisesmith23:11:07

(and also legal)