Fork me on GitHub

is there any maintained clojurescript oauth 2 client library out there?


If a cljs client library for that exists what needs to be maintained with it? Once it implements the spec, has tests, and has been battle tested isn’t it done?


Hi. Bumped into strange issue with protocols. lein check started to fail on CI: the load order of the files changed (it’s not deterministic) and because of that, a satisfies? fails on a protocol. I tried to find a minimal case that errors, here it is: Is this a bug or a feature? As the load is only called by lein check, one should not bump into this kind of problem with actual usage?


Hmm, I guess this could happen also when reloading namespaces with tools like tools.namespace.


even simpler thing that fails:

(defprotocol Fun
  (fun [this]))

(defn instance []
    (fun [this] "this is fun")))

(satisfies? Fun (instance))
; true

(defprotocol Fun
  (fun [this]))

(satisfies? Fun (instance))
; false


I spent the better part of an evening once being trapped in something like this.

Dustin Getz16:12:28

I want to add metadata to a primitive, like an int. I think it should work on boxed ints. (with-meta (bigint 10) {:a 42}) ;ClassCastException class clojure.lang.BigInt cannot be cast to class clojure.lang.IObj How can I reify up the thing I want in a way that behaves like a boxed primitive?

Dustin Getz16:12:45

package clojure.lang;
public abstract class Obj implements clojure.lang.IObj, {
    final clojure.lang.IPersistentMap _meta;
    public Obj(clojure.lang.IPersistentMap meta) { /* compiled code */ }
    public Obj() { /* compiled code */ }
    public final clojure.lang.IPersistentMap meta() { /* compiled code */ }
    public abstract clojure.lang.Obj withMeta(clojure.lang.IPersistentMap iPersistentMap);


so uhh, metadata only can be attached to clojure IObjs


meaning ints are right out


if you really want to carry metadata with an int just put the int in a map with the metadata


and at that point, you might as well just attach the data directly and not through metadata


{:value 42 :a "something"}


(with-meta {:value 42} {:a "something"})

Dustin Getz16:12:26

Yes but can't I extend-protocol (or something) java.lang.Integer or clojure.lang.BigInt into an IObj


IObj is not a protocol


FWIW, datafy solves the same problem by putting atom (and other derefable) values in a singleton vector:

❤️ 8

its a bit unfortunate, but some parts of clojure are done using interfaces


and not protocols

Dustin Getz16:12:16

If this is impossible, what exactly is the reason why

Dustin Getz16:12:27

java abstract classes are not portable?


you cannot implement an


(clicked send too early)


you cannot implement an interface on a class that is already defined. BigInt already exists and is declared and it does not implement the IObj interface. You cant redefine BigInt to implement it


the reason it is different for protocols is because protocols, while they are made into interfaces, will fallback to looking up the type in a map to see if it satisfies the protocol

Dustin Getz16:12:11

Why doesnt BigInt implement IObj


BigInt is a java class afaik

Dustin Getz16:12:42

=> clojure.lang.BigInt


(finger on nose)

Dustin Getz16:12:10

so is it possible to reify a DustinBigInt which duck types like a big int and implements the thing i want


maybe not big int but Number


and I would just write out the java for this


Im sure its doable with genclass and whatnot but (fart noises)

Dustin Getz16:12:51

will it seamlessly unbox as a clojure value? Quack like an int?


i know most ops work on a java.lang.Number


so if you extend that it should work i think maybe

Dustin Getz16:12:57

(+ (bigint 10) 1)
=> 11N

(defn +
  "Returns the sum of nums. (+) returns 0. Does not auto-promote
  longs, will throw on overflow. See also: +'"
  {:inline (nary-inline 'add 'unchecked_add)
   :inline-arities >1?
   :added "1.2"}
  ([] 0)
  ([x] (cast Number x))
  ([x y] (. clojure.lang.Numbers (add x y)))
  ([x y & more]
     (reduce1 + (+ x y) more)))

Dustin Getz16:12:34

Yeah, this is a rabbit hole


what do you want metadata on numbers for?

Dustin Getz16:12:38

i need to tag a dbid that came out of datomic with the database it came out of, because it will remove a layer of boxing

Dustin Getz16:12:42

currently use edn to do it


speaking of metadata


user=> (defn foo [] "bar")
user=> (= foo (with-meta foo {:bar "baz"}))
I thought metadata wasn’t supposed to effect equality

Dustin Getz16:12:22

functions are reference equality

Dustin Getz16:12:16

(= inc (with-meta inc {:a 10}))
=> false
(= [1] (with-meta [1] {:a 10}))
=> true


I guess I got tripped up with this bit of documentation:

An important thing to understand about metadata is that it is not considered to be part of the value of an object. As such, metadata does not impact equality (or hash codes). Two objects that differ only in metadata are equal.


but then, I don’t know how you do that with functions unless you attach it to the var/symbol

Alex Miller (Clojure team)17:12:52

you attach it to the var/symbol


I was originally doing this to compare to CLJS, which doesn’t have var’s to attach metadata

Dustin Getz16:12:46

it's hitting the function equality paath in =, which is just reference equality, and the wrapped ref is different


So is your idea with storing that info in metadata to just increase perf?


(I'm thinking that since you mentioned removing a layer of boxing)

Dustin Getz16:12:35

No, it's a weird hyperfiddle thing. Our urls look like this: and that #entity tag is used to distinguish tempids from strings in the url

Dustin Getz16:12:30

and further our code paths use it, so there's this dumb wrapper type everywhere


I’m trying to understand how with-meta works with functions


user=> (type foo)
user=> (type (with-meta foo {:a "b"}))
it’s kind of obscured what interfaces a function created by defn actually implements. I was thinking it was probably an instance of AFn but I can’t confirm that



user=> (defn foo [] 42)
user=> (ancestors (class foo))
#{clojure.lang.AFn clojure.lang.IFn java.util.Comparator java.util.concurrent.Callable java.lang.Object clojure.lang.IObj clojure.lang.AFunction java.lang.Runnable clojure.lang.IMeta clojure.lang.Fn}

❤️ 8

👌 TIL ancestors!


meta on function objects is problematic

☝️ 1

with-meta is designed for use with values with value equality, but function objects have reference equality which with-meta breaks


Perhaps one could imagine modifying clojure.core/= so that when comparing two functions, whether they have metadata or not, would do (identical? x y) on the functions x and y, ignoring the metadata associated with them. I have a hard time believing that the core team would consider that an important enough change in behavior to make, unless there is some use case they have in mind that would make something easier for them to do.

Dustin Getz23:12:02

(= (fn [x] (inc (inc x))) (fn [x] (* 2 x)))

Dustin Getz23:12:38

Oh, i thought it already did that


Apparently it does not, given this Clojure 1.10.0-RC5 session:


user=> (def f1 (fn [x] (inc x))) #'user/f1 user=> (def f2 (with-meta f1 {:a 5})) #'user/f2 user=> (meta f1) nil user=> (meta f2) {:a 5} user=> (= f1 f2) false

Dustin Getz23:12:24

yea i give up, i have no idea how metadata works now


In that case I believe clojure.core/= is comparing whether f1 and f2 are identical objects in memory, including the metadata, and they are not.


That is just a special case for functions. For immutable Clojure collections, the metadata is ignored when comparing collections.


I would argue that in 99.99% of cases you shouldn't be worried about doing = between Clojure functions anyway. Only exceptions I can think of are really special cases involving implementing dev tools, or attempting to serialize functions across a wire.


I can guess that others are more imaginitive than I in finding reasons to want to use = to compare functions to each other, but you run up against the halting problem if you are thinking that two independently defined functions should ever return true when comparing them.


A recent example is the case where you want to see if someone dynamically rebound a function.


That bit of code is checking for equality against the default init for the dyn var


My hunch is that lack of equality for fns when meta is involved is simply an oversight.


Note: My guess above on the core team's inclinations are simply that: my guess. Anyone who wishes for a change in behavior in this area is welcome to file a JIRA ticket and see how it goes.

👍 4

Given the existence of REBL and its uses of metadata, for all I know a change in behavior there might become welcome.


Does anything relies on fn meta?


It was always my thought what most people think as fn meta is actually meta on the var holding the fn.

Dustin Getz23:12:30

thats a good point