Fork me on GitHub
#beginners
<
2020-04-11
>
Lukas09:04:17

Hi, could anyone explain how to import methods defined with defprotocol in a seperate namespace?

hindol09:04:28

Each method from the protocol will be available independently. Just require the namespace where the protocol is defined.

Lukas09:04:24

ty meaning

(ns storage)

(defprotocol Storage
   (write-entity [storage entity]))

(ns core
  (:require [storage]))
this will work?

Lukas09:04:18

because when I evaluate an expr, with the repl, it says Unable to resolve symbol: write-entity in this context

hindol09:04:15

You have to call like this: (storage/write-entity <anything that implements the protocol>).

hindol09:04:06

So, the protocol name Storage is used while implementing a protocol. It is not used otherwise.

Lukas09:04:07

could i also use :refer?

hindol09:04:44

Yes, of course. The normal rules of referring to something applies.

Lukas09:04:01

great thanks a lot

hindol13:04:16

I should add, :refer is usually frowned upon. Better just name the ns something using :as.

noisesmith18:04:30

it sounds like you understand this now @UP2FUP0TT but another way to describe it is that the protocol methods don't belong to the object, they belong to the protocol itself

Jim Newton13:04:03

what is the equivalent of prog1 in clojure. I.e., a function or special form which evaluates all its arguments and returns the value of the first one?

Jim Newton13:04:48

it and prog2 and progn allow you to wrap debugging print statements around expressions without effecting the return values.

hindol13:04:20

@jimka.issy There are several examples of spy macros scattered around the web, which will print without obstructing program flow.

andy.fingerhut13:04:44

I am not aware of a built-in prog1 in Clojure. One could write it as a macro fairly easily if you really wanted it, e.g. using something like (let [tmp first-form] form2 ... formn tmp)

andy.fingerhut13:04:10

But for debugging in particular, yes there are other more commonly used mechanisms used in Clojure than by implementing prog1

Jim Newton13:04:36

I've written them, no need for a macro.

(defn cl-prog1 [val & _]
  val)

(defn cl-prog2 [_ val & _]
  val)

(defn cl-progn [& others]
  (last others))

Jim Newton13:04:31

w.r.t., debugging, Can I trace a function, and expect the system to tell me when it is called and with which values, and when it returns and with which value?

andy.fingerhut13:04:26

There is a library tools.trace that can do something like that, and some others: https://github.com/clojure/tools.trace

Jim Newton13:04:49

does anyone use cl-format ? I sometimes have the problem using it that it confuses the order of the output. but perhaps it is cider who is confusing the order of output.

andy.fingerhut13:04:16

tools.trace works by replacing the value of the Clojure Var whose value is the function that you want to trace, which works for many common ways of calling a function, but it fails for a few cases, e.g. when Clojure code is compiled with direct linking enabled (as the core of Clojure is by default, but it is off by default when compiling user Clojure code), and for some functions declared via defprotocol.

andy.fingerhut13:04:52

I believe that cl-format is not a commonly used function among Clojure users. Not avoided by everyone, but not often recommended, either.

andy.fingerhut13:04:29

If you suspect problems in the implementation, it can be a good idea to double-check the behavior in a REPL environment that involves as little 'extra stuff' like CIDER as possible. There is a #cider channel if you are curious to ask about potential CIDER-specific issues.

Jim Newton13:04:32

good advise, but the place I see it swapping output is when HUGE amounts of text is printed. My suspicion is that cl-format has an internal string buffer, which gets flushed to stdout occasionally, and this conflicts with other printf/println code. OR that printing too much at a time overflows some of cider's wire protocols.

Jim Newton13:04:37

just a hunch

andy.fingerhut13:04:58

If your code is single-threaded, I would suspect CIDER first, but that is only a guess on my part.

andy.fingerhut13:04:34

I do not know if I have found any REPL environment that behaves nicely all the time when huge output is involved, but I tend to try to avoid that issue via writing to files instead of *out* when I encounter it. Perhaps others have different experiences or other recommendations there.

Jim Newton13:04:51

I see here https://clojuredocs.org/clojure.core/time a clojure function to print the elapsed time. Is that the best way to get the time programmatically? i.e., to redirect this to a string and parse the string?

andy.fingerhut13:04:57

That is often used for quick performance tests in a REPL, with output read by a human.

andy.fingerhut13:04:03

It is for extremely quick-and-dirty benchmarking, with the warning that JVM's typically have JIT enabled, so run times can vary widely until and unless code becomes JIT'd. A benchmarking library like criterium will run your code many many times, attempting to ensure that JIT kick in before it starts taking measurements that it will report.

andy.fingerhut13:04:31

You can look at the source of clojure.core/time to see how it gets the current wall clock time, which is a simple Java API call.

Jim Newton13:04:08

In scala I sometimes use a java library called System.nanoTime() which returns a number of nanoseconds since perhaps jan 1 1970. How could I call this java function in clojure?

jumar18:04:03

It almost certainly doesn’t return nanos from epoch start. The”origin” can be arbitrary; most likely the time of the last boot.

jumar18:04:45

The spec even allows nanoTime to return negative values!

andy.fingerhut13:04:40

If you want current time without any need to handle time zones, dates, etc. then that Java API call is suitable for many purposes. I hear the java.time package is pretty good when you want to deal with all the timezone and conversion fun things.

Jim Newton13:04:56

voila, the answer is in the src code for time. as andy suggested.

Jim Newton13:04:04

(defmacro time
  "Evaluates expr and prints the time it took.  Returns the value of
 expr."
  {:added "1.0"}
  [expr]
  `(let [start# (. System (nanoTime))
         ret# ~expr]
     (prn (str "Elapsed time: " (/ (double (- (. System (nanoTime)) start#)) 1000000.0) " msecs"))
     ret#))

Jim Newton13:04:45

Scala System.nanoTime() --> clojure (. System (nanoTime))

andy.fingerhut13:04:51

(source time) in a REPL is your friend, for that and a whole bunch of other code. Well, at least for nice little things like time . I wouldn't recommend trying to understand all of the details of more complex things all of the time, but this one is quite small.

Jim Newton13:04:39

I didn't know about source but in cider I can press meta-. on a name do jump to its source code.

Jim Newton13:04:01

clojure-rte.core> (. System (nanoTime))
2573822059095163
clojure-rte.core> 

Bill Phillips14:04:16

I’d usually write (System/nanoTime), but yeah

Jim Newton15:04:22

@jings.bill that's even better (System/nanoTime) so why does the time implementation use the bizare syntax? is System/nanoTime a new syntax that didn't exist when time was first implemented in core.clj ? or is it perhaps a bootstrapping issue, in that at the time core.clj is compiled, the System namespace does not yet exist?

Bill Phillips15:04:19

who knows? (System/nanoTime) does expand to (. System (nanoTime)), though. it’s syntax sugar

Jim Newton15:04:03

That's strange, I thought System/nanoTime was a namespace designation of a symbol.

nick15:04:31

Coming from a different background what confused me a bit is how dependencies/libs are loaded each ns/namespace requires them and make available via :as alias. Even if in a given namespace a function from this required library/file is used only once. I had the impression that it's like an introduction of an unnecessary level of indirection when you could just use direct call.  For example: (ns bigco.data   `(:require [http://clojure.java.io :as io]))` (.mkdirs (io/as-file "/tmp/quux")) vs (ns bigco.data) (.mkdirs ( "/tmp/quux")) Then I found this message in https://github.com/clojure/tools.namespace >Warnings and Potential Problems >Fully-qualified names: Be careful when using fully-qualified symbol names without namespace aliases (require with no :as). If the namespace happens to be loaded already, it will not necessarily cause an error if you forget to require it, but the dependency graph of namespaces will be incorrect. Is it the reason why :as is used so commonly, even if it is just once in a current namespace? For easier reloading via clojure.tools in repl?

Ben Sless16:04:20

Don't you just have to require the ns? I don't think aliasing it is mandatory for it to be loaded

Timur Latypoff19:04:51

I guess @UK0810AQ2 means that you can just do:

(ns bigco.data
  (:require []))
(.mkdirs ( "/tmp/quux"))
— without the :as part, and thus without assigning a shortcut. Also, if my understanding is correct, you have to require Clojure namespaces (instead of just using the single function that you need) because requiring the namespace has side-effects. It is like running a "script": before you run the "script" for the first time, top-level defs and defns are undefined, you cannot use them.

Ben Sless20:04:50

You are correct. Clojure files are loaded serially. Calling require looks for files containing the symbol argument on your class path (try requiring a ns which doesn't exist to see which files it's looking for). Then the reader reads the file one form at a time and compiles them, and adds all the defed bindings to the ns object

👍 4
nick21:04:04

@UTXR46DS6 @UK0810AQ2 thanks a lot! It makes sense now

Bill Phillips16:04:09

I haven’t thought deeply or read and grokked the Java interop syntax. But I know that it is not the same as the Clojure-y entities it appears to be the same as

Bill Phillips16:04:41

e.g.

user=> clojure.string/join
#object[clojure.string$join 0x487db668 "clojure.string$join@487db668"]
user=> System/currentTimeMillis
Syntax error compiling at (REPL:0:0).
Unable to find static field: currentTimeMillis in class java.lang.System

Bill Phillips16:04:09

similarly:

user=> clojure.string
Syntax error (ClassNotFoundException) compiling at (REPL:0:0).
clojure.string
user=> System
java.lang.System

Chris16:04:21

How would you model a graph in clojure? I’m currently using some adjacency list/map way. I was wondering how others are doing this. My current datastructure:

{:a #{:b :c}
 :b #{:c}
 :c #{:a}}

shooit16:04:44

That seems like a perfectly good place to start. Check out the clojure.zip library for some awesome funcional tooling around graph/tree traversal

💪 12
Chris16:04:33

Cool, thanks for the tip !

Aviv Kotek17:04:52

is there any way I can get the index of the sequence i'm currently reducing on? let's say I want to return a map from a sequence with each element and it neighbors of n=2 (a b c d e) => {"a" ["c"] "b" ["d"] "c" ["e" "a"] "d" [b]....} i'd like to perform a reduce on xs and merge it & cons while building new map with (nth xs idx n) and it (idx-2) is there anyway to get it without performing loop iteration? I can use map-indexed and have simple maps of each then merge them, but curious if I can do it with reduce only.

Chris17:04:24

I did not fully understand the problem, however, maybe reduce-kv solves your job? https://clojuredocs.org/clojure.core/reduce-kv

Chris17:04:52

If you pass a vector in it, then you get the index as second parameter

👍 4
Chris17:04:24

However, you seem to be using lists

Ben Sless17:04:17

You can use a windowing operation over the sequence. In our case something like partition will produce the expected behavior if you pass it step size 1

Aviv Kotek20:04:27

I would want to get the forward-n-elements and the backward-n-elements

Aviv Kotek20:04:45

so if n=2, for [a b c d e] looking at c I will get 'a' and 'e', how can I do that with partition?

Aviv Kotek20:04:37

I think reduce-kv will do the work, thanks Chris.

Ben Sless21:04:18

A possible example:

(apply merge-with concat (map (fn [[x y z]] {x [z] z [x]}) (partition 3 1 '[a b c d e])))

Ben Sless21:04:25

If performance is a consideration I'd jump into partition to avoid the intermediate collections, merge and concat

✔️ 4
Chris17:04:12

Does somebody know why the following does not work?

(contains? [#{"a" "b"} #{"c"}] #{"c"}) ; => false

Chris17:04:26

Oh I think I got it…`contains?` does not work like this for vectors

👍 4
Chris17:04:26

Oh I think I got it…`contains?` does not work like this for vectors

👍 4
Kazuki Yokoyama18:04:19

Hello, whats is more idiomatic? Say (defn f [x] {:body {:result {:id x}}} , and I want to extract id from a call to f . Which of the following is more idiomatic:

(let [{{{id :id} :result} :body} (f)] <make use of id...>)
or
(let [id (-> (f) :body :result :id)] <make use of id...>)
or
(let [id (get-in (f) [:body :result :id])] <make use of id...>)
? IMO the former uses the right tools (map destructuring) for the job, but is less readable. The second way is more compact, although less obvious in its intent. The latter, I believe is the standard way to do this, but looks verbose. I'd appreciate some opinions.

seancorfield19:04:26

I tend to use the second form because I find it more readable but that's pretty subjective. I'm sure lots of people would find the third form more readable.

phronmophobic19:04:08

I rarely(or never?) use the first form if i’m traversing multiple layers. I use both 2nd or 3rd depending on the context

Martin19:04:26

I’ve got a silly question

Martin19:04:04

I’ve got a main method and I want to block on a readline

Martin19:04:12

how do I do that?

Martin19:04:52

would this be correct:

(defn -main []
  (let [config (read-config ( "config.edn"))
        system (ig/init config)]
    (shutdown/add-hook! ::system #(ig/halt! system))
    (read-line)))

OrdoFlammae19:04:08

Try it in the REPL?

Martin20:04:12

thanks, I did… it seems to work. I guess what I’m looking for what would be the idomatic way to block the main loop?

OrdoFlammae20:04:13

I guess it depends on why you're blocking the main loop. Are you waiting for input?

Martin20:04:55

I don’t want the application to exit while running

seancorfield20:04:50

@martincharlesrichards If you're starting a web server or similar process, there should be an option that causes it to run in the main thread. Another option is to have a promise in your system and then wait on that (with a deref in -main) -- and sometimes it's useful to have some invocation that deliver's a value to it, to stop your app cleanly.

seancorfield20:04:33

This example uses Component, but a similar mechanism should work for Integrant: https://github.com/seancorfield/usermanager-example/blob/master/src/usermanager/main.clj#L146-L172 it has a "shutdown" promise.

seancorfield20:04:26

Here's where it starts the system and then waits on the promise to be delivered https://github.com/seancorfield/usermanager-example/blob/master/src/usermanager/main.clj#L210-L212

Martin20:04:33

hmmm, thank you. So you’d have some sort of running state in your system and await it being deref’d

Martin20:04:02

and making use of the JVM shutdown hook?

David Pham21:04:03

I have a pattern that often arise and I don’t know what is the idiomatic way of solving it: I am building a map, and multiple values might be nil in which case, I don’t want them included in the map. What is your preferred solution to solve it? I used cond-> but not sure if reduce might be better?

Ben Sless21:04:59

cond-> is good. There's no iteration when you use it

OrdoFlammae23:04:22

From the clojure guides https://www.clojure.org/guides/learn/syntax#_numeric_types > A trailing N can be used to force arbitrary precision. What does "arbitrary precision" mean?

seancorfield23:04:24

It can represent arbitrarily large integers.

dpsutton23:04:31

most numeric datatypes are fixed-width so if your number gets too big there's no way to write it down in that type. There are types that can grow arbitrarily large to handle this.

seancorfield23:04:51

user=> (* 10000000000 10000000000)
Execution error (ArithmeticException) at user/eval11 (REPL:1).
integer overflow
user=> (* 10000000000N 10000000000N)
100000000000000000000N
user=> (* 10000000000N 10000000000)
100000000000000000000N
user=>
note that only one operand needs to be BigInt for the other(s) to be promoted.

seancorfield23:04:26

^ @ordoflammae Does that help explain it?

OrdoFlammae23:04:39

OK, yes, thanks.

seancorfield23:04:30

There's a M suffix for real numbers that gives you BigDecimal which is arbitrary precision for non-integers (because Clojure normally uses Double)

OrdoFlammae23:04:27

I'm trying to set up a situation where x and y are consecutive items in a vector, but I can't figure out how to do it idiomatically. For example, when going over a vector [1 2 3 4 5], the first iteration should have x = 1, y = 2, the second iteration should have x = 2, y = 3, etc. I can't figure out how to do this with a map or doseq construct, is there an idiomatic way to do this?

seancorfield23:04:31

(partition-all 2 1 coll) is probably what you are looking for

OrdoFlammae23:04:10

OK, I think I can figure out how to use that.

OrdoFlammae23:04:05

Yup, that's exactly what I want. Thanks!