Fork me on GitHub
#beginners
<
2020-01-07
>
hindol06:01:43

For transducers, is a single step transducer filter #(and (int? %) (pos? %)) more efficient than a two step transducer (filter int?) (filter pos?) or are they same because no intermediate sequences are created anyways?

Alex Miller (Clojure team)13:01:40

Yes, it is faster as it will perform a single pass with no intermediate sequence objects, rather than two passes. Filter seqs are chunked (in 32-element chunks) so the benefits are smaller than you might expect. In general, transducers will increasingly win as you increase the number of transformations and/or the size of the collection.

hindol21:01:02

Are there other cases where the lazy seq is realized in chunks?

Alex Miller (Clojure team)22:01:04

and generally, I'd say that if you care which you're using, you shouldn't be using lazy seqs

seancorfield06:01:18

@hindenbug I wouldn't expect there to be much difference in practice -- I'd expect the single step to be slightly faster -- but I'd go with readability until it was proven I had a performance issue.

👍 4
seancorfield06:01:01

I ran some tests with Criterium and, yes, the single step is faster but it's not all that much faster (and the output I'm getting from my benchmarks are sufficiently variable that I can't really get an accurate read on the difference).

seancorfield06:01:57

(the slowest runs of the single-step are slower than the fastest runs of the two step)

hindol08:01:03

Perfect, thanks. I really need to start looking into criterium for quick perf tests like this.

grounded_sage08:01:38

This (slurp (:body (client/get "" {:as :stream}))) with the actual domain I am requesting. Returns this

Execution error (IOException) at org.apache.http.conn.EofSensorInputStream/isReadAllowed (EofSensorInputStream.java:107).
Attempted read on closed stream.
class .IOException

littleli09:01:53

can't you just get the content directly? (slurp "")

didibus10:01:26

Ya, you should be able to just slurp the URL directly

didibus10:01:47

But if not, you'd have to tell us at least what library client is from

grounded_sage13:01:11

I can't slurp directly because i run out of memory.

grounded_sage13:01:46

(defn append-to-file
  [file-name s]
  (spit file-name s :append true))

(defn write-json-file [file-name s]
  (with-open [rdr (io/reader (:body s))]
    (map #(append-to-file file-name (str % "\n"))
         (line-seq rdr))))

(defn fetch-data
  [{:keys [url query-params]}]
  (client/get url
              {:accept :json
               :as :stream
               :query-params query-params}))

(write-json-file "result.json"
                       (fetch-data {:url "}))

grounded_sage13:01:05

This is what I have so far. The client is clj-http

grounded_sage13:01:52

I am getting this

Error printing return value (IOException) at .BufferedReader/ensureOpen (BufferedReader.java:122).
Stream closed
class clojure.lang.ExceptionInfo

grounded_sage13:01:21

Though it does write the file to disk. How do I return a value? When I add a println as a message to say it's finished it doesn't write to disk. Adding a nil or ìdentity also stops it from writing to disk.

grounded_sage13:01:23

Ah figured it out. I use doseq

didibus19:01:44

ah ok, that's a really big webpage you're loading :p

grounded_sage08:01:22

Just realised it closes stream after first read. This is what I get when first running against the stream.

Execution error (OutOfMemoryError) at (REPL:1).
Java heap space
class java.lang.OutOfMemoryError

Ramon Rios09:01:40

Hey guys, i'm triyng to create a vector with some maps on it.

(def address-request {:uri "api/address"
                      :method :post
                      :format ajax/json-request
                      :response-format ajax/json-response
                      :on-success [:customer/addresses-success]
                      :on-failure [:customer/addresses-failure]})
This is my map. I would like to, for each element that i have in another map do a update-in for add :param with the value and then add it on a vector. This is my attempt to do it:
(defn request-vector [params request-body]
  (reduce (conj [] (update-in request-body :params assoc params) params)))
But i'm getting a error: java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Keyword What you guys think it would be the best approach do achieve this goal?

delaguardo09:01:30

check arguments for update-in , the path where you want to apply update function must be sequence. (update-in request-body [:params] assoc params) or use update instead of update-in

Ramon Rios10:01:50

Thank you for the insight!

Ramon Rios10:01:59

I got almost there

Ramon Rios10:01:24

i did a (reduce-kv    `(fn [r k v]`      `(conj r (update request-body :params v)))`    `[]`    `params)`

Ramon Rios10:01:34

But the v is null

didibus10:01:30

Is params a map?

Ramon Rios10:01:38

params {:main {:street "name" :customer ..uuid..} :facturation {:street "name" :customer ..uuid..}}

didibus10:01:42

Maybe show an example of the map and then the resulting vector you'd want. I'm not sure I fully follow what you're trying to achieve

didibus10:01:32

But in any case, v should not be nil, unless you have a nil value in your map

Ramon Rios10:01:52

I println v and it shows a value

didibus10:01:55

Right, the problem is your update

didibus10:01:13

Update takes a map, then a key and finally a function

didibus10:01:19

But your v is not a function I assume

didibus10:01:40

Try using assoc instead of update

Ramon Rios10:01:56

Thanks friend

T11:01:22

Hello! Starting to learn clojure and programming in general. Total beginner. I have a question. I have a nested vector: [something [a 1]] What I want from it: a new key + value based on this. so: :someting/a 1 How can I do this?

yuhan11:01:39

You can construct qualified keywords by calling keyword with 2 arguments, does that help?

yuhan11:01:16

Is the something in the original form a symbol?

Hi12:01:41

hello, how to substitute newlines in string with literally "\\n"?

lispyclouds13:01:27

do you mean like this? (clojure.string/replace "a\nx" "\n" "\\n")

Hi13:01:45

yes, exactly this, thank you!

Oz13:01:52

Hi, I'm just starting with reagent, following this tutorial https://dhruvp.github.io/2015/02/23/mailchimip-clojure/ doing the input field example, I modified it to a number input, so I can input hours/minutes, and then wanted to pass :min and :max values. Not sure what to do, I've added to the input-element component an argument called &attrs , then passed a hash-map {:min 0 :max 24} and merged it with the rest of the attributes. Which seems to work, but I still fear I might have sinned and there is a more preferred way to do it. what do you think?

(defn input-element
  "An input element which updates its value on change"
  [id name type value attrs]
  [:input (merge {:id        id
                  :name      name
                  :class     "form-control"
                  :type      type
                  :required  ""
                  :value     @value
                  :on-change #(reset! value (-> % .-target .-value))} attrs)])
;; TODO pass min and max values into input-element.
(defn hours-input [hours-atom]
  [input-element "hours" "hours" "number" hours-atom {:min 0 :max "23"}]
  )

aisamu13:01:57

Some people like to put all the props on a map (not just max and min on your example). The &attrs also threw me off a bit, since it resembles a typo on the rest format (`& attrs`)

👍 4
Oz13:01:06

The "&" was my very silly voodoo/cargo-cult concept of the rest format, now read about it and realized my mistake, so I'll edit it away from the snippet 🙂

jhughes15:01:53

Hi, question about embedding Metabase in a clojure app: the metabase docs say “Insert this code snippet in your server code to generate the signed embedding URL” where is server code located in a Clojure/CLJS app?

Dennis Tel15:01:07

Hi! I’m just getting started with clojure and one thing I find a bit confusing is which tool I should use for dependencies. Some places recommend Leiningen (which is build on top of Maven?) but others recommend just using deps. Which one is the recommended option? I’m looking for something lightweight that can generate a simple project setup with a test runner, do some builds etc

nate17:01:12

I echo everything that was said in the channel (good idea to use lein to start). If you'd like more commentary on the subject, we did a podcast episode about it: https://clojuredesign.club/episode/040-should-i-use-lein-boot-or-tools-deps/

athomasoriginal00:01:04

If you have not read this https://corfield.org/blog/2018/04/18/all-the-paths/ I would take a look to familiarize yourself with the options available. As for what your looking for, my recommendation to each one is: > generate a simple project setup https://github.com/seancorfield/clj-new > builds + dep management clj + deps.edn > test runner Clojure has a light weight testing library. I recommend to use that. https://clojure.github.io/clojure/clojure.test-api.html

seancorfield15:01:58

Leiningen is the "easiest", CLI/`deps.edn` is the simplest. Pragmatically, use whichever tool the book or tutorial you are learning from is using.

Dennis Tel15:01:02

Thanks for the advice!

Dennis Tel15:01:10

Leiningen it is then 😄

seancorfield15:01:03

For background @dennistel90 I started with Leiningen back in 2010 because it was the only option. At work we switched from Lein to Boot in 2015 and then to CLI / deps.edn in 2018. These days I only use the CLI / deps.edn tooling.

Dennis Tel15:01:36

@seancorfield what is the motivation (from your team, but also the community) to move from leiningen -> deps + cli?

seancorfield17:01:03

I blogged about our switch from Leiningen to Boot https://corfield.org/blog/categories/boot/ but I didn't write up why we changed to CLI/`deps.edn` (I will, eventually).

seancorfield17:01:25

I also blogged a comparison of Leiningen, Boot, and CLI for aspects of running code and building artifacts etc https://corfield.org/blog/2018/04/18/all-the-paths/

Dennis Tel15:01:12

oh yeah thats definitely the plan for learning

Dennis Tel15:01:40

asking more to get an idea of what is going on and where to possibly move to after learning clojure

didibus19:01:42

One thing that's good to learn after learning Clojure with lein. Is using Clojure without a dependency manager and build tool at all. Learn how to manually download jar and sources, and build up a classpath, and call clj with it manually, and similarly start a repl with clojure.main directly and learn to use the Clojure load and compile functions

didibus19:01:56

Once you know that, you'll understand what all these tools do under the hood, and it'll be easier for you to pick up and use any of them

Dennis Tel15:01:28

Slightly related to tooling, how hard/easy is it to write a clojure library that works on JVM but also in Clojurescript?

deleted15:01:38

but that's more for the deps.edn that lives in your home directory

Dennis Tel15:01:35

Don’t have one yet 😄 is this similar to a global .npmrc/.bashrc or something?

seancorfield18:01:29

Yes, the CLI/`deps.edn` tooling uses three deps.edn files: the "system" installed version (from the CLI -- technically it's baked into tools.deps.alpha now rather than a physical file on disk), the "user" version (in ~/.clojure/deps.edn -- a default version appears after installing the CLI but you can customize it as you wish), and then the "project" version (in the current directory).

Dennis Tel15:01:02

Yeah , it being a hosted language is definitely what draws me to it since I plan to use it on node/JVM and possible the BEAM VM(clojerl?) as well

didibus19:01:49

If you're looking for doing ClojureScript as well, shadow-cljs is a great build tool for it. Probably the easiest

Dennis Tel15:01:25

@deleted-user Thank you so much for the info and suggestions! now lets learn some Clojure! 💪

grounded_sage15:01:28

How do people redefine a variable or swap out some kind of reference with a large data structure. Using an atom and reset! Is too slow. I also tried using a volatile! Then vreset! But it’s still way too slow. At the moment I have (def my-def val-1) and (def my-def val-2) inside a comment block so I can use the repl to redefine it. I’m doing this because I have a lot of other functions I am using at the repl to inspect the data structures. I only want to change the reference once.

grounded_sage16:01:56

Setting the atom or the def is fine for the first time. I can reset it to a string or something small. But when I reset it to the alternate data structure the repl simply hangs.

didibus19:01:55

Yeah, the repl hangs because it is trying to print the data structure

grounded_sage16:01:06

Yea I was trying to avoid binding because all the snippets I have would need to go inside the binding. What I have for the moment works for what I need but I’m curious if there is something I was doing wrong.

dpsutton16:01:00

can you def the alternate data structure? do something like (def new-stuff big-nasty-thing-that's-slow) and then (reset! the-atom new-stuff) and see if that is slow?

dpsutton16:01:04

also, are you printing the big nasty thing? maybe try (do (reset! the-atom new-stuff) nil) so that your repl isn't choking on printing a big gnarly structure

grounded_sage17:01:39

@dpsutton aha! That’s it when you reset the atom in the repl it prints out the new result. The (do ... nil) was what I was looking for. Thanks!

🎉 4
🎊 4
yuhan17:01:39

It's also a good idea to set *print-length* and *print-level* if you're working with large / infinite data structures in the REPL :)

Patrick19:01:53

hi i have a short question regarding clojure.spec. how would you spec lets say a person who can have children (which are also persons)?

Patrick19:01:23

The datastructure would look something like

{:name "person1" :children [{:name "child1"} {:name "child2"}]}

Patrick19:01:24

when trying to register a spec child like (s/def ::children ::person) it doesn't work, 'cause person is not defined yet (which makes sense)

hiredman19:01:50

define person first, then children

hiredman19:01:29

(it will work because the spec combinators won't immediately try to resolve ::children)

hiredman19:01:46

user=> (s/def ::bar (s/coll-of ::foo))
:user/bar
user=> (s/def ::foo (s/coll-of ::bar))
:user/foo
user=>

👌 4
Patrick19:01:07

oh thank you ^^ i never tried it the other way around ^^

Ludwig20:01:37

Hi Everyone, when we read a future via deference , do it add overhead of passing the data back to the reader thread?

andy.fingerhut20:01:42

For Clojure/Java, the value passed back is just a pointer, i.e. a Java reference. It will not make a "deep copy" or anything like that. Pretty much nothing in Clojure makes a deep copy of anything, unless you do it yourself.

👍 4
andy.fingerhut20:01:01

Safely passing objects between Java threads requires proper synchronization, which should be implemented for you already for all immutable Clojure values, including collections. For other Java objects, it depends on their implementation.

walterl21:01:45

I'm using a Ring request's :body's .hasContent (https://www.eclipse.org/jetty/javadoc/current/org/eclipse/jetty/server/HttpInput.html#hasContent() ) in a handler, but ring-mock uses ByteArrayInputStreams for mock requests' bodies, which causes tests to fail with an error that .hasContent doesn't exist. What is the "Right Way" to hack in a .hasContent to specific ByteArrayInputStream instances? I've looked at reify, extend and related, but haven't found anything that works.

hiredman21:01:09

the ring spec says it is an inputstream, which doesn't have that method, you are looking at a particular implementation of the ring spec, that happens to use a subclass of inputstream which has that method

hiredman21:01:58

using that method means you are not using the ring spec, which is why it will break when you use things like ring-mock and that stick to the spec

walterl21:01:17

Ah, right. I keep forgetting that Ring is a spec 🙂

walterl21:01:18

Hmmm... Maybe I need something with "lower level" support then. Thanks @hiredman.

dharrigan22:01:02

Is it possible to defmacro that would be available in all namespaces (of the project?)

noisesmith22:01:56

any macro must be in a namespace, all namespaces are potentially available

dharrigan22:01:43

yes, but that would mean that I would have to import the namespace (with the macro) into each other namespace. I get that (and I understood that would be the case), but I was wondering that if I define a macro, can it be treated like one of the special forms, like when-let where it's in core and available everywhere, perhaps by defining my own namespace that would be available everywhere too - rather than importing...

dharrigan22:01:16

I could mitigate this by importing the namespace and using a small alias, like m so I could do (m/my-funky-macro ....)

didibus06:01:09

Is this for your REPL?

didibus06:01:32

Because in non REPL contexts, you want to make sure that you require or use the dependent namespaces always. Using a fully qualified function from another namespace if it wasn't loaded, thus you want to be sure you do by explicitly declaring a require or use on them.

didibus06:01:36

In REPL contexts, I declare all these fns and macros I want everywhere in the user.clj file. That file is auto-required by the REPL, and so you can start using functions from it directly without requiring it.

didibus06:01:57

But, if you mean to say only within a given namespace that you explicitly required, you can use :refer :all or use to be able to call the functions or macros from the required namespace with just their name like when-let

didibus06:01:56

That said, its not good practice when not in a REPL context, since it makes it hard for people to track where the fn or macro originates from.

dharrigan08:01:15

No, not for the REPL, for the project - but thank you for helping out too! It's all good and based upon the excellent advice here and above, I can move forward with a design decision 🙂

didibus17:01:11

If its for project code, you definitely want to have a require and use an alias.

hiredman22:01:45

when-let is not a special form

hiredman22:01:57

it is a macro in the clojure.core namespace

dharrigan22:01:58

I meant macro 🙂

dharrigan22:01:14

I am wearing my n00b hat 100% 🙂

hiredman22:01:59

the ns macro just ends up making all of clojure.core available unqualified by default, if you create your ns some other way it won't be

ghadi22:01:38

this is the way to go

dharrigan22:01:39

Linking to my own suggestion - I like it 🙂

dharrigan22:01:10

Thanks everyone - appreciate it.

andy.fingerhut23:01:20

I mean, if you want, you can define new functions and/or macros in clojure.core, but I wouldn't recommend it.

didibus06:01:32

Actually don't think you can

didibus06:01:40

Because clojure.core is AOT and direct linked

andy.fingerhut07:01:24

$ clj
Clojure 1.10.1
user=> (doc foo)
nil
user=> (foo 7)
Syntax error compiling at (REPL:1:1).
Unable to resolve symbol: foo in this context
user=> (in-ns 'clojure.core)
#object[clojure.lang.Namespace 0x2e61d218 "clojure.core"]
clojure.core=> (defn foo "foo in clojure.core" [x] (+ x 5))
#'clojure.core/foo
clojure.core=> (in-ns 'user)
#object[clojure.lang.Namespace 0x235a0c16 "user"]
user=> (ns bar)
nil
bar=> (doc foo)
Syntax error compiling at (REPL:1:1).
Unable to resolve symbol: doc in this context
bar=> (clojure.repl/doc foo)
-------------------------
clojure.core/foo
([x])
  foo in clojure.core
nil
bar=> (foo 7)
12

andy.fingerhut07:01:47

For most purposes, it is just another namespace.

didibus07:01:33

Try with a macro?

didibus07:01:46

Cause it didn't work for me, but I tried with a macro

andy.fingerhut07:01:08

Note that I created a new namespace bar to test it in. foo wasn't visible in user namespace for some reason, perhaps because it did the refer-clojure operation before I defn'd foo. Not sure exactly what is happening there.

andy.fingerhut07:01:20

Yep, new defmacro created in clojure.core was visible in ns bar also

didibus17:01:02

Ah, I was just moving back to user, and you're right, you need to force refer again, or call ns again to have it refer the new clojure.core vars

noisesmith23:01:40

there's a library that uses some weird hack to make a . namespace, so your macro could be ./my-funky-macro - but this is meant more as a dev time convenience

noisesmith23:01:59

having namespaces that let a human reader know where something came from is actually a very nice feature

dpsutton23:01:27

annoying trying to read scheme, common lisp, and emacs lisp for that reason

💯 4
noisesmith23:01:14

I even get annoyed by the way java code examples always leave out the import clause / full package name, with the assumption you have a tool that inserts the import for you

☝️ 4
noisesmith23:01:46

language conventions that avoid needing tooling > tooling

dpsutton23:01:52

yes! examples that import package.* are incredibly annoying

didibus06:01:02

Oh man, and I hate static imports, I guess similar to how I hate use or refer all, though I understand its value at the repl

andy.fingerhut23:01:38

Single-letter or otherwise short aliases seem far preferable, if you can stand to type them 🙂

noisesmith23:01:03

right, and explicit aliases to a namespace with a meaningful name in the ns block

Ludwig23:01:39

hi, why my repl prints less info when there is an exception? I'm using this library https://github.com/ptaoussanis/truss , calling the sample function

(defn square [n]
  (let [n (have integer? n)] ; <- A Truss assertion [1]
    (* n n)))

(square nil)
it should print something like
;; Invariant violation in `taoensso.truss.examples:10`.
;; Test form `(integer? n)` with failing input: `<nil>`
;; {:*?data* nil,
;;  :elidable? true,
;;  :dt #inst "2016-10-14T10:01:28.671-00:00",
;;  :val nil,
;;  :ns-str "taoensso.truss.examples",
;;  :val-type nil,
;;  :?err nil,
;;  :*assert* true,
;;  :?data nil,
;;  :?line 10,
;;  :form-str "(integer? n)"} 
but I only get
Syntax error (ExceptionInfo) compiling at (src/clj/sample/core.clj:200:1).
Invariant violation in `pim.product.core:198`. Test form `(integer? n)` failed against input val `<truss/nil>`. 
it happens both at Intellij with cursive and launching lein repl from terminal. I'm using the latest clojure version

noisesmith23:01:29

@vachichng if you evaluate *e , that shows the full printed form of the most recent exception

noisesmith23:01:14

this is described in the repl startup splash, alongside *1 etc. for most repls

Ludwig23:01:55

@noisesmith oh thanks, it worked! how can I set that as default?

noisesmith23:01:42

that's set by the repl itself, I don't know if there' s a universal answer

noisesmith23:01:53

are you using nrepl, clj, socket repl?

noisesmith23:01:34

where you might just want pst (which is in clojure.core) without the clj-stacktrace dep

noisesmith23:01:39

I wonder if there's a more generic answer that would work with clojure.main / socket repls - maybe starting a new repl with a different error handling function is possible? sadly these days calling pr on an exception-info object has better info than pst

Alex Miller (Clojure team)05:01:28

You can use any accept function with the socket repl - the provided repl is just one convenient answer

👍 4
Ludwig23:01:03

@noisesmith tried both ways, as you said, and from the github comment, but still getting the same results, no stacktrace printed

seancorfield23:01:40

user=> (clojure.main/repl :caught pst)
user=> (/ 1 0)
ArithmeticException Divide by zero
	clojure.lang.Numbers.divide (Numbers.java:188)
	clojure.lang.Numbers.divide (Numbers.java:3901)
	user/eval144 (NO_SOURCE_FILE:1)
	user/eval144 (NO_SOURCE_FILE:1)
	clojure.lang.Compiler.eval (Compiler.java:7177)
	clojure.lang.Compiler.eval (Compiler.java:7132)
	clojure.core/eval (core.clj:3214)
	clojure.core/eval (core.clj:3210)
	clojure.main/repl/read-eval-print--9086/fn--9089 (main.clj:437)
	clojure.main/repl/read-eval-print--9086 (main.clj:437)
	clojure.main/repl/fn--9095 (main.clj:458)
	clojure.main/repl (main.clj:458)
(starting a repl inside a repl)

noisesmith23:01:52

ahh! that's the option I was missing

noisesmith23:01:08

(cmd)user=> (clojure.main/repl :caught pr)
(cmd)user=> (throw (ex-info "foo" {:a 0}))
#error {
 :cause "foo"
 :data {:a 0}
 :via
 [{:type clojure.lang.ExceptionInfo
   :message "foo"
   :data {:a 0}
   :at [user$eval9 invokeStatic "NO_SOURCE_FILE" 1]}]
 :trace
 [[user$eval9 invokeStatic "NO_SOURCE_FILE" 1]
  [user$eval9 invoke "NO_SOURCE_FILE" 1]
  [clojure.lang.Compiler eval "Compiler.java" 7177]
  [clojure.lang.Compiler eval "Compiler.java" 7132]
  [clojure.core$eval invokeStatic "core.clj" 3214]

noisesmith23:01:16

pr is so much better than pst now

seancorfield23:01:08

A built-in default that trimmed :trace would be nice tho'...

noisesmith23:01:24

@vachichng so if the right :caught is set you can get better printing behavior, and you can start a new subrepl and supply any one-arg-function you like there

Ludwig00:01:57

yeah, it worked on the terminal launched repl, but still not working under Intellij cursive nrepl

noisesmith00:01:38

right - it only takes effect in the repl that clojure.main starts, it doesn't change any behavior of the parent

noisesmith00:01:16

I wouldn't be surprised if there was an nrepl middleware out there somewhere that improves trace printing as well

Ludwig00:01:51

yeah, there is one for cider-nrepl

didibus06:01:57

I havn't used Cursive in a while, but I think it has a button to show the full exception

didibus06:01:02

Something like: Print last exception

👍 4
didibus06:01:39

And you can bind a shortcut for it I think

Ludwig00:01:30

yeah, you are right, there is an action for that, many thanks for the tip!

seancorfield23:01:25

There's Throwable->map if you want to turn exceptions into data and manipulate them.

👍 4
noisesmith23:01:49

that's what pr/ prn use under the hood for exceptions now right?