Fork me on GitHub
#clojure
<
2018-01-15
>
andy.fingerhut00:01:21

I'm pretty sure it is not a documented feature, but all versions of Clojure I am aware of read %1.1 %1.2 etc without any errors, and truncate the floating point values to the next lowest integer. That is, #(f %1.1 %1.2 %1.3) is syntactically legal and completely equivalent in behavior to #(f %1 %1 %1)

Alex Miller (Clojure team)01:01:40

rely on that at your peril

andy.fingerhut00:01:49

The closest thing to what you want that I can think of is to use destructuring, e.g.

andy.fingerhut00:01:02

(map (fn [[a b]] (f a b)) ...)

qqq00:01:42

fasicnating, problem is, to use destructuring, I need to use (fn of (let .. which makes it verbose

andy.fingerhut00:01:10

It is less verbose than using first, second, third, etc.

qqq00:01:35

but less clean and more verbose than %1.1 %1.2 (if it worked as intended)

andy.fingerhut00:01:31

Clojure does work the way it was intended, just not by anyone's intentions except Rich Hickey 🙂

andy.fingerhut00:01:28

More nice stuff about destructuring approach -- you can nest it arbitrarily deeply, if you want. https://clojure.org/guides/destructuring is a nice set of examples and explanatory docs for those who haven't used the feature before.

qqq00:01:58

I'd prefer "java/c struct dot notation", to write something like

qqq00:01:17

%1.2.:foo.:bar instead of (:bar (:foo (second %1)))

qqq00:01:25

[for anonymous functions]

andy.fingerhut00:01:47

Have you used expressions like (-> 1 (nth 2) :foo :bar) before?

seancorfield00:01:05

(get-in % [1 :foo :bar])

andy.fingerhut00:01:07

edit: (-> %1 (nth 2) :foo :bar)

qqq00:01:14

yes, but for #(...), I'm looking for things taht fit on one line, so every char counts

andy.fingerhut00:01:30

Anyone is free to create modified versions of Clojure that work however they want, but you cannot legally distribute them as something called Clojure (at least not under US law), and you are not likely to get a lot of users of your modified version.

qqq00:01:50

whoa, I'm not sure how we jumped to forking cojure

qqq00:01:24

it seems like it'd be much easier to just dfeine a new macro, i.e. (defmacro ff ... ) and changing #(.... ) to (ff .... ) where the ff looks at symbols starting with % and rewrites them

andy.fingerhut00:01:25

I'm not suggesting it. You said you are looking for something. I am telling you where you can find it (or in this case, create it, since I don't think it currently exists)

andy.fingerhut00:01:04

The syntax you give as an example: %1.2.:foo.:bar is unreadable by the existing Clojure reader, so not possible to do exactly that via a macro definition.

andy.fingerhut00:01:28

Things starting with % in Clojure are not symbols. I'm not sure if they have been given a name, but they are restricted to contain things that can be read as a number after the %

qqq00:01:22

ah, I see, perhaps somethign like '12_foo_bar instead then

noisesmith00:01:24

I thought % was just another symbol constituant

noisesmith00:01:23

user=> (let [%a 1] (+ %a %a %a))
3

andy.fingerhut00:01:43

I am pretty sure reading of % is special within a #( ) expression

andy.fingerhut00:01:10

Yeah, looks like it can be used as part of a normal symbol outside of #( )

noisesmith00:01:48

oh, yeah, there we go

+user=> (#(let [%a %] (+ %a %a %a)) 2)
IllegalStateException arg literal must be %, %& or %integer  clojure.lang.LispReader$ArgReader.invoke (LispReader.java:928)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: % in this context, compiling:(NO_SOURCE_PATH:0:0) 
RuntimeException Unmatched delimiter: ]  clojure.lang.Util.runtimeException (Util.java:221)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: %a in this context, compiling:(NO_SOURCE_PATH:4:15) 
RuntimeException Unmatched delimiter: )  clojure.lang.Util.runtimeException (Util.java:221)
2
RuntimeException Unmatched delimiter: )  clojure.lang.Util.runtimeException (Util.java:221)

andy.fingerhut00:01:13

@qqq If you want to stick with straight Clojure, then you have to have at least a single-character name for your macro, and enclose invocation of that macro inside parens, so that is 3 chars plus a space. Only 1 less than (fn )

andy.fingerhut00:01:32

Yes, fn requires the arg vector, too, which #( ... ) does not.

andy.fingerhut00:01:20

Hmm. I guess maybe possible to write a macro invoked like (f ...) instead of #( ... ) that did something like you want. It would not be a straightforward macro to write, depending on how general you wanted it to be, e.g. whether one could use macro invocations inside of (f ...)

qqq01:01:13

@andy.fingerhut: when I want to destructure an entire structure, I like destructuring notation; when I am just 'selecting' a portion of the structure, I like get-in .... alot more, and this saves quite a bit over get-in, especially considering 80-char lines

andy.fingerhut01:01:59

I understand. If what you are asking for already exists, I haven't heard of it, but I don't claim anywhere near 100% knowledge of Clojure libraries. I am 99.9% sure it isn't in the core Clojure library.

chrisjd07:01:39

@qqq I think (map (partial apply f) lst) is how I would do what you want.

rauh09:01:37

Shouldn't concat be lazier in this case? It prints the "X" even though it's never needed:

(take 3 (apply concat [1] [2] [3] (lazy-seq (prn "X") [[4]])))

schmee09:01:11

lazy seqs are “chunked”, IIRC 32 elements are realized at a time

mpenet09:01:05

Not all. Depends on what produces the lazy-seq

rauh09:01:45

@schmee Yeah I'm aware, but not in this case there is no chunking. I just noticed the use of next instead of rest for concat so I came up with arguments that show the potential problem (since next is less lazy than rest).

crankyadmin14:01:57

Hi, I have an xml that needs parsing and I'm down a rabbit hole trying to work out the easiest way to go about go this. I need to extract the values log-start-date player-name player-uuid log-end-date and get back a list of the entries.

crankyadmin14:01:46

everything I've tried to date either a, doesn't give me what I'd like b, way too complicated and doesn't feel very clojure like.

crankyadmin14:01:56

I worked it work... too many late nights 🙂

crankyadmin14:01:36

Hmmm may be not...

crankyadmin14:01:27

I just need a list (of some sort) of the entires back now... currently I appear to be getting a concat'd string back.

Alonoaky15:01:24

hey, anyone managed to use lein-protobuf with proto3? trying not to use a custom protoc executable. or maybe there's a better option for proto3? thanks

edwaraco15:01:58

Hi. I'm building one test case and I need to verify if one function that runs in background is invoked. Currently I'm using something like:

(defn do-something-1 [a b c]
  ;; dosemthing)

(defn do-something-2 [a]
  (let [b 1 c 2]
    (send-off (agent  a) b c)))
And my test case is:
(deftest test-do-something-2
  (let [invoked? (promise)]
    (with-redefs [do-something-1 (fn [a b c]
                                    (is (= a 3))
                                    (is (= b 1))
                                    (is (= c 2))
                                    (deliver invoked? true))]
      (do-something-2 3)
      (is (true? (deref invoked? 5 false)))))
It works as long as do-something-1 function is invoked in less 5 millisecond. Thanks for some suggestion. PDT. Sorry for my english 🙂

noisesmith17:01:39

@rauh @schmee it’s not chunking doing this - it’s because apply needs to access the last arg, so the implicit do of the lazy-seq body is realized

=> (take 3 (apply concat '([1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30] [31] [32]) (lazy-seq (prn 'x) [[4]])))
x
([1] [2] [3])

noisesmith17:01:57

or… it might not be that simple, but I don’t see a reason to think concat is doing it

rauh17:01:07

@noisesmith Oh I see, good catch.

noisesmith17:01:11

@rauh - this is the same behavior I think

=> (do (apply map inc (lazy-seq [[(do (prn 'x) 1) 2 3]])) nil)
x
nil

ghadi17:01:15

it's the apply of concat that is doing it. See https://dev.clojure.org/jira/browse/CLJ-1218

ghadi17:01:04

(IIRC, because concat has an arity-4 defined, applying it will realize at least 4 elements of its input)

bronsa17:01:36

nah, it’s apply

user=> (apply (fn [a & _] a) 1 (lazy-seq (prn "foo") [1]))
"foo"
1

ghadi17:01:16

@bronsa yes for that case -- I meant the original example with apply concat

bronsa17:01:05

@ghadi I believe even in the original example concat has nothing to do with this in this case — the last sequence gets forced before it reaches concat

bronsa17:01:46

user=> (def x (list* 1 2 3 (lazy-seq (prn "foo") [1])))
#'user/x
user=> (def x (list* 1 2 3 4 (lazy-seq (prn "foo") [1])))
"foo"
#'user/x

bronsa17:01:26

we’re both kinda wrong

bronsa17:01:53

user=> (.applyTo (fn [& _] 1) (list* [1] [2] [3] (lazy-seq (prn "foo") [[1]])))
1
user=> (.applyTo (fn [& _] 1) (list* [1] [2] [3] [4] (lazy-seq (prn "foo") [[1]])))
"foo"
1
user=> (.applyTo concat (list* [1] [2] [3] (lazy-seq (prn "foo") [[1]])))
"foo"
(1 2 3 1)

bronsa17:01:07

so.. it’s either concat or apply depending on how many padding arguments you have

itaied17:01:48

I have the following bootstrap for my webapp:

(defn -main
  [& args]
  (try
    (let [c (create-config-spec env)]
      (component/start (system/new-system c)))
    (catch Exception e
      (println e))))
When running the app via lein or uberjar, the exception is printed with the classpath, though in the repl it doesn't. How can I configure it to exclude the classpath as it just redundant and too verbose?

noisesmith18:01:19

regarding the lack of laziness and concat vs. apply as a culprit - backing up for a second to the big picture, I’d say the lesson here is to never write code that would break if an extra 32 items of your lazy seq were unexpectedly realized early; if you are doing side effects over a lazy input use a separate imperative step after all the lazy operations (eg. run! or doseq) - if the take call were inside run! or doseq the extra realized elements would not be a problem

octahedrion20:01:18

is there a way to cancel execution in the new default clj repl ?

octahedrion20:01:53

also, why is that with lein repl and clj I have to use rlwrap to get basic things like history with arrow keys, whereas that comes for free with python's repl ?

octahedrion20:01:31

- i mean, i don't care but for beginners it's an extra step you have to tell them

schmee21:01:53

ctrl + c cancels execution

octahedrion21:01:05

no ctrl+c quits the repl

octahedrion21:01:17

in the clj repl

noisesmith21:01:30

@octo221 regarding the history stuff, that should be handled by jline, which both lein repl and clj support, though they probably won’t use your custom local readline config it’s pretty comprehensive feature wise, including control-r backward history completion

seancorfield21:01:01

I would consider the clj REPL to be a very bare bones experience, just good enough to get up and running. If rlwrap isn't available, use clojure instead (I believe that's the only difference between those scripts?). If you want a "better REPL", use boot repl or lein repl. clj is not intended to be a replacement for those.

ghadi21:01:39

for some value of "better" REPL

seancorfield21:01:25

Yeah, it's kinda subjective...

qqq21:01:36

Why would one ever use repl in the terminal instead of editor sending last-sexp or sending entire buffer to repl ?

rnagpal21:01:46

How do I set field of an abstract class which I am extending using gen-class in the init method ?

octahedrion21:01:07

ok i updated to the latest clj and that does have history - so that's nice, but still no ctrl+c to stop execution

octahedrion21:01:56

@qqq - well, I agree, but when you're demonstrating something quickly to someone you want to start a repl quickly

octahedrion21:01:32

- clj starts fast and has no dependencies apart from needing brew installed first

octahedrion21:01:53

(and to install brew all you have to do is paste some dodgy ruby code from your browser into your terminal)

seancorfield21:01:10

I don't think there's anything conceptually wrong with having a "simple" way to get up and running and then recommending Boot or Leiningen as the next step to graduate to -- after all, you'll need one of those tools to build/deploy any project.

seancorfield21:01:45

No one is going to be able to manage "forever" using just clj...

dpsutton21:01:36

i don't think that clj is a good tool for a beginner either

dpsutton21:01:48

when i was starting if i had to manage my own classpath and figure out how to load a file and remember that dashes become underscores on the file system i'd probably not be doing clojure

qqq21:01:07

To me, going from: 1. editing entire file in editor 2. switching to REPL terminal and typing in (load-file "...") to relolad to 1. editing in editor, hitting C-x C-e to send last sexp was transformational and I think newcomers should be shown this workflow asap

noisesmith21:01:15

I tutored for clojurebridge a while ago, and one of the biggest problems was that we wasted too much time with weird tooling that prioritized capabilities over stability or usability, so in addition to the natural beginners problems for people new to both programming and clojure, we also dealt with problems in the tools themselves

noisesmith21:01:24

I would rather have let the students write code in microsoft word (or whatever program they want) and just use a simple predictable tool with copy/paste

qqq21:01:28

non-fuxed-width fonts, no auto indentation, no matching ()? good luck 🙂

noisesmith21:01:41

to me something like clj is perfect if your goal is to see what this clojure thing is right now with as little hassle or nonsense as possible

dpsutton21:01:56

i think i would take the opposite side of that. i can't see anyone sticking with clojure on their own after an intro if they can't navigate and load code

noisesmith21:01:25

@qqq of course I would recommend notepad (or even sublime) - I’m just saying spending 30 minutes because nobody in the room understands why a tool is misbehaving is a terrible use of class time

dpsutton21:01:40

yeah that certainly sounds frustrating too

qqq21:01:45

maybe what is needed is something like "DrRacket" for Clojure

noisesmith21:01:03

there’s 30, take your pick, they all mostly almost work

Will21:01:21

Hi guys, I am having issues using the clojure.java.jdbc library to connect to a database. I have a gradle java project with some clojure code up and running. I have the library in my build.gradle and its pulling the dependency, but when I put the library in the require statement in my clojure namespace like this: (:require [clojure.java.jdbc :as jdbc]) I get this error: Caused by: java.lang.RuntimeException: Unable to resolve symbol: volatile! in this context

seancorfield22:01:22

Just FYI folks, this got answered in #beginners

noisesmith21:01:48

@dpsutton all you need for an intro class is “this is what the code looks like, this is how to call it, here’s how to copy and paste” - if they run into trickier things that is better served by the clojure knowledge of the instructors, rather than editor specific domain knowledge (of the 8 instructors in our room, none used the editor provided with the lesson as a primary editor, and most had never used it for a substantial task - and honestly you’d have that problem no matter which editor you pick)

dpsutton21:01:59

i was gonna help with an intro class down in new orleans but i had a wedding that weekend. I'd like to help out to see what their experiences are

dpsutton21:01:13

not sure if it was clojurebridge or not

qqq21:01:49

@noisesmith: the diff between dr razcket vs "the 30 clojure variants" is taht dr racket comes prepackaged with racket

noisesmith22:01:14

anyway that got kind of tangential. even if I am trying a new language, I’d vastly prefer something where I can hand it valid code and get my result, rather than having to spend a bunch of time learning weird UI that has nothing to do with the language itself