clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
sova-soars-the-sora 2021-02-24T00:24:36.104500Z

Thanks, I'm checking it out now. It's fun trying to wrap one's mind around P2P 😄

richiardiandrea 2021-02-24T02:07:04.106100Z

Hi everybody, is there any known problem with satisfies? not working on protocols that use :extend-with-metadata?

richiardiandrea 2021-02-24T02:11:06.106200Z

oooh ok I need a :refer - I was using namespace/Protocol inside satisfies? duh thanks a lot to the author of this post https://matthewboston.com/blog/clojure-protocol-namespaces/

richiardiandrea 2021-02-24T02:16:51.106500Z

nope it's not that - that works on extended things though

2021-02-24T02:36:31.108100Z

That isn't the name of a protocol

2021-02-24T02:36:54.108700Z

A protocol is not imported

2021-02-24T02:37:37.109300Z

A protocol is a var defined using defprotocol

richiardiandrea 2021-02-24T16:34:26.141Z

Yeah I am doing that ... I was thinking of adding a spec that make sure the passed object satisfies? my protocol

richiardiandrea 2021-02-24T03:24:47.109500Z

Ok sounds good, i tried refer as well as namespace/protocol but not a var. I had to take a break from the madness but will try again tomorrow, do you have any example by any chance I can look at? Thanks for answering!

alexmiller 2021-02-24T03:29:52.109700Z

refer and namespace/protocol should both work (just like normal vars)

👍 1
alexmiller 2021-02-24T03:31:22.110Z

but back to your original question - satisfies? does NOT work with metadata extended instances, in case that's the real problem you're seeing

2021-02-24T03:34:47.110200Z

I am just saying, if you use import to get access to a name, that name does not name a protocol

👍 1
2021-02-24T03:35:37.110400Z

Import let's you refer to a java class or interface by its simple name, a protocol is not a java class or interface

👍 1
2021-02-24T03:36:20.110600Z

So of you use import, it's not a protocol

👍 1
richiardiandrea 2021-02-24T04:07:28.110900Z

@alexmiller @hiredman I went down the rabbit hole (on mobile) and checked github code. It seems there is at least one other instance of my problem here https://github.com/psagers/links-clj/blob/9e8795dfc1ce407720d5f1f1b683cba3983deeee/src/server/net/ignorare/links/db.clj#L30 Has this been reported? That is exactly what I am seing. In any case tomorrow I will try an isolated repro..

alexmiller 2021-02-24T04:15:18.111600Z

yes, you can vote for it here: https://ask.clojure.org/index.php/4622/satisfies-doesnt-work-instance-based-protocol-polymorphism

richiardiandrea 2021-02-24T04:16:34.111900Z

Cool thank you Alex

alexmiller 2021-02-24T04:18:26.112100Z

in general, you should not actually be using satisfies? much - it's better to rely on just invoking the protocol methods and fallback to default behavior (Object etc) if needed

jumar 2021-02-24T04:22:22.112300Z

I remembered that I saw something about measuring thread allocations. This could be a good enough approximation. It uses ThreadMXBean: https://stackoverflow.com/questions/61539760/benchmarking-jvm-memory-consumption-similarly-to-how-it-is-done-by-the-android-o Here's an example: https://github.com/jumarko/clojure-experiments/blob/master/src/clojure_experiments/performance/memory.clj#L166-L187

(defn thread-allocated-bytes [t]
  (let [thread-mbean (java.lang.management.ManagementFactory/getThreadMXBean)
        thread-id (.getId t)]
    (.getThreadAllocatedBytes thread-mbean thread-id)))

(defn allocated-bytes
  [f]
  (let [thread (Thread/currentThread)
        start (thread-allocated-bytes thread)]
    (f)
    (- (thread-allocated-bytes thread) start)))

(comment
  
  (let [t (Thread.  (fn []
                      (let [v (vec (range 1e8))])
                      (Thread/sleep 1000) (println "DONE.")))]
    (println "Thread allocated - before start:" (thread-allocated-bytes t))
    (.start t)
    (println "Thread allocated - after start:" (thread-allocated-bytes t))

    (Thread/sleep 100)
    (println "Thread allocated - after 100 ms:" (thread-allocated-bytes t))

    (when (< 1e6 (thread-allocated-bytes t))
      (println "Stopping the thread...")
      ;; note that `(.interrupt t)` isn't enough here - the thread is busy allocating and cannot be interrupted
      (.stop t)
      (println "Thread stopped")
      (println "Thread allocated - after stop:" (thread-allocated-bytes t)))

    (Thread/sleep 200)
    (println "Thread allocated - after 300 ms:" (thread-allocated-bytes t))

    (Thread/sleep 800)
    (println "Thread allocated - after 1100 ms:" (thread-allocated-bytes t)))
  ,)
;; prints:
;; Thread allocated - before start: -1
;; Thread allocated - after start: 14064
;; Thread allocated - after 100 ms: 125412240
;; Stopping the thread...
;; Thread stopped
;; Thread allocated - after stop: -1
;; Thread allocated - after 300 ms: -1
;

martinklepsch 2021-02-24T11:53:14.122300Z

Super basic macro question: How can I turn {:x 1 :y 2} into (def x 1) + (def y 2)?

martinklepsch 2021-02-25T13:03:28.189300Z

At least now it’s not going to disappear in the voids of this Slack https://martinklepsch.org/posts/clojure-macro-magic-vars-from-map.html

martinklepsch 2021-02-24T12:00:35.122400Z

(defmacro def-all [m]
  (->> (for [[n v] m]
         `(def ~(symbol n) ~v))
       (into [])))
This kind of works, curious if there’s better ways

nbardiuk 2021-02-24T12:44:33.122600Z

I've recently discovered https://clojuredocs.org/clojure.template/do-template could be useful for your scenario

martinklepsch 2021-02-24T12:55:06.122900Z

huh, cool! never seen that before and it’s kind of nice

nooga 2021-02-24T14:30:48.127500Z

I have a question about licensing. I’m working on something that could be construed as a Clojure interpreter - early in the works but planning to be as compatible as possible, probably to the point of being able to crunch clojure.core. I don’t necessarily plan to distribute clojure.core with the interpreter as I would rather write my own. But even then, some macros and functions would end up looking 1:1 same as in clojure.core. Given the above - am I forced to make my project EPL-1.0?

alexmiller 2021-02-24T14:52:30.128Z

Disclaimer: IANAL and this is not legal advice. It seems to me that if you are copying clojure.core all or in part, you are effectively making a "contribution" under the terms of the license and a derivative work provided in source code form must be made available under the EPL 1.0 agreement per the license. Distributing in object form has different constraints.

👍 1
2021-02-24T14:54:35.128200Z

If you are doing this as part of a project for a company you work for, and plan to distribute source code for it, then perhaps the company you have actually hires intellectual property lawyers that can give you actual legal advice. If this is a personal project, you can hire an intellectual property lawyer yourself for such advice, but that can be expensive, and is pretty uncommonly done.

2021-02-24T14:56:37.128400Z

Stated more positively, the existence of Clojure implementation distributed under the EPL gives you the option to read it, learn from it, and copy parts of it into your own project, but then you have accepted the EPL and its terms. You can also make the project you want in a clean room implementation where you never look at the Clojure implementation code, and then it is your code and you can use any license you choose.

dpsutton 2021-02-24T15:00:30.129800Z

I learned about that like two days ago looking at the implementation of “are”. I had never heard of it either

kulminaator 2021-02-24T15:11:34.130Z

; offtopic - would be great if we could just write code & solve problems and not have to deal with money or licenses that the latter requires

2021-02-24T15:12:57.130200Z

I understand the feeling, but unless you think that the idea of intellectual and/or all property should be abolished, licenses will exist.

kulminaator 2021-02-24T15:14:13.130400Z

i'd vote for robots doing all the annoying work for us so we'd be left with the fun parts

2021-02-24T15:15:04.130800Z

such robots do not automatically spring into existence by the mere act of voting/wishing 🙂

kulminaator 2021-02-24T15:15:35.131Z

well once they do we'll be in trouble 😄

kulminaator 2021-02-24T15:16:54.131200Z

not in an armageddon war style trouble, trouble in the way that we still have to stay sane and somehow behave

nooga 2021-02-24T15:17:54.131400Z

so this is a personal project

nooga 2021-02-24T15:23:26.131600Z

I wanted to use the MIT license but I’m just wondering if defining my when as (list 'if test (cons 'do body)) would count as copying parts of clojure and forcing me into EPL

nooga 2021-02-24T15:24:10.131800Z

as there would be multiple cases when, even if not intended, code would look the same because sometimes there is only one sane way to implement something

kulminaator 2021-02-24T15:25:04.132Z

if you separate that part of the code out and publish that under epl with correct references , and use it as a library in your project .... would that enable you to "mit" the rest?

nooga 2021-02-24T15:25:47.132200Z

I’d guess so but IANAL

2021-02-24T15:29:35.132400Z

There are examples of companies wanting to be squeaky clean about a reimplementation actually adopting so called "clean room reimplementation" techniques, which you can study more about if you want to be squeaky clean. These same kinds of 'fuzzy boundaries' exist in copyright law for published books and papers, e.g. copying one or a few sentences with attribution is covered under "fair use" in many countries law, copying entire chapters is not. Is the dividing line a particular number of characters or words? I doubt it.

nooga 2021-02-24T15:44:22.132800Z

So I’m doing my own compiler and bytecode vm which I have to design and write myself based on: • my knowledge of clojure (top of my head) • available user-facing docs on how clojure and its APIs should behave Thus, I’m not concerned about infringing on anything at this point - the fun will start when the interpreter matures enough to run actual Clojure code

2021-02-24T15:49:39.133Z

And you have a strong preference to release your code under a license that is something different than EPL 1.0 ?

nooga 2021-02-24T15:51:36.133200Z

I’m going for MIT, I’m not fond of copyleft licenses like EPL

2021-02-24T15:54:24.133400Z

Still not a lawyer, but if you "write your own replacement for clojure.core", without referring to the contents of clojure.core, from the top of your head understanding of how things should behave, then you are probably fine. If 100 of those definitions are character-for-character identical with sections of the clojure.core source file, it is hard to believe that you actually implemented it independently.

nooga 2021-02-24T15:54:47.133600Z

right

2021-02-24T15:55:39.133800Z

If two one-line functions are character-for-character identical and the rest are slightly different (or more), then it seems plausible that you wrote them yourself.

2021-02-24T15:57:00.134Z

Those kinds of micro-analysis of differences would only ever be an issue if someone wanted to take this up in a court case, which if you truly implemented your own code, and even briefly described your process in a project README or something, seems unlikely to ever happen.

nooga 2021-02-24T15:57:24.134200Z

that makes sense

2021-02-24T16:00:03.134400Z

As a coding practicality side issue, making something 100% compatible with Clojure/JVM implementation in all ways sounds extremely time consuming to do without referring to its implementation code. ClojureScript and Clojure/JVM have differences, partly due to differing underlying JVM and ClojureScript runtimes, but partly due to the fact that it is hard to make two implementations 100% compatible.

nooga 2021-02-24T16:01:09.134700Z

yeah

2021-02-24T16:01:25.134900Z

You could ask @borkdude for his estimates of % compatibility (if a percent number makes sense at all) between his sci interpreter and Clojure/JVM, but there are a list of known differences there.

nooga 2021-02-24T16:02:16.135100Z

I mean, I’m just making an interpreter that could only be considered a toy when compared to Clojure itself - my goal is to have as much compat as possible but, as you pointed out, this will not be 100% due to runtime differences and time constraints

borkdude 2021-02-24T16:03:30.135300Z

The main diff between sci and JVM Clojure is features that rely on defining new classes at runtime like defprotocol and defrecord are basically "faked" using multi-methods. And deftype and definterface aren't supported at all.

borkdude 2021-02-24T16:04:36.135600Z

These things aren't supported by joker at all maybe for the same reason. In your interpreter, if you support Go interop, maybe this will be a funny mix of Go structs and defprotocol, that's up to you to find out and I will be interested to look what you come up with :)

nooga 2021-02-24T16:05:38.135900Z

I want ns.Def("x", &MyGoStuff{foo: 1}) show up as a record on the other side, with methods if MyGoStuff has any

nooga 2021-02-24T16:07:00.136100Z

I’ve been experimenting with this and it’s def doable

nooga 2021-02-24T16:07:58.136300Z

anyway, thank you @andy.fingerhut, @alexmiller and @borkdude. This clears things up a bit for me 👍

alexmiller 2021-02-24T16:11:35.136600Z

as an obvious test, I think if you ever find yourself copying and pasting code from clojure itself into your files, that is clearly ... "copying" ... and "copyright" is in play :)

👍 2
😄 3
borkdude 2021-02-24T16:12:23.137100Z

I love copying clojure

😄 2
ericdallo 2021-02-24T16:28:04.140Z

Hello, I have an issue with a https://gist.github.com/scttnlsn/9744501 that @borkdude helped finding to me, and when I use it seems to only print the value on the first or multiple times `put!, the second, fourth, etc are not called:

(defn debounce [in ms]
  (let [out (chan)]
    (go-loop [last-val nil]
      (let [val (if (nil? last-val) (<! in) last-val)
            timer (timeout ms)
            [new-val ch] (alts! [in timer])]
        (condp = ch
          timer (do (>! out val) (recur nil))
          in (recur new-val))))
    out))

(def in (chan 1))

(go-loop [value (<! (debounce in 1000))]
  (println value)
  (recur (<! (debounce in 1000))))

(put! in {:bar (rand-int 100)})

blak3mill3r 2021-02-25T15:16:20.193900Z

(go-loop [value (<! (debounce in 1000))]
  (println value)
  (recur (<! (debounce in 1000))))
This is calling debounce again for each iteration (constructing its lexically scoped out channel again each time). Also, did you want the first value to be debounced?

blak3mill3r 2021-02-25T15:16:47.194100Z

(def cin (a/chan 1))
(def cin-d (debounce cin 2000))
(a/go-loop []
  (println "Read" (a/<! cin-d))
  (recur))
(a/put! cin 5)

blak3mill3r 2021-02-25T15:17:15.194400Z

^ this is how I would expect to use it, just call debounce on your input channel once, and keep a reference to the resulting channel

ericdallo 2021-02-24T16:29:05.140200Z

So when I call the put! , the first time it's printed the value, when called a second time, it's not

ericdallo 2021-02-24T16:29:29.140400Z

but If I call quickly multiple times the put!, only the last value is printed as expected

ericdallo 2021-02-24T16:33:10.140600Z

AFAI could understand, the debounce function waits for a second input to them start the debounce

dpsutton 2021-02-24T16:33:19.140800Z

when you recur nil after putting, if you don't get anything with the timer, you >! nil which isn't great for async. I think you need some sentinel value for a lack of value other than nil. and if your timer goes off with this sentinel just recur without the >! to out

ericdallo 2021-02-24T16:35:44.141300Z

oh, it makes sense, actually, there is a if that check if the last value is nil and waits for a next <!, that seems to be the issue too

2021-02-24T17:50:42.141700Z

I'd replace the (into []) with an outer do

(ins)user=&gt; (defmacro def-all [m] (cons 'do (for [[n v] m] `(def ~(symbol n) ~v))))
#'user/def-all
(ins)user=&gt; (def-all {:x 1 :y 2})
#'user/y
(ins)user=&gt; x
1
(ins)user=&gt; y
2

kwladyka 2021-02-24T18:35:25.143700Z

How to find in spec which specs make an issue with Couldn't satisfy such-that predicate after 100 tries.? How to see for each spec how many tries was make during generate? I have complex data structure and I really don’t want to guess this manually 🙂

2021-02-24T18:35:54.143900Z

you'll need to

2021-02-24T18:36:06.144300Z

for anything complex you should just write a custom generator

kwladyka 2021-02-24T18:37:18.145800Z

I expect it will be an issue about a few specs maybe. But I don’t know which one.

2021-02-24T18:37:44.146100Z

any spec with s/and

kwladyka 2021-02-24T18:38:55.146600Z

Does it mean there is no way to > How to find in spec which specs make an issue with Couldn’t satisfy such-that predicate after 100 tries.? > How to see for each spec how many tries was make during generate? ?

ghadi 2021-02-24T18:39:33.147300Z

post a full stack trace

2021-02-24T18:39:48.147800Z

I don't believe so, once generation is happening, things are actually pretty opaque to spec, it is in the hands of test.check

ghadi 2021-02-24T18:40:36.148500Z

like a leaf generator used in a collection generator, maybe nested indirectly

2021-02-24T18:41:27.149400Z

it is reality, using s/and makes the couldn't satisfy error very likely, unless you use a custom generator

2021-02-24T18:42:21.150400Z

the way generation works for s/and specs is it generates data with the first spec, then filters it through the predicates of the rest of the specs

kwladyka 2021-02-24T18:42:51.151300Z

yes, I was hoping to be able to track this to find issues

kwladyka 2021-02-24T18:43:34.152800Z

without this… this is really dark side of spec heh. Even my own generators can be not optimised and there is no good way to test it.

2021-02-24T18:43:56.153400Z

there is #clojure-spec and #test-check

👍 1
robertfw 2021-02-24T18:44:25.154Z

ime it's almost always something with s/and so I'll echo the advice to start there. struggled with this a bunch and the best I came up with is to use a binary search approach - e.g., for the case you are looking at, eliminate half at a time

2021-02-24T18:44:54.154700Z

#test-check can be very helpful for writing generators

robertfw 2021-02-24T18:45:08.155100Z

also get in the habit of testing generation whenever you update your specs. it's a pain if you go and change a bunch of specs and then have to go through each

➕ 1
seancorfield 2021-02-24T18:45:30.155600Z

☝️:skin-tone-2: Definitely this.

kwladyka 2021-02-24T18:45:49.156200Z

I have a situation where there was no spec before and I wrote spec for data just now.

kwladyka 2021-02-24T18:45:58.156600Z

so not always the case 😉

seancorfield 2021-02-24T18:46:09.156900Z

I tend to have an RCF with s/exercise calls in while I'm developing new specs and I try to make sure every spec generates as I'm writing them.

👍 1
2021-02-24T19:46:04.158300Z

fib :: [Integer]
fib = 0 : 1 : zipWith (+) fib (tail fib)
Saw this in another chat medium. It’s a haskell sample to create a Fibonacci sequence. Is there a slick equivalent like that for Clojure or is https://blog.klipse.tech/clojurescript/2016/04/19/fibonacci.html the best out there?

dpsutton 2021-02-24T19:47:06.158600Z

(def fib (lazy-cat [1 1] (map + (rest fib) fib)))

👏 5
borkdude 2021-02-24T19:48:58.158900Z

^ that's pretty much the Haskell equivalent yeah

2021-02-24T19:52:39.159100Z

except that the list and the tail are flipped in order (doesn't matter for correctness since + doesn't care)

dpsutton 2021-02-24T19:54:21.159300Z

yeah. i remembered seeing it and just googled. credit to SO user Joe Lehmann

dpsutton 2021-02-24T19:54:39.159600Z

they also mentioned that clojure-docs has examples of this for iterate and lazy-seq

2021-02-24T19:56:37.159800Z

I saw the iterate example but it used an anonymous function, which wasn’t bad but was curious if a cleaner version existed.

2021-02-24T19:58:36.160200Z

Though should it be lazy-cat [0 1]?

borkdude 2021-02-24T19:59:21.160500Z

fib normally starts with 1, 1

2021-02-24T19:59:47.160700Z

the mathematical definition (and the haskell example) starts with 0,1

dpsutton 2021-02-24T20:00:31.160900Z

it also might be kinda both. depending on the branch of mathematics, the natural numbers begin with 0 or 1, depending on what kind of edge cases you are more annoyed by

2021-02-24T20:00:44.161100Z

"In some older books, the value {\displaystyle F_{0}=0} is omitted"

2021-02-24T20:15:52.161300Z

Interesting. I don’t really know much about the fibonacci sequence.

2021-02-24T20:16:45.161500Z

https://clojuredocs.org/clojure.core/lazy-cat Btw what does that mean in the warning about it retaining the head? Does that mean it’s storing every value in memory?

2021-02-24T20:16:55.161700Z

> ;; N.B. this example holds onto the head of a lazy seq which should generally be avoided

2021-02-24T20:33:31.161900Z

I know this doesn’t exactly amount to return value for your help on this but hopefully getting someone excited about something in Clojure is worth something to you.

Gustavo Bertolino 2021-02-24T20:39:17.162200Z

More efficient fn to create a fibonacci seq

(def m-fib
  (memoize (fn [n]
             (condp = n
               0 1
               1 1
               (+ (m-fib (dec n)) (m-fib (- n 2)))))))

sova-soars-the-sora 2021-02-24T21:45:30.162400Z

i wish all this cool ninja code automatically went into a mega list of how to do stuff in clj

2021-02-24T22:44:48.162800Z

the lazy version is already memoized (and the memoization has the same memory usage problems that holding onto the head does in terms of heap usage for larger values)

2021-02-24T22:45:24.163Z

I guess it is true that the memoized version doesn't need to walk the spine of a lazy seq to get the pre-computed answer though

2021-02-24T23:44:31.163200Z

@gustavobertolino Nice! I shared that in the chat and someone had a question I don’t know how to answer: > Why use (- n 2) when you can do (nth (iterate dec 5) 2)?