Fork me on GitHub
#clojure
<
2019-08-29
>
caleb.macdonaldblack00:08:00

Can redefine a def, like I can a defn function with with-redefs?

caleb.macdonaldblack00:08:50

I'm trying to write a test but I need to mock out a value

caleb.macdonaldblack01:08:32

@trailcapital Oh. I must have had a different error somewhere else when I tried that the first time. Thanks 🙂

caleb.macdonaldblack01:08:15

I didn't see the example though. I just assumed it only worked with functions

seancorfield01:08:29

Just bear in mind that some uses of def'd values are "compiled in" and won't be seen if you try to redef them...

seancorfield01:08:30

user=> (def foo 42)
#'user/foo
user=> (def bar foo)
#'user/bar
user=> (defn quux [x] (+ bar x))
#'user/quux
user=> (quux 0)
42
user=> (with-redefs [foo 13] (quux 0))
42
user=> (with-redefs [bar 13] (quux 0))
13

seancorfield01:08:23

bar is compiled to refer to the value that foo has at the time -- so redefining foo has no affect on bar's value

caleb.macdonaldblack01:08:09

Ah yeah I'm aware of that one. Had some issues in the past working with pedestal around that. I was reevaluating functions in the repl that were referenced in interceptors defined with def and trying to figure out why it wouldn't work.

dpsutton02:08:29

I was surprised to see that vectors don't implement ISeq. (seq? [:a :b]) is false. Can anyone shed some light for me?

seancorfield03:08:28

https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/ISeq.java -- vectors don't support those operations, they need to be coerced to seqs

Alex Miller (Clojure team)03:08:12

seq is the operation that yields the seq view of a collection (which often is not itself a logical list)

Alex Miller (Clojure team)03:08:02

things that can be seq-ed are seqable?

dpsutton03:08:08

@seancorfield I looked at that briefly but I think i got tunnel vision on determining what can have first called on it and figured seq? fit that bill and vectors satisfied that. But I forgot about next and more

dpsutton03:08:45

thanks @alexmiller. My gut still tells me that vectors are certainly logical lists. (I don't mean this argumentatively, i mean i'm trying to fix my thinking/reasoning)

dpsutton03:08:22

But i'm reading the http://clojure.org stuff again trying to internalize this so I develop that intuition. But it sounds like seqable? would guarantee that first will succeed

dpsutton03:08:07

and thanks both of you for answers so late 🙂

Alex Miller (Clojure team)03:08:39

vectors can provide a logical list view (an implementation of the seq interface)

Alex Miller (Clojure team)03:08:49

but they do not directly implement the seq interface

Alex Miller (Clojure team)03:08:16

seqable? guarantees that first will succeed after you call seq on it

seancorfield03:08:16

(seq [:a :b]) -> (:a :b) and (seq? (seq [:a :b])) will be true (he says, without trying it in a REPL!)

Alex Miller (Clojure team)03:08:10

you will note that first does the seq call for you

Alex Miller (Clojure team)03:08:21

user=> (doc first)
-------------------------
clojure.core/first
([coll])
  Returns the first item in the collection. Calls seq on its
    argument. If coll is nil, returns nil.

Alex Miller (Clojure team)03:08:30

calls seq on its argument

dpsutton03:08:07

well that makes it dead simple doesn't it 🙂 Thanks again

Alex Miller (Clojure team)03:08:18

most of the seq-oriented functions do this

seancorfield03:08:40

Note also

user=> (seq? #{:a :b})
false
user=> (seq? {:a 1 :b 2})
false

seancorfield03:08:37

But both are seqable? and when you call seq on them, you get things that are seq? (`ISeq`)

dpsutton03:08:58

yeah i was looking at seq? first. I think since lots of things call seq for me transparently i don't often have to worry about seq/seqable and that's why when it matters i a little mushy

dpsutton03:08:08

but of course, the doc strings

yuhan04:08:17

Does there exist a macro like if-let or when-let that works with destructured bindings?

yuhan04:08:44

I suppose I could roll my own but it doesn't seem to make sense for (if-let [[x y z] val]) to always evaluate to the true branch no matter what the val is

Alex Miller (Clojure team)04:08:46

both of those can use destructuring

Alex Miller (Clojure team)04:08:47

if you want something other than what they do, then you would need to roll your own

Alex Miller (Clojure team)04:08:34

if-let / when-let will respect depend on whether val is logically true or not

Alex Miller (Clojure team)04:08:28

user=> (if-let [[a b c] nil] "t" "f")
"f"
user=> (if-let [[a b c] [1 2 3]] "t" "f")
"t"

yuhan04:08:32

aha, that makes a lot more sense now

yuhan04:08:53

somehow I always thought of it in terms of the truthiness of the left side of the bindings

Alex Miller (Clojure team)04:08:19

it's really x in (if-let [[a b c :as x] val] ...)

yuhan04:08:05

(defmacro if-let*
  "Like `clojure.core/if-let`, but only evaluates to `then` if all bound vars
  are truthy, including destructured ones"
  ([bindings then]
   `(if-let* ~bindings ~then nil))
  ([bindings then else]
   (let [bindings* (destructure bindings)]
     `(let* ~bindings*
        (if (and ~@(->> bindings* (partition 2) (map first) (distinct)))
          ~then ~else)))))

(if-let* [{:keys [a b]} {:a 10}]
  (+ a b) :error)
;; => :error

yuhan04:08:36

I'm amazed all the time how easily Clojure lets you invent your own syntax 🙂

pinkfrog07:08:25

is stm commonly used, or it’s just an overkill?

pinkfrog07:08:37

when compared to atom.

andy.fingerhut07:08:51

I think it is used fairly seldom.

👍 4
dominicm08:08:27

You might use it more often in a desktop application or database or something like that. But a lot of modern applications can be written without state.

keymone08:08:56

is this thing up to date: https://clojure.github.io/tools.emitter.jvm/ ? is there some other built-in way to grab compiled bytecode?

andy.fingerhut09:08:44

what do you want to do with compiled bytecode? Disassemble it and print out the disassembly listing, or decompile it back to approximately equivalent Java source code? clj-java-decompiler can do either of those things: https://github.com/clojure-goes-fast/clj-java-decompiler

Leon09:08:20

whats the easiest way to get expensive map-operations (that rely on external stuff like web-requests) to execute in paralell?

erwinrooijakkers10:08:06

pmap depends on number of cores and does not lead to optimal performance gains. Can you map over promises? E.g.,

(require '[org.httpkit.client :as http])

(defn- fetch [urls]
  (let [promises (doall (map http/get urls))
        responses (doall (map deref promises))]
    responses))

Leon10:08:44

if that's the prefferable option for asynchronous stuff, i'll look into it;D (verrry new to clojure)

Leon11:08:42

i guess in your example http/get already works using promises,... are there any recommended resources on how to work with promises and such?

vlaaad09:08:48

little p before map

keymone09:08:13

@andy.fingerhut i want to ship it over the network into different vm runtime as part of deployment process

jumar10:08:11

Do you just want to AOT compile your app?

keymone10:08:33

only a subset of functions, ship them over to another app and patch relevant namespaces. it's still very much proof of concept thing

jumar18:08:32

compile doesn't help?

keymone20:08:58

maybe it does, i havent tried yet

yuhan11:08:31

I'm trying to use sorted-set-by to represent a priority queue, but ran into the case where trying to conj an element with the same "priority" results in a no-op:

(let [cmp (fn [x y] (compare (:cost x) (:cost y)))
      s   (sorted-set-by cmp {:cost 0 :color "red"})]
  (conj s {:cost 0 :color "green"}))
;; => #{{:cost 0, :color "red"}}

yuhan11:08:57

is this the wrong data structure to be using? I thought of some workarounds like checking for 0 in the comparator and returning 1 instead, but that feels quite hacky

yuhan11:08:40

hmm, hash-maps don't implement IComparable though, is it a good idea to compare their hashes as a fallback?

yuhan11:08:22

Thanks! that looks interesting, although > maps are compared by their entries in sorted order of their keys. this seems like it would be expensive when I'm dealing with large heavily-nested maps

vlaaad11:08:34

yeah, but it seems that you need this kind of comparator to be able to use sorted-set-by

yuhan11:08:23

According to the clojuredocs the comparator just needs to be consistent? My problem domain is partially ordered with relation to cost, so essentially I just need a fast artificial way of imposing a total ordering

yuhan12:08:29

(assuming hash is fast, I haven't actually run any benchmarks)

andy.fingerhut17:08:26

I am catching up late here, and someone else may have already suggested this to you, but there is a data.priority-map data structure library that may do exactly what you want: https://github.com/clojure/data.priority-map

pinkfrog13:08:00

do protocol and multimethod bring too much complication because to understand what to call, the user have to grok over all the codebase.

pinkfrog13:08:24

this is because the definition of protocol and (multi)methods can be dynamically added.

pinkfrog13:08:33

instead of appearing solely in the class definition.

dominicm13:08:14

The a la carte nature is what makes them simple. It does make searching for them harder though.

Alex Miller (Clojure team)13:08:03

why do you have to search at all?

Alex Miller (Clojure team)13:08:17

as a consumer, you just subscribe to the abstraction

dominicm13:08:55

My experience is that often in codebases, the abstraction is poorly documented so the implementations are the documentation. There's also the cases where you make a breaking change to the protocol, because it's internal, and then you need to search for them then too.

Alex Miller (Clojure team)13:08:25

these seem like problems of your own making :)

👍 8
Alex Miller (Clojure team)13:08:43

in general, we have found that it is best to never have someone call directly into your protocol as an API, but to instead front it with a method that gives you a point of modification on top

Alex Miller (Clojure team)13:08:42

so like in spec, there is a Spec protocol with methods like conform*, but users don't call that, they call conform function, which calls conform* on your behalf (and has its own custom logic to deal with stuff, like resolving spec name to spec object)

Alex Miller (Clojure team)13:08:42

in practice, we've found this greatly improves your ability to create good abstractions that survive over time

dominicm13:08:50

More specifically, they are problems someone else creates for me to clean up / live with!

Alex Miller (Clojure team)13:08:24

well, I mean "you" in the general sense (could also be "you" of 6 months ago :)

dominicm13:08:19

I've seen you mention this before, and beyond having additional logic in the fronting function (only call the underlying protocol on Tuesdays, or reverse the arguments) I wasn't sure of what the general case was there. The fronting function wouldn't help with understanding the abstraction though would it? What sort of things do you find it useful to do in that fronting function?

vlaaad13:08:43

add a docstring 😄

mpenet13:08:54

you can spec the fronting fn for one 🙂

Alex Miller (Clojure team)14:08:24

all of the above and often you'll find there is some special case or transformation that applies to all cases

plexus14:08:26

anyone seen this behaviour where (seq (.getURLs (.getContextClassLoader (Thread/currentThread)))) is empty? this is when eval'ing inside nrepl/cider on a new clojure-cli based project

plexus14:08:06

it's pretty annoying because it means io/resource sees an empty classpath and so won't find any resources

dpsutton14:08:36

i did it in our work project nrepl/cider based project and also in just clj and both came up nil

dpsutton14:08:44

also in core.match with clj and also responds with nil

dominicm14:08:04

io/resource works in clojure with cider nrepl generally. io/resource doesn't loop over each url. Is (io/resource "clojure/core.clj") returning nil for you?

plexus14:08:45

no, you're right, it does find "clojure/core.clj"

plexus14:08:00

I have :paths ["src" "test" "resources"] in deps.edn, and a file resources/activities/system.edn, but (io/resource "activities/system.edn") returns nil...

dominicm14:08:20

Did the folder exist when the jvm started?

plexus14:08:37

good question... let me restart

dominicm14:08:58

(I spent hours on that once, now it's my first question)

plexus14:08:33

and that fixed it. Thanks @dominicm!

👍 4
samedhi15:08:12

Is there any way to add add "middleware" to clojure.tools.logging to change the format of what is logged when calling info, warn, debug, etc?

dominicm15:08:40

You probably want to do this at the underlying layer, e.g. In logback

erwinrooijakkers10:08:20

Add logback dependency

erwinrooijakkers10:08:40

project.clj:

;; Logging
[ch.qos.logback/logback-classic "1.2.3" :exclusions [org.slf4j/slf4j-api]]
[org.slf4j/slf4j-api "1.7.26"]
[org.slf4j/jul-to-slf4j "1.7.25"]
[org.slf4j/jcl-over-slf4j "1.7.25"]
[org.slf4j/log4j-over-slf4j "1.7.26"]
[org.clojure/tools.logging "0.4.1"]

erwinrooijakkers10:08:43

And logback.xml in your resources:

<!-- Logback configuration. See  -->
<!-- Scanning is currently turned on; This will impact performance! -->
<configuration scan="true" scanPeriod="10 seconds">
  <statusListener class="ch.qos.logback.core.status.NopStatusListener" />

  <include resource="logback-stdout-appender.xml"/>

  <root level="INFO">
    <appender-ref ref="STDOUT" />
  </root>

  <!-- For loggers in the these namespaces, log at all levels. -->
  <logger name="user" level="ALL" />

</configuration>
logback-stdout-appender.xml:
<!-- Console output -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
  <!-- encoder defaults to ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
  <encoder>
    <pattern>%d{HH:mm:ss.SSS} %-5level [%thread] %logger{} - %msg%n</pattern>
  </encoder>
</appender>
There you can change the format.

samedhi15:08:18

Thank both of you.

Mno15:08:43

would wrapping it in another function be enough?

dominicm15:08:03

Watch out, logging is macro based, so you should wrap it with macros too

λustin f(n)15:08:35

When using Transit ( https://github.com/cognitect/transit-format ), how closely does the version number need to match? I am communicating between the -clj and -cljs implementations, and the highest common release version between them is 0.8.247

Alex Miller (Clojure team)16:08:41

The transit format itself has not changed since it was released so any implementation should be working from the same spec. That said, there might be some implementation details that are not spec-related.

λustin f(n)16:08:18

So as it stands you would recommend simply using the latest version for each of them then?

λustin f(n)15:08:24

But I am getting a difference in the data after translation when using both of them on that version number

λustin f(n)15:08:59

Namely, keyword values of a nested map are turning into strings when sent from clojure -> clojurescript

walterl15:08:20

> Watch out, logging is macro based, so you should wrap it with macros too -- @dominicm @ https://clojurians.slack.com/archives/C03S1KBA2/p1567093683142300?thread_ts=1567093003.139200&amp;cid=C03S1KBA2 Why should one wrap macro usage (only?) in another macro? (I didn't want to hijack that thread with my n00b question.)

dominicm15:08:45

As a general rule, you don't need to. But you probably want some semantics of that macro. For example, info being a macro is what allows it to get the current namespace for including in the log message.

walterl16:08:29

Understood. Thanks!

souenzzo17:08:58

there is a function for (my-fn [1 2 3 4]) ;;=> {1 2, 2 3, 3 4} ?

noisesmith17:08:22

oh, I misread

👍 4
noisesmith17:08:23

no built in, this is likely the most natural way to do it

user=> (apply hash-map (apply concat (partition 2 1 [1 2 3 4])))
{1 2, 3 4, 2 3}

👍 4
souenzzo17:08:38

Final impl + test

(let [next-in-cycle #(apply hash-map (apply concat (partition 2 1 (take (inc (count %))
                                                                        (cycle %)))))
      left [:N :L :S :W]
      turn-left (next-in-cycle left)
      turn-right (next-in-cycle (reverse left))]
  (= (map (juxt turn-left turn-right) left)
     [[:L :W] [:S :N] [:W :L] [:N :S]]))

😮 4
petterik18:08:05

Here's another one

(into {} (map vec) (partition 2 1 [1 2 3 4]))

seancorfield18:08:58

Aw, you beat me to it! I was literally just about to paste that in after checking in the REPL!

🙂 4
souenzzo18:08:33

sorter with @UJY23QLS1 + @UARBFQGVB

(let [next-in-cycle #(-> (inc (count %))
                         (take (cycle %))
                         (as-> x (zipmap x (rest x))))
      left [:N :L :S :W]
      turn-left (next-in-cycle left)
      turn-right (next-in-cycle (reverse left))]
  (= (map (juxt turn-left turn-right) left)
     [[:L :W] [:S :N] [:W :L] [:N :S]]))

noisesmith18:08:10

given your use case: (zipmap x (rest (cycle x)))

noisesmith18:08:40

or maybe not

rich 4
walterl17:08:36

user=> ((fn [xs] (zipmap xs (map inc (butlast xs)))) [1 2 3 4])
{1 2 2 3 3 4}

souenzzo17:08:16

But this one is just for numbers

skuttleman18:08:22

This can work with more than numbers

user=> ((fn [xs] (zipmap xs (rest xs))) [:a :b :c :d])
;; => {:a :b, :b :c, :c :d}

💯 4
quadron20:08:33

java-time related question: how do I truncate a java-time value, if I want to truncate it by any arbitrary unit?! java-time/truncate only accepts predetermined units such as 1 second, 1 nanosecond, ...

quadron20:08:14

Do I have to turn the timestamp into a numerical value (unix time), do computation there and return it? or is there a simpler native way?

quadron20:08:50

(my-truncate timestamp (java-time/minutes 39))

bostonaholic20:08:00

@veix.q5 you might want to look at clj-time to see if there’s anything in there that will do this for you

bostonaholic20:08:23

I don’t know off the top of my head. I do know that it has a lot of useful functions.

dpsutton20:08:52

can you explain what you mean by truncate? Truncate only takes a unit as you truncate to that unit. It truncate to the month would be 2019-08, truncate to the year would be 2019. What does it mean to truncate 39 minutes?

quadron20:08:27

@dpsutton well my understanding is that time itself is a sequence of delta-ts (iterate (partial + delta-t) origin) , 2019-08 is basically an imaginary representation.

dpsutton20:08:41

i guess. but widely agreed on

dpsutton20:08:03

when's the last 39 minute duration depends on when we all started counting 39 minute blocks of time

quadron20:08:06

I didn't catch your last sentence

dpsutton21:08:34

if you want to truncate to the day or month, we all agree on those and can easily do so. If you want to truncate to an arbitrary last duration, it depends on when the durations began.

dpsutton21:08:31

but maybe i'm misunderstanding you as i still don't know what you would expect to result from truncating the current time by 39 minutes

quadron21:08:46

day or month units and the overall human representation of time (the way they split seasons and rotations of the sun) are built into a whole, which is good for our monkey experience. what I'm saying is that with a representation that is more fine-grained, I could have both the monkey time AND the simple time.

dpsutton21:08:17

can you tell me what you expect from truncating the current time by/to 39 minutes?

noisesmith21:08:10

trivially speaking, you can just round an ms ts to the nearest (* 1000 60 60 39), but based on what starting point?

noisesmith21:08:32

we don't and won't have a data type that is rounded to 39 minutes

quadron21:08:35

I have a timeseries that is supposed to be divided into time windows. the size of the windows is algorithmically discovered through finding an optimum solution to a model.

quadron21:08:25

the starting point never matters. what matters is the delta-ts.

noisesmith21:08:42

then you don't want a time type, you want an interval type

noisesmith21:08:54

or you can just use eg. MS and then do the standard rounding / quotient / truncation etc. math

👍 4
quadron21:08:33

by the way, I guess the starting point matters when consensus is needed.

noisesmith21:08:00

I could imagine a representation based on a starting / synchronization point and a period, but I know of no standard data type that works that way

noisesmith21:08:50

we use data types like this in audio synthesis but typically it's simplified by a procedural representation where time is nearly always processed incrementally in order when these artificial periods come into play

4
noisesmith21:08:56

@veix.q5 a better way to put this might be that instead of defining a piece of data representing a processing period, you define a "rate of update" and then register various things to be invoked or updated at that frequency

noisesmith21:08:34

in some environments there can be local redefinition of the rate of update, the only radical outlier I can think of is the Chuck language where objects have arbitrary update rates (and it's nearly unusably slow last I tried it due to not sharing an update scheduler between assorted objects and instead allowing each object to have its own update timer)

noisesmith21:08:26

this could be totally unrelated to what you are doing - surely with a rate like 39 minutes per cycle you aren't talking about DSP in a conventional sense, but there might be some shared design concerns :D

noisesmith21:08:12

but there definitely is a ScheduledExecutor that comes with the vm, and you can give it 39 minutes as a rate of update if this procedural approach works

quadron22:08:28

do you have any other terms for designating "rate of update"?

noisesmith22:08:50

in most audioi synthesis DSLs it would be called the "control rate"

quadron22:08:01

ScheduledExecutor and the audio stuff concern cases where Real time is at issue? right? otherwise, for Symbolic time, is it any different to a transducer that keeps a counter/memory up to "39" for example?

noisesmith22:08:31

I don't think I understand your use case