Fork me on GitHub
#clojure
<
2021-02-24
>
richiardiandrea02:02:04

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

richiardiandrea02:02:06

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/

richiardiandrea02:02:51

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

richiardiandrea02:02:54

I get this error, has anybody ever seen it?

Unhandled java.lang.NullPointerException
   (No message)

                  core.clj:  144  clojure.core/instance?
          core_deftype.clj:  536  clojure.core/find-protocol-impl
          core_deftype.clj:  569  clojure.core/satisfies?
This is when doing:
(import 'MyProtocol)
(satisfies? MyProtocol x)

richiardiandrea02:02:54

I get this error, has anybody ever seen it?

Unhandled java.lang.NullPointerException
   (No message)

                  core.clj:  144  clojure.core/instance?
          core_deftype.clj:  536  clojure.core/find-protocol-impl
          core_deftype.clj:  569  clojure.core/satisfies?
This is when doing:
(import 'MyProtocol)
(satisfies? MyProtocol x)

hiredman02:02:31

That isn't the name of a protocol

hiredman02:02:54

A protocol is not imported

hiredman02:02:37

A protocol is a var defined using defprotocol

richiardiandrea03:02:47

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!

Alex Miller (Clojure team)03:02:52

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

👍 3
Alex Miller (Clojure team)03:02:22

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

hiredman03:02:47

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

👍 3
hiredman03:02:37

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

👍 3
hiredman03:02:20

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

👍 3
richiardiandrea04:02:28

@U064X3EF3 @U0NCTKEV8 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..

richiardiandrea04:02:34

Cool thank you Alex

Alex Miller (Clojure team)04:02:26

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

richiardiandrea16:02:26

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

martinklepsch11:02:14

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

martinklepsch12:02:35

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

nbardiuk12:02:33

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

martinklepsch12:02:06

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

dpsutton15:02:30

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

noisesmith17:02:42

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

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

sova-soars-the-sora21:02:30

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

martinklepsch13:02:28

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

nooga14:02:48

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?

Alex Miller (Clojure team)14:02:30

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.

👍 3
andy.fingerhut14:02:35

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.

andy.fingerhut14:02:37

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.

kulminaator15:02:34

; 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

andy.fingerhut15:02:57

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

kulminaator15:02:13

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

andy.fingerhut15:02:04

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

kulminaator15:02:35

well once they do we'll be in trouble 😄

kulminaator15:02:54

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

nooga15:02:54

so this is a personal project

nooga15:02:26

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

nooga15:02:10

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

kulminaator15:02:04

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?

nooga15:02:47

I’d guess so but IANAL

andy.fingerhut15:02:35

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.

nooga15:02:22

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

andy.fingerhut15:02:39

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

nooga15:02:36

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

andy.fingerhut15:02:24

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.

andy.fingerhut15:02:39

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.

andy.fingerhut15:02:00

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.

nooga15:02:24

that makes sense

andy.fingerhut16:02:03

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.

andy.fingerhut16:02:25

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.

nooga16:02:16

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

borkdude16:02:30

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.

borkdude16:02:36

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 :)

nooga16:02:38

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

nooga16:02:00

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

nooga16:02:58

anyway, thank you @U0CMVHBL2, @U064X3EF3 and @borkdude. This clears things up a bit for me 👍

Alex Miller (Clojure team)16:02:35

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 :)

😄 9
👍 6
borkdude16:02:23

I love copying clojure

😄 6
ericdallo16:02:04

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)})

ericdallo16:02:05

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

ericdallo16:02:29

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

ericdallo16:02:10

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

dpsutton16:02:19

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

ericdallo16:02:44

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

blak3mill3r15:02:20

(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?

blak3mill3r15:02:47

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

blak3mill3r15:02:15

^ 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

kwladyka18:02:25

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 🙂

hiredman18:02:54

you'll need to

hiredman18:02:06

for anything complex you should just write a custom generator

kwladyka18:02:38

Is it official recommendation from spec authors?

kwladyka18:02:27

I mean exactly what you wrote any spec with s/and

kwladyka18:02:18

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

hiredman18:02:44

any spec with s/and

kwladyka18:02:55

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? ?

ghadi18:02:33

post a full stack trace

hiredman18:02:48

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

ghadi18:02:36

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

kwladyka18:02:38

Is it official recommendation from spec authors?

hiredman18:02:27

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

kwladyka18:02:27

I mean exactly what you wrote any spec with s/and

hiredman18:02:21

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

kwladyka18:02:51

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

kwladyka18:02:34

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.

hiredman18:02:56

there is #clojure-spec and #test-check

👍 3
robertfw18:02:25

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

hiredman18:02:54

#test-check can be very helpful for writing generators

robertfw18:02:08

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

3
seancorfield18:02:30

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

kwladyka18:02:49

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

kwladyka18:02:58

so not always the case 😉

seancorfield18:02:09

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.

👍 3
jaide19:02:04

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?

dpsutton19:02:06

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

👏 15
borkdude19:02:58

^ that's pretty much the Haskell equivalent yeah

noisesmith19:02:39

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

dpsutton19:02:21

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

dpsutton19:02:39

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

jaide19:02:37

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

jaide19:02:36

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

borkdude19:02:21

fib normally starts with 1, 1

noisesmith19:02:47

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

dpsutton20:02:31

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

noisesmith20:02:44

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

jaide20:02:52

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

jaide20:02:45

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?

jaide20:02:55

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

jaide20:02:31

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 Bertolino20:02:17

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)))))))

noisesmith22:02:48

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)

noisesmith22:02:24

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

jaide23:02:31

@U012H246NCE 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)?

Gustavo Bertolino00:02:49

I'm not sure if (iterate dec 5) 2) is a valid construction. This construction he suggested out of its context gets hard to see what he really meant. But (- n 2) makes sense a lot for me

jaide00:02:41

After more discussion it was meant to be a joke that I didn’t pick up on. Sorry about that! 😳

jaide00:02:24

Whoa! The first fib impl shared here is not accurate across all uh dialects?

clojure
(def fib (lazy-cat [0 1] (map + (rest fib) fib)))
In JVM Clojure:
clojure
user=> (time (nth fib 82))
"Elapsed time: 0.052346 msecs"
61305790721611591
In ClojureScript (via shadow-cljs):
clojure
cljs.user=> (time (nth fib 82))
"Elapsed time: 0.055000 msecs"
61305790721611580
In Babashka Clojure:
clojure
user=> (time (nth fib 82))
"Elapsed time: 0.0377 msecs"
61305790721611591
Why is the cljs one off by 11?

dpsutton00:02:48

the js vm can't handle integers that large

dpsutton00:02:08

61305790721611591 > Number.MAX_SAFE_INTEGER
true

jaide01:02:20

Oh wow!!! I had no idea.

hiredman01:02:23

the wild thing is when you are sending ids as numbers in json from a backend to the fronted and it exceeds the max safe integer

andy.fingerhut01:02:33

JavaScript represents number as 64-bit IEEE floating point, which can represent integers in the range [-2^54, +2^54] exactly, but only some integers outside of that range.

jcburley05:02:09

Wow, I’m surprised Joker (which is purely an interpreter, no JIT or compilation at all, written in Go) is even this fast:

user=> (time (nth fib 82))
"Elapsed time: 0.293 msecs"
61305790721611591

jaide00:02:24

Whoa! The first fib impl shared here is not accurate across all uh dialects?

clojure
(def fib (lazy-cat [0 1] (map + (rest fib) fib)))
In JVM Clojure:
clojure
user=> (time (nth fib 82))
"Elapsed time: 0.052346 msecs"
61305790721611591
In ClojureScript (via shadow-cljs):
clojure
cljs.user=> (time (nth fib 82))
"Elapsed time: 0.055000 msecs"
61305790721611580
In Babashka Clojure:
clojure
user=> (time (nth fib 82))
"Elapsed time: 0.0377 msecs"
61305790721611591
Why is the cljs one off by 11?