Fork me on GitHub
#clojure
<
2017-06-14
>
Matthew Davidson (kingmob)00:06:23

Ahh, ok. I guess I can recall normally without TCO… As a trie, it’s not too deep. Will have to check.

hiredman00:06:07

you can just create your own recur frame using loop

cmal04:06:04

Hi, I am learning the reader conditional, and I tried this code in repl. I found it not returned as I expected, as showed in the 3rd and 5th expression, I thought these will return “CLR”:

user> (read-string {:read-cond :allow :features #{:cljs}} "#?(:cljs \"ClojureScript\" :clj \"Clojure\")")
"ClojureScript"
user> (read-string {:read-cond :allow :features #{:cljs}} "#?(:cljs \"ClojureScript\" :clj \"Clojure\" :clr \"CRL\")")
"ClojureScript"
user> (read-string {:read-cond :allow :features #{:clr}} "#?(:cljs \"ClojureScript\" :clj \"Clojure\" :clr \"CRL\")")
"Clojure"
user> (read-string {:read-cond :allow :features #{:clj}} "#?(:cljs \"ClojureScript\" :clj \"Clojure\" :clr \"CRL\")")
"Clojure"
user> (read-string {:read-cond :allow :features #{:clr}} "#?(:cljs \"ClojureScript\" :clj \"Clojure\" :clr \"CRL\")")
"Clojure"
user> (read-string {:read-cond :allow :features #{:clr}} "#?(:cljs \"ClojureScript\" :clr \"CRL\")")
"CRL"
user> (read-string {:read-cond :allow :features #{:clr}} "#?(:cljs \"ClojureScript\" :clr \"CRL\" :clj \"Clojure\")")
"CRL"
user> (read-string {:read-cond :allow :features #{:clj}} "#?(:cljs \"ClojureScript\" :clr \"CRL\" :clj \"Clojure\")")
"Clojure"
user> (read-string {:read-cond :allow :features #{:cljs}} "#?(:cljs \"ClojureScript\" :clr \"CRL\" :clj \"Clojure\")")
"ClojureScript"
Why is this possible? Thanks.

cmal04:06:56

I found the :clj form must comes the last to get the read-string to work well.

cmal04:06:38

the doc https://clojure.org/reference/reader said “However, note that the Clojure reader will always inject the platform feature :clj as well.” Does that because of this?

seancorfield04:06:18

That sounds like the most likely explanation.

Matthew Davidson (kingmob)04:06:19

Is there a good online resource for the subtleties of deftype? I’m trying to use a Java array as an unsycnhronized-mutable field, and getting some weird behavior when trying to use aset (This is non-idiomatic, performance-sensitive code)

Matthew Davidson (kingmob)04:06:52

I get an ArrayStoreException when using aset inside the the type, but similar code produces no errors at the REPL

noisesmith16:06:07

it sounds like you need to respect the type of your array

Matthew Davidson (kingmob)18:06:48

Thanks, @noisesmith, yes, I saw that. I finally figured out the issue an hour later. The reason the code worked manually, but not in my fns must have been references to outdated definitions. This isn’t the first time I’ve noticed some weird evaluation errors when using records/types, forward declarations and external factory fns. Once I evaled everything in the correct order, the problem disappeared.

Matthew Davidson (kingmob)04:06:24

Is there some weird interaction between set! and aset going on?

gowder04:06:40

Today in extreme overuse of Clojure: I wanted to pretty-print json in color from the command line. 7-line Python script with pygments only worked from local directory but not from $PATH for totally mysterious reasons. So I rewrote it in 11 lines of Clojure. Amusingly, it turns out that Clygments pulls in, among other things, all of Jython. So my 11 line trivial thing is a 40mb(!!!) binary. But, by god, it WORKS FROM THE PATH. https://github.com/paultopia/cloppjson

kenbier05:06:56

I am querying an integer array in postgres database using clojure.java.jdbc. The result is coming back as an org.postgresql.jdbc4.Jdbc4Array. Is there any simple way to get this to coerce back into a clojure vector?

seancorfield05:06:03

kenbier: calling .getArray on it should get you a Java array and then into [] should let you get Clojure vector?

kenbier05:06:20

@seancorfield hmm ok, thanks. I suppose I’ll have to do this for each query that returns an array? Leave the connection open and call .getArray.

kenbier05:06:00

It’d be nice if I could find a way to do this automatically.

kenbier05:06:58

perfect this is just what I was looking for. thanks!

kenbier05:06:17

sweet that works!! partywombat thanks @seancorfield

j-po05:06:31

@gowder: That's amazing. I love it. I just spend like 30 seconds going, "Oh yes. Oh no. Oh yes. Oh no..."

cmal05:06:51

@seancorfield Hi, I still do not understand why the :clj will intercept :cljs and :clr form. What does the inject the platform feature :clj mean in “However, note that the Clojure reader will always inject the platform feature :clj as well.” ? Thank you.

seancorfield05:06:43

cmal: It's as-if the set of features always has :clj in it (as well as whatever you specify) -- so the reader goes through the list of conditional forms and picks the first one that is in the set.

seancorfield05:06:56

So if :clj is first, it will always match.

cmal05:06:48

Ahh, clearly enough, thank you for your kind!

qqq08:06:45

is there any project which provides erlang like processes on top of clojure via core.async (for the mailboxes)

mpenet08:06:38

pulsar I think, not sure how reliable this is tho. It's not easy to replicate these on the jvm

flowthing09:06:36

@gowder: FWIW, jq (https://stedolan.github.io/jq) also works for pretty-printing JSON with colors.

razum2um10:06:20

is there a cleaner way make a postwalk with state (without atom): https://gist.github.com/razum2um/b691ad0d760795efaeb886e6caf05122 ?

noisesmith16:06:03

razum2um: I would prefer to reduce over tree-seq, rather than post-walk with a side effecting function - you can get the same result without needing to mutate anything

noisesmith16:06:20

(reduce (fn [state el] (if-let [datum (:data el)] (conj state datum) state)) [] (tree-seq coll? seq x))

cmal10:06:17

Hi, why does transducer here returns a vector rather than a list?

user> (def xf (map dec))
#'user/xf
user> (transduce xf conj (range 10))
[-1 0 1 2 3 4 5 6 7 8]
I thought it would return (-1 0 1 2 3 4 5 6 7 8) instead.

bronsa10:06:02

the zero of conj is []

bronsa10:06:35

because you don't provide an init, transduce is going to use that

cmal10:06:57

Ahh… Thanks!

bronsa10:06:00

if you provided () you'd get (8 7 6 5 4 3 2 1 0 -1) anyway not [], since seqs conj to the left

cmal10:06:15

:thumbsup: 😄

curlyfry12:06:39

@cmal If you want a lazy seq as a result of applying your transducer, check out https://clojuredocs.org/clojure.core/sequence

gowder13:06:30

@flowthing did not know that, thanks!

lxsameer13:06:33

i didn't get exactly what he meant about the solution instead of semantic versioning for functions, NSs and artifacts

lxsameer13:06:57

can someone please help me understand this 🙂

joshjones13:06:34

One takeaway from this is that the interface to your public functions which are used by others should not change as you release new code. Much in the same way data is by definition immutable, and the lion who jumps is not the same lion who lands (paraphrasing here), the interface to your functions, once exposed, should not change. This also means they should not be removed if at all possible (or arities removed, or type of return change, etc). Better to call a function foo2 than to change foo and break lots of other code which depends on it. @lxsameer

captainlexington13:06:23

@lxsameer He's saying that sem-ver doesn't actually /solve/ the problem of breaking changes, it just brushes it under the rug. A better way to "solve" the problem of breaking changes is not to make them.

lxsameer13:06:40

good , so basically for changing a namespace i have to create new one ( ofcourse it was not possible to change it and keep the old interface at the same time) , right ?

lxsameer13:06:50

what about artifacts ? should i create a new one ?

captainlexington13:06:20

'artifacts' is a weird way to talk about it, I think. His example of python was good

captainlexington13:06:40

If your library has changed so much it won't work with previous versions, it needs a new name

captainlexington13:06:20

Because, let's face it, Python2 and Python3 are different languages, and both will have to be maintained separately until the end of time

lxsameer13:06:36

yeah that's right

leonoel13:06:35

changing a namespace is ok if the contract is respected adding new functions is ok correct a bug is ok improve perf is ok make a function accept less is ok make a function return more is ok etc etc...

joshjones13:06:44

"make a function return more" <-- can you elaborate?

captainlexington13:06:01

e.g. it could return a map with extra keys

joshjones13:06:38

yes -- though this gets into hairy territory, and gets into what expectations the caller has, even if they aren't explicitly guaranteed by the callee. If a function has been known to always return {:a 1 :b 2} and the function now adds a new k-v to this, then equality checks which expect only :a and :b (even if incorrectly, as they should select the keys they need) will fail ... it's not an unlikely scenario

leonoel13:06:09

fair enough

rinaldi15:06:54

This is a Leiningen specific question, but what's the difference between :dependencies and :plugins on the profile configuration file?

ghadi16:06:18

@rinaldi plugins are about modifying / enhancing leiningen itself. dependencies are your code's needed libraries

rinaldi17:06:16

@ghadi Oh gotcha, that makes sense. Thank you.

qqq17:06:53

Where can I find a good writeup on the differences of cljs/core.async and clj/core.async . It seems like there should be some fundamental differences since browser js is single threaded; but I don't know enough about it to dcerive all tye implications.

bfabry17:06:06

@qqq I think the main difference is that thread >!! <!! alt!! et al have no meaning, so you use go >! <! alt! instead always. I found David Nolen's blog posts on core.async the best intro to it, and they're all written in cljs, so that might be a place to start

hiredman17:06:00

they share no code, if you can believe it

bfabry17:06:19

that makes sense when I think about it but that's rather impressive

qqq17:06:20

@hiredman: they are two separate impls that happen to have a similar API ?

qqq17:06:51

when usint (chan 10), this is a chan with at most 10 elements. is there a way to tell put! or >! on this channel to return an error instead of blocking ?

qqq18:06:08

this is called "back pressure" right ?

noisesmith18:06:11

David Nolen has mentioned in the past that a certain layout was kept in cljs code to keep the diffs from clj simple

noisesmith18:06:34

so there may not be explicitly shared code, but they at least look at the diffs in respect to the two repos

bfabry18:06:56

yeah that's back pressure. you can use offer! for what you want

noisesmith18:06:56

unless I totally misunderstood him

qqq18:06:43

@bfabry : nice, thanks

bfabry18:06:25

and poll! is the receive equivalent

bfabry18:06:27

alternatively use alt! with an optional timeout channel

qqq18:06:42

right, I'm familiar with the alt trick, but was not aware of poll

joshjones18:06:49

@qqq well, put! will never block/park, but you can only have 1024 pending puts

qqq18:06:07

though I think in general, I'm okay with the 'taker' blocking, but the 'putter' sometimes need to tell stuff to backfoff

joshjones18:06:26

>! will park on a full buffer, >!! will block, and put! will always succeed, unless the pending puts limit of 1024 is reached, which means that you are doing too many async puts @qqq

qqq18:06:07

park = suspends go thread, blolck = suspends os thread right?

bfabry18:06:47

yes, in the context of cljs >!! doesn't make sense

noisesmith18:06:57

park means “let someone else use the thread your go is currently using”

bfabry18:06:01

because, one thread, if we block it, how could the channel ever clear

qqq18:06:04

right since it would block the mani js thread, and the system deadlocks

joshjones18:06:03

@qqq pretty much, yes -- park means "save the state of this thead, and let another piece of code use it"

noisesmith18:06:32

go blocks describe a series of continuations delimited by parking operations, and it’s helpful to avoid calling them “threads” since threads are real things that we use

mobileink18:06:36

noisesmith: are they green threads? fibers? thread-as-codepath v thread-as-execution context? maybe "continuation" is the best term, even if clojure doesn't have first-class continuations. i can never decide on the most appropriate terminology.

joshjones18:06:12

they depend on the JVM implementation, which on all but the most obscure systems are native OS threads

noisesmith18:06:48

yeah, I’d avoid using “thread” to describe a code path in a language that uses actual threads and calls them such

noisesmith18:06:24

there’s probably a less clumsy language to describe what’s in a go block, perhaps “state machine” or even just “program”

noisesmith18:06:51

as in “a go block describes a program with steps executed in specified order, but is passed between threads at parking operation boundaries, and is erroneous if it fails to park or halt”

mobileink18:06:41

noisesmith: problem is it's so natural to think of the codepath as a thread. but the execution thread is a different animal. i have yet to find an explanation with clear simple language for this.

noisesmith18:06:37

it’s a pragmatic choice to not use thread in this context; we have an object that is actually called a thread formally in the system, so calling other things threads is counterproductive

mobileink18:06:45

i think knuth might just use "routine" in TAOCP.

noisesmith18:06:08

just like calling methods “functions” - it made sense to allow idiomatically allow calling methods functions in java before java 8

noisesmith18:06:55

but in clojure where we have an actual function type, and java after java 7 where there is a Function type, using function/method interchangeably is just spreading confusion

mobileink18:06:56

yep. i'm trying to be more disciplined about that, esp. in javascript objects.

mobileink18:06:13

block a thread, park a routine?

noisesmith18:06:01

I like that, especially since golang already calls them “goroutines”

joshjones18:06:07

and in line with what noisesmith just said, note that unlike preemptable native threads, code executing in a go block must relinquish control (to other, parked code) in order to pause execution, so the semantics of threads and go blocks are not the same, even though threads are the vehicle used to execute the code in a go block

qqq18:06:36

right; go blocks ar e'inverted' at interaction with channels

qqq18:06:52

so a (while true) can inf loop a goblock, which would infloop the thread it's running on

hiredman18:06:15

the go macro in particular is very different between cljs and clojure, the clojure one uses tools.analyzer

hiredman18:06:35

the cljs one uses what is effectively a custom analyzer

hiredman18:06:12

the clojure go macro tries to avoid splitting code that it doesn't have to, but the cljs just splits everything (if I recall)

qqq18:06:56

I heard it is one of the most complicated clojure macros of all time

qqq18:06:05

what motivated you to wake up one morning and decide "today, I will read the go macro" ?

hiredman18:06:31

the clojure one I read because I wanted to see how they decided to do it, the cljs one because I wanted to fix bugs in it

hiredman18:06:23

the fact that it is a macro isn't all that interesting, it is really a compiler from source language to target language

hiredman18:06:41

the source language and target language happen to be the same language

hiredman18:06:53

it is neat. just core.async effectively contains two different compilers. compilers tend to be complicated and have odd bugs and edge cases, and having two distinct ones mean bug fixes in one may or may not translate over

hiredman18:06:52

cljs core.async has other interesting issues, because macroexpansion in cljs can return what is effectively a blob of javascript, which the core.async analyzer cannot analyze

kurt-o-sys18:06:21

@lxsameer Yeah... I quite like the approach given by Carlo Pescio: http://www.carlopescio.com/2012/12/ask-not-what-object-is-but.html It's about: minimize the sum of the size of artifacts you change. - Or: don't change things, just add stuff

kurt-o-sys18:06:20

Simple rule, not always very simple to implement, but it's been very helpful to me ever since I read his blog.

hiredman18:06:11

just, given some code that running it under cljs with core.async and under clojure with core.async, what are the odds the code runs the same given that cljs and clojure don't share code, core.async cljs and core.async clojure don't share code, and whatever js runtime you pick may or may not share some amount of code with the jvm you pick

hiredman18:06:04

the only thing there to make them behave the same at all is eternal vigilance

qqq19:06:30

when an offer! fails, is there a way in a go-block to say: I'm going to chill for a bit, let other blocks run ?

bfabry20:06:46

that's exactly what alts! with a timeout will do

bfabry20:06:51

unless I'm misunderstanding

noisesmith20:06:58

@qqq more generally, you could just read from anything, even if you know it returns instantly, to give the go scheduler a chance to change things up

noisesmith20:06:33

or write to something, for that matter

bfabry20:06:26

(alt! (timeout 1) :write-failed [[put-port put-val]] :write-succeeded)

qqq21:06:27

@bfabry : good point; I somehow failed to make that mental connection; thanks!

alricu21:06:59

hi, is there any way to send a client certificate (.crt and .key) in a Get request? in clojure

alricu21:06:49

I am trying with Http.client but i cannot see the option

alricu21:06:02

also tried with http kit

alricu21:06:04

and no success

ghadi21:06:10

@alricu I believe you have to create a keystore (client cert / key), and a truststore (CA). Then you pass it as an option to clj-http https://github.com/dakrone/clj-http/#keystores-trust-stores

ghadi21:06:23

SSL in java is just the worst

ghadi21:06:56

way easier in most python libs or go

ghadi21:06:42

creating the keystore involves a baroque set of cli commands that you have to look up even if you've made them 1000 times

hiredman22:06:39

not that helps, because you likely have a cert to start with

qqq23:06:47

grab lock
 ... do some work
release lock
is this primitive basically not available in clojure ?

noisesmith23:06:31

there’s good reason to prefer lock free stuff, but locking does what you just described

hiredman23:06:53

the jvm lets any object used as a lock, called a "monitor", which is what locking does

hiredman23:06:10

generally it is better to use a purpose made lock than locking, if you need to lock

noisesmith23:06:19

you usually are better off with a design that doesn’t use it

qqq23:06:49

I need an "atom" where the swap! is NOT re-entrant ... where (swap! a f) <-- f is called 0precisely once

hiredman23:06:58

use compare-and-set!

noisesmith23:06:44

you can’t really have correct code with precisely once can you? at most once is reasonable

qqq23:06:55

this is not for network delivery

noisesmith23:06:01

(as opposed to swap! which is at least once)

noisesmith23:06:15

well, you can’t have exactly once semantics over a network period

qqq23:06:25

I don't think compare-and-set! works; it might evaluate f on an old value

qqq23:06:47

@noisesmith: you can't reason about computer programs unless you have 'precisely once execution of functions locally'

hoff23:06:57

when a test.check run causes an exception, where does it say what value caused the error?

noisesmith23:06:22

@qqq but we are talking about N threads, once you have that precisely once doesn’t really work out

qqq23:06:46

@noisesmith: I think you are making untrue assumptions.

qqq23:06:49

Let's see how to clarify this.

qqq23:06:35

Imagine we have an initial state s0; imageine wse have a bunch of functions f1, f2, f3, f4, f5, .... each of which has type signature :: state -> state

gfredericks23:06:20

@hoff how are you invoking test.check?

qqq23:06:30

(defn exec [s f] (locking s (reset! s (f @s))) // this guarantess precisely once invocation

hoff23:06:32

defspec and then lein test

noisesmith23:06:51

because someone else could have the lock

gfredericks23:06:51

@hoff it should print a map that has both the initial problematic value and the shrunken one

qqq23:06:13

yeah, so you wait until they are done

qqq23:06:15

then you execute

qqq23:06:21

it's precisely once unless deadlocks

noisesmith23:06:27

like I said, zero or once, they could hold on until exit

hoff23:06:30

I'm just getting '{:result #error { :cause nil :via [{:type java.lang.StackOverflowError :message nil :at [clojure.lang.RT seq "RT.java" 525]}] :trace [[clojure.lang.RT seq "RT.java" 525]'

hoff23:06:40

and then a million lines from the stackoverflow

gfredericks23:06:45

@hoff check below the stack

gfredericks23:06:50

for a key :fail

qqq23:06:56

@noisesmith : I don't think that is the official definitino of 'zero or once'

gfredericks23:06:25

@hoff then you'll also see a top-level key :shrunk with a key :smallest

qqq23:06:48

by your argument, even sinl-gle threadced programs are also only zero-or-once, because, in theory, you could throw the computer out the window

noisesmith23:06:57

@qqq it’s a basic rule of concurrent systems, you can have zero or one, you can have one or more, you can’t have precisely once.

noisesmith23:06:06

once concurrency exists those are your conditions

qqq23:06:20

let's stop having this conversation

qqq23:06:22

it's a waste of both our time

hoff23:06:45

found it, it was hidden at the end of the last line. (there was a stacktrace line shifting it off to the right)

bfabry23:06:15

@qqq presumably you want exactly-once because f has side-effects?

gfredericks23:06:02

@hoff some sort of default manner of printing exceptions by clojure proper has unfortunate effects like that

noisesmith23:06:31

@qqq anyway, calling compare-and-set! and only calling f if the function returns true is an alternative but it doesn’t prevent calling on stale state, otherwise yeah you need a lock.

noisesmith23:06:11

but compare-and-set! will respond false if your state is old

qqq23:06:21

@bfabry : yes, f_i can have side effects; what I want is when I feed it f1, f2, f3, ... f9 (from possibly different threads) -- I want each f_i to execute precisely once, but I don't care which order they evalute in; here, "preisely once -- assumes no deadlock, no crashing"

qqq23:06:49

compare_and_set! doesn'w ork , because by the time we reailze the state is old, we al4ready did the sideeffects

noisesmith23:06:05

sounds a lot like an agent - they prevent a lot of the issues with using locking directly

noisesmith23:06:22

any reason why one wouldn’t do what you want?

bfabry23:06:01

yeah so atoms (generally) are not the primitives you want to be using to do something like that. atoms are for coordinating modifications to the atom, not for coordinating arbitrary modifications to the world. I'd suggest core.async (or maybe agents but I dunno much about them)

qqq23:06:06

@noisesmith @bfabry : agents would be perfect, if only they were synchronous

noisesmith23:06:06

what about a send wrapper that passes in a promise that gets delivered so the caller can block on it? maybe that’s hokey

qqq23:06:25

interesting

qqq23:06:33

how do I get a "blocking promise"

noisesmith23:06:41

promises are blocking promises

noisesmith23:06:50

@p will block until someone delivers to it

noisesmith23:06:10

p being defined as (let [p (promise)] …)

qqq23:06:16

the callback on the agent then calls deliver

qqq23:06:20

I think this will do; thanks!

noisesmith23:06:51

await is for all pending actions, not just the one you just sent - but it is simpler, yes

noisesmith23:06:12

depending on usage pattern await could be better

noisesmith23:06:17

probably better

qqq23:06:38

https://groups.google.com/forum/#!topic/clojure/hvc2fzYYAWc <-- is there no promise for cljs yet? (I'm trying to write files in cljc)

noisesmith23:06:54

there are no promises, and no agents, in cljs

qqq23:06:06

argh lol

qqq23:06:16

okay, core.async it is 🙂

noisesmith23:06:17

if your code needs to be cljc use core.async

qqq23:06:44

well, thanks for helping me work through all the possible choices in the design space

qqq23:06:55

I feel like I get why things have to do be a certai way now

noisesmith23:06:24

Sorry for being pedantic about the “no such thing as exactly once” thing, it’s a peeve of mine in distsys and isn’t an issue for most single server code. Systems that are distributed and claim exactly once are typically doing something disasterously wrong.

noisesmith23:06:58

and really, “at most once” isn’t so bad 😄

qqq23:06:04

I completely agree with you taht there is no such thing as "exactly once delivery"

qqq23:06:40

but I feel like in a single machine environment, even if it's multi core multi processed, unless the machine halts / crashes / inf-loops, it is possible to guarantee "exactly once execution" -- otherwise, we dcan't reasonab about programs

noisesmith23:06:05

> we can’t reason about programs agreed!

bfabry23:06:33

for what it's worth Google Dataflow promises "exactly-once semantics" which means that it rolls back the results of extra executions (gets thrown out the window if you do side-effects)

weavejester23:06:56

Executing something once is possible in practice, with the usual caveats about being on a single machine and assuming reliable hardware/OS/VM.

weavejester23:06:58

But yeah, once you distribute stuff or get to the point where reliability of the underlying system has to be taken into account, all bets are off.

weavejester23:06:15

And the byzantine general problem rears its head.