Fork me on GitHub
#clojure
<
2018-06-25
>
Garrett Hopper03:06:25

I know it's probably almost never used, but is there a good way to unquote and deref an atom in a macro?

(defmacro example []
  (let [a (atom nil)]
    `(println ~@a)))
The best I've got is
(defmacro example []
  (let [a (atom nil)]
    `(println ~(deref a))))
or
(defmacro example []
  (let [a (atom nil)]
    `(println ~(-> @a))))
Something feels wrong about both though.

mg03:06:29

Might as well deref in the let

Garrett Hopper03:06:55

I suppose this was a bad example, but you have a point. I can just redefine the symbol as the derefed version, since the atom's state is created in other let bindings.

Timo Freiberg10:06:08

or (to prevent an XY problem), can anybody suggest a way to only eval once instead of once per line?

Timo Freiberg10:06:19

i think i got it.

(defn make-filter
  [filter-expr-str]
  (let [raw-expr (read-string filter-expr-str)
        filter-expr (partial
                     (eval `(fn [~(symbol "it")]
                              ~raw-expr)))]
    filter-expr))
seems to evaluate the predicate once in the closure!

👍 4
cjohansen13:06:12

spec question: is there some way to make spec consider nil the absence of a key? When using s/keys with optional keys, the spec still fails if I include the key with an explicit nil, which causes me to define the key as something that is either nil or a usable thing

cjohansen13:06:33

(s/def ::key string?)
(s/def ::mymap (s/keys ::opt [::key]))

(s/valid? ::mymap {}) ;; true
(s/valid? ::mymap {::key nil}) ;; false

joelsanchez13:06:28

you have spec/nilable for that

cjohansen13:06:01

a little bit better, but it still defines the key as nilable. ideally I’d like to say that nils are OK for optionals

cjohansen13:06:26

with nilable, you can sneak nils into required map keys, which I don’t want

cjohansen13:06:50

guess I could write my own instrument that would strip nils from maps

joelsanchez13:06:11

> you can sneak nils into required map keys well, the idea is to make nilable only the optional keys (not the required keys). but I understand that this solution might not be ideal for you.

cjohansen13:06:14

this stops working the moment you have a key that is required in one context, and optional in another

cjohansen13:06:35

and thus works against the idea of having global reusable specs

andre.stylianos13:06:50

I'm not sure I understand, but isn't what you're doing what's actually working "against the idea of having global reusable specs"? If you say in one place that ::key should be a string, but you can pass ::key nil in another place then (s/def ::key string?) is not globally true.

andre.stylianos13:06:58

The thing is that you want to make ::key nilable in the context of ::mymap, but then it's no longer ::key according to your spec because now it can be nil

andre.stylianos13:06:42

You could do something like

(s/def ::key string?)
(s/def :mymap/key (s/nilable ::key))
(s/def ::mymap (s/keys ::opt [:mymap/key]))

andre.stylianos13:06:20

which is more verbose but still stays true to the global specs

cjohansen13:06:38

Yes, this works, but is very cumbersome. Given that Clojure has nil punning almost everywhere I’d expect this to be the default behavior of :opt

cjohansen13:06:04

e.g., I can’t think of any other area in Clojure where {::key nil} would be handled much differently from {}

cjohansen13:06:25

maybe I’m wrong

andre.stylianos13:06:29

(let [{:keys [a b] :or {a 1}} {:a nil :b 3}]
    [a b])
=> [nil 3]

(let [{:keys [a b] :or {a 1}} {:b 3}]
   [a b])
=> [1 3]

cjohansen13:06:53

hah, there’s one 🙂

andre.stylianos13:06:56

this would be one case 😛

cjohansen13:06:47

(s/keys :req [] :opt [] :opt-nillable []) 😛

4
cjohansen13:06:25

I agree with all of the rationale behind spec, that’s why I’m using it. I just slightly disagree with the definition of “optional” and nils

cjohansen13:06:51

typically explicit nils will be the result of things like this:

cjohansen13:06:12

(some-fn {:some-key (when some-val (compute some-val))})

cjohansen13:06:32

but I can write it as:

cjohansen13:06:10

(some-fn (merge {:default "Keys"}
                (when some-val {:some-key (compute some-val))}))

cjohansen13:06:52

and it’s not the end of the world… just results in a bit more friction when introducing spec into an existing codebase

andre.stylianos13:06:28

(cond-> {:default "Keys"}
            some-val (assoc :some-key (compute some-val)))
is also an option if you have a lot of cases

andre.stylianos13:06:13

and I can see where you're coming from, it's definitely more verbose

cjohansen13:06:31

thanks for chipping in

joelsanchez13:06:17

@andre.stylianos come on man, we have good ol' remove-nil-values-from-map

(defn remove-nil-values-from-map [m]
  (->> m
       (filter second)
       (into {})))
no need for cond->/`as->` + assoc-in hell imo

😂 4
andre.stylianos13:06:54

There's that 😂

dpsutton13:06:55

that will remove false values as well though right?

dpsutton13:06:11

(filter (comp some? second)) maybe? (had order backwards)

dpsutton13:06:20

i get those backwards all the time 🙂

😂 4
8
joelsanchez13:06:37

(me too. also empty and empty?...)

mpenet13:06:26

you can squeeze that in an xform for extra points

mpenet13:06:40

(into {} (filter val) m)

☝️ 4
joelsanchez13:06:30

(into {} (filter (comp some? val)) m) summing up then?

jdkealy15:06:25

Hi, I just switched from http-kit to jetty. I have an endpoint that when i request it, i get a "java.lang.StackOverflowError" ... the same endpoint returns fine in http-kit and also, when i repl onto the server and try calling the function directly with the same parameters, it also returns ok. Anyone have any suggestion as to how i would debug this ?

hyankov15:06:29

I’ve a question about swap!. According to the doc:

Atomically swaps the value of atom to be:
  (apply f current-value-of-atom args). Note that f may be called
  multiple times, and thus should be free of side effects.  Returns
  the value that was swapped in.
I have println statements in f. Thus, strictly speaking the function has side effects, however, it always returns the same value for given input so it should be safe to use it. Am I missing something fundamental here?

bherrmann15:06:16

I think that is fine, as long as no one relies on the println statements

hyankov15:06:08

Thanks for replying @bherrmann Why is that? What could happen to the print statements?

bherrmann15:06:23

could be invoked multiple times

bherrmann15:06:43

swap might try repeatedly to update the atom… so it is possible for f to be invoked more than once…. if there is contention for the atom.

hyankov15:06:08

If two threads are calling swap! on the same atom at the same time one of the callers might have to repeat?

hyankov15:06:43

Oh, you were faster 🙂 Thanks a lot!

carocad15:06:32

@alexmiller what would be the workflow to get strint templating working in clojurescript? I am willing to work on it but I have never submitted a patch to clojure code 🙂 https://github.com/clojure/core.incubator/blob/master/src/main/clojure/clojure/core/strint.clj

Alex Miller (Clojure team)15:06:27

if you’re asking about cljs, I’d head to #cljs-dev

Alex Miller (Clojure team)15:06:18

however, there is support for some of this stuff in format and cl-format (not sure the status of those in cljs)

lilactown16:06:02

what are thoughts on side effects in macros? bad idea, or horrible idea?

lilactown16:06:35

e.g. a macro that performs some analysis and writes to a file

lilactown16:06:56

(I'm actually thinking about this more in a CLJS context so maybe I should ask in that channel)

danm16:06:27

What analysis are you expecting it to write? If it's anything mutable then how would that work?

the2bears16:06:33

@lilactown if the side effect can be done in its own function, that would be my preferred way to handle it. Ask yourself why you need the macro.

lilactown16:06:35

the usecase I was thinking of was generating a .css file from a clojure form

the2bears16:06:35

In that case it seems reasonable, since you want the form unevaluated I would assume.

the2bears16:06:56

But there may be better ways that I can't think of yet pre-caffeine 🙂

stathissideris16:06:21

with deps.edn, I’m trying to override a git dep with a local dep. My deps.edn looks like this:

{:deps
 {org.clojure/tools.analyzer.jvm {:mvn/version "0.7.2"}
  seesaw                         {:mvn/version "1.5.0"}
  rakk                           {:git/url ""
                                  :sha     "a4b5717086e66989856a45a1f6e5d04628e6aec3"}
  cross-parinfer                 {:mvn/version "1.4.5"}
  org.clojure/tools.deps.alpha   {:git/url ""
                                  :sha     "d492e97259c013ba401c5238842cd3445839d020"}}
 :aliases
 {:repl {:extra-deps {org.clojure/tools.nrepl {:mvn/version "0.2.13"}
                      cider/cider-nrepl       {:mvn/version "0.17.0"}}}
  :local {:override-deps {rakk {:local/root "/Users/sideris/devel/rakk"}}}}}

stathissideris16:06:57

but when I do clj -Sforce -R:local:repl -Spath I see that rakk is coming from git rather than the local dir

Alex Miller (Clojure team)16:06:09

This doesn’t work yet

Alex Miller (Clojure team)16:06:34

There’s a ticket for it

stathissideris16:06:11

@alexmiller is it the particular combination of git+local or overriding doesn’t work in general?

stathissideris16:06:59

@alexmiller also do think in the future there may be a way to say “also merge this local deps.edn into the main map” to avoid having to commit machine-specific paths to git such as "/Users/sideris/devel/rakk"

Alex Miller (Clojure team)16:06:49

The issue is with changing and comparing versions across coord types

stathissideris16:06:51

hm, I can imagine how that could be a problem

Alex Miller (Clojure team)16:06:08

I’m trying to understand your other question there

Alex Miller (Clojure team)16:06:21

And how it’s different from -Sdeps

stathissideris17:06:03

-Sdeps replaces deps.edn, correct? I was looking for something that would merge a snippet into an existing deps.edn

Alex Miller (Clojure team)17:06:35

no it merges over the rest

Alex Miller (Clojure team)17:06:50

it acts as a final deps.edn in the merge chain

stathissideris17:06:57

oh sorry, that would work then

stathissideris17:06:24

> -Sdeps DEPS Deps data to use as the final deps file the word “final” threw me off, I thought it meant that it replaces everything

stathissideris17:06:48

maybe “last” would be less ambiguous, maybe it’s just my brain 😄

stathissideris17:06:12

thanks for clarifying

JH17:06:03

Hello fellow Clojurians, I just wanted to put out a general question for the community. What is the benefit of writing css using garden compared to just writing plain old css in css files?

Alex Miller (Clojure team)17:06:07

presumably, b/c it’s data and you can manipulate it as data

Alex Miller (Clojure team)17:06:36

but if you don’t need to do that, then that’s not much benefit

JH17:06:47

Yeah that's what I thought, from what I've seen it's typically written in clojure. The downside I see to this is if you want to manipulate it you'd have to do it on the backend. The main benefit I was hoping to get out of it is to have dynamic css within clojurescript, where events would trigger changes in css. However I haven't found a great way of doing so.

sova-soars-the-sora17:06:28

hi i'm trying to use java.io.copy to save an html uploaded image to local disk and when i invoke` (copy (io/file tempfile) (io/file "some-dir-name-i-just-invented-because-i-want-a-new-file-here.jpg"))` i'm informed that the destination file doesn't exist. scratches head

folcon17:06:41

@sova just to check, are you calling:

(io/copy (io/file "tempfile") (io/file "new.jpg"))
?

noisesmith17:06:43

@sova I don't think copy creates directories implicitly

noisesmith17:06:10

but there's a separate method on File that will do that given a File object

sova-soars-the-sora17:06:02

i need to have the directories exist already

noisesmith17:06:37

this is an intentional feature btw - for example it can prevent a bad user input from making you create a file on a malicious path

4
sova-soars-the-sora17:06:45

excellent. now this image board will actually work now.

sova-soars-the-sora17:06:22

yeah good call. and i'd rather not accidentally create ten thousand directories just trying to see where stuff gets saved

sova-soars-the-sora17:06:14

thanks for your help guys. step by step great accomplishments

folcon17:06:52

Out of curiosity, does anyone recognise getting a StackOverflowError java.util.regex.Pattern$BmpCharProperty.match exception when running (apply str a-large-coll-of-strs)? It’s rather puzzling how it’s happening. If it helps the coll contains seqs of the file contents from an unpacked zipfile.

hiredman17:06:22

are you sure the exception is happening there?

hiredman17:06:56

I would guess you a regex searching the string after you are constructing it, and the error is being thrown there

noisesmith17:06:22

it could be an error in something lazy that created a-large-coll-of-strs

folcon17:06:39

Possibly, I’ve got a zipfile and a reducer which pulls out the content of a matching file, so I get a big list of strings. Big being relative, (count coll) gives me 1004. (every? string? coll) gives me true.

folcon17:06:13

so I’m pretty sure I only have strings in there, but (apply str coll) just throws the exception

folcon17:06:00

I’m more than willing to admit my java interop isn’t great, but I’m really not sure whats up here…

hiredman17:06:36

how sure are you that the apply str is what is throwing the exception? do you have a stracktrace with its line number?

folcon17:06:51

now running (apply str (take 800 coll)) works perfectly

dpsutton17:06:51

what about (apply str (drop 800 coll))

folcon17:06:32

(apply str (take 900 coll)) does not, but (apply str (take 200 (drop 800 coll))) does

hiredman17:06:35

apply str doesn't do anything with regexs, which is why I am asking the question about how sure you are

folcon17:06:13

Not certain, that’s why I’m really puzzled. The problem is the error is a stackoverflow error, and I have no idea how to read something that deep

hiredman17:06:39

what do you mean?

hiredman17:06:27

the stracktrace isn't printing out?

folcon17:06:35

*e just gives me: elided at the top as it goes off the terminal

...
  [java.util.regex.Pattern$BmpCharProperty match "Pattern.java" 3798]
  [java.util.regex.Pattern$GroupHead match "Pattern.java" 4658]
  [java.util.regex.Pattern$Loop match "Pattern.java" 4785]
  [java.util.regex.Pattern$GroupTail match "Pattern.java" 4717]
  [java.util.regex.Pattern$Curly match0 "Pattern.java" 4272]
  [java.util.regex.Pattern$Curly match "Pattern.java" 4234]
  [java.util.regex.Pattern$CharProperty match "Pattern.java" 3777]
  [java.util.regex.Pattern$BmpCharProperty match "Pattern.java" 3798]
  [java.util.regex.Pattern$GroupHead match "Pattern.java" 4658]
  [java.util.regex.Pattern$Loop match "Pattern.java" 4785]]}

folcon17:06:45

It’s pretty long

hiredman17:06:34

so print it out in something that will let you scroll

folcon18:06:01

It’s scrolling off the top of the repl

hiredman18:06:02

near the top (at the top modula things like reflection) will be the line that caused the error

hiredman18:06:15

use a better repl then

hiredman18:06:32

or a terminal with scroll back, or tmux, or run it in emacs, or whatever

dpsutton18:06:06

the point is your error happened a long time before (apply str ... its just the the computation was delayed until it was realized here

folcon18:06:39

yep, just trying to remember how to do print -> str so I can take the first 200 characters. (.printStackTrace *e)

dpsutton18:06:58

are you just interacting through a terminal?

hiredman18:06:09

my guess, entirely unsupported at the moment, is you are printing the big string out at the repl, and your repl client is actually trying to run a regex on it

folcon18:06:41

I’m using a terminal

folcon18:06:10

Ok, so the test would be to do a take on the result

folcon18:06:32

@hiredman, that was brilliant, your supposition is accurate it seems!

folcon18:06:00

Right, time to file a bug with the repl author 😃

folcon18:06:16

or a warning

dpsutton18:06:01

is it rebel readline?

dpsutton18:06:11

can't think of how else you would have java in the repl

dpsutton18:06:22

nice sleuthing hiredman

folcon18:06:32

very nice, very impressed!

folcon18:06:37

Thank you 😃

dpsutton18:06:46

his tokenizer is regex based

folcon18:06:41

Yep, also found that someone’s added the issue, so he’s aware of it

folcon18:06:46

No point bugging him again.

metacritical18:06:56

@dpsutton Spec would have been a more readable solution.

foobar18:06:10

How can I use clojure and avoid the JVM ?

nenadalm19:06:00

you can use clojurescript (on nodejs). If you wanted some framework, you could use macchiato https://macchiato-framework.github.io/

hiredman18:06:01

I would recommend using learning clojure to get over whatever prejudices your have acquired against the jvm

foobar18:06:52

^Its the fact that its heading in the direction of no longer being open source

hiredman18:06:30

there is a self hosted clojurescript effort, but I am not sure how much that is used, and there is a clr clojure port which is battling against the static language trend on the clr

hiredman18:06:37

what makes you say that?

foobar18:06:57

Oracle are going to charge for Java SE, the latest developments will be paid for, so over time the openjdk efforts are going to be under a lot of pressure

hiredman18:06:57

it sounds like maybe you only read the title on a hacker news link? I see oracle is going to start charging for support of java se releases that are older then what they have stated they will support

hiredman18:06:17

and that is oracle's jvm, which is not openjdk

foobar19:06:28

But what If openjdk cannot implement java 11 say?

ghadi20:06:56

openjdk is now the official, canonical distribution -- they start with implementing Java specs in openjdk. It's GPL

danielglauser19:06:10

Clojure works just fine on the Oracle JVM.

dpsutton19:06:19

clojure targets older style bytecode so that shouldn't be a problem anyways

dpsutton19:06:47

that's a gist but probably not technically correct. Java 1.6 bytecode?

noisesmith19:06:49

right, it targets 1.6, there's been talk about upgrading to 1.7 for invoke-dynamic but I don't think that's actually on the radar yet

dpsutton19:06:12

is it the emitted java code or emitted bytecode that targets 1.6. My knowledge of jvm internals is quite thin

noisesmith19:06:48

bytecode is generated for 1.6, the java is compiled to emit bytecode for 1.6 also

noisesmith19:06:06

there is no java code emitted anywhere

foobar19:06:46

Ok, but clojure without java interop is limited so its fairly tied to java

noisesmith19:06:51

cljs and clj-clr are both up to date, and both are able to do everything to interop with their host that jvm clojure can do, up to the limits of the host

noisesmith19:06:13

the difference is that there's less of an ecosystem with tooling and libraries for those platforms

noisesmith19:06:29

(but cljs comes close)

foobar19:06:40

So nodejs is the only choice? Does clj-clr run on the core clr?

noisesmith19:06:59

cljs runs on nodejs, or rhino, or a browser js, there's also planck which self-hosts with OS provided js apis

noisesmith19:06:40

but most libraries use features only found in a browser or node

noisesmith19:06:16

clj-clr runs with microsoft's clr or mono

ghadi19:06:13

oracle is not closing up the jdk, basically the opposite

ghadi19:06:30

this is a common misconception on ill-informed hacker news and reddit comments

ghadi19:06:56

They just opened up a ton of previously commercial features (JFR tools)

ghadi19:06:37

Speaking of JDKs -- what do people do on MacOS to switch JDKs quickly?

ghadi19:06:56

cool - thanks. Does jenv also install them, or just manage environments?

reborg19:06:07

I think I had to install them, I had a bunch already, I just connected to them

mfikes19:06:38

I set JAVA_HOME using java_home, which is sufficient. Example:

export JAVA_HOME=`/usr/libexec/java_home -v 1.8`

4
reborg19:06:32

Even with different JDKs each project?

jumar08:06:07

export JAVA_HOME doesn't help you much when you have non-cmd apps that use JDKs. I'd like to look at jEnv at some point. Currently, I set JAVA_HOME env variable in ~/Library/LaunchAgents/environment.plist

sova-soars-the-sora20:06:47

is there a way i can invoke a function with an optional argument / arity 3 but sometimes 4

dpsutton20:06:18

what do you imagine it would look like? I can think of a few ways to do this

sova-soars-the-sora20:06:19

something like

(([one two three] ... 
([one two thee free] ..))
perhaps?

sova-soars-the-sora20:06:39

okay. i call this function on a /post command. it has an optional image parameter. i want to do that only when image isn't nil... i don't want to write nearly duplicate functions for slightly different scenarios... so maybe i will just always pass four and have that fourth one sometimes be nil.

sova-soars-the-sora02:06:45

turns out just letting it be nil is a great option. haha

Nick Cabral20:06:19

Anyone see a problem with picking one naming convention for a project, rather than using language-specific conventions at each layer of your stack? Seems more trouble than it’s worth to relate user-id in clojure to user_id in postgres and to userId in frontend (typescript) code.

polymeris21:06:59

I don't think it's that much trouble if you have some middleware handle it.

Nick Cabral20:06:36

I know the answer is “do whatever you want if it’s all your code”. Just interested in other opinions

jon20:06:51

I don't mind the stack specific convention, because trying to force one can sometimes be much more difficult than it seems, say if something you have no control over is introduced and it needs things to be stack specific convention to work