Fork me on GitHub
#clojure
<
2018-12-15
>
valerauko04:12:12

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

jaide05:12:38

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?

ikitommi13:12:11

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: https://github.com/ikitommi/fun. 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?

ikitommi13:12:14

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

ikitommi13:12:53

even simpler thing that fails:

(defprotocol Fun
  (fun [this]))

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

(satisfies? Fun (instance))
; true

(defprotocol Fun
  (fun [this]))

(satisfies? Fun (instance))
; false

slipset15:12:41

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, java.io.Serializable {
    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);
}

emccue16:12:01

so uhh, metadata only can be attached to clojure IObjs

emccue16:12:06

meaning ints are right out

emccue16:12:51

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

emccue16:12:12

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

emccue16:12:30

{:value 42 :a "something"}

emccue16:12:49

(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

emccue16:12:36

IObj is not a protocol

mfikes16:12:49

FWIW, datafy solves the same problem by putting atom (and other derefable) values in a singleton vector: https://github.com/clojure/clojure/blob/master/src/clj/clojure/datafy.clj#L49

❤️ 8
emccue16:12:02

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

emccue16:12:14

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?

emccue16:12:30

you cannot implement an

emccue16:12:11

(clicked send too early)

emccue16:12:06

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

emccue16:12:01

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

emccue16:12:24

BigInt is a java class afaik

Dustin Getz16:12:42

=> clojure.lang.BigInt

emccue16:12:02

(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

emccue16:12:38

maybe not big int but Number

emccue16:12:05

and I would just write out the java for this

emccue16:12:23

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?

emccue16:12:24

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

emccue16:12:51

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

emccue16:12:06

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

lilactown16:12:26

speaking of metadata

lilactown16:12:27

user=> (defn foo [] "bar")
#'user/foo
user=> (= foo (with-meta foo {:bar "baz"}))
false
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

lilactown16:12:14

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.

lilactown16:12:42

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

lilactown17:12:15

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

emccue16:12:39

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

emccue16:12:11

(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

lilactown21:12:41

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

lilactown21:12:09

user=> (type foo)
user$foo
user=> (type (with-meta foo {:a "b"}))
clojure.lang.AFunction$1
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

seancorfield21:12:56

@lilactown

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

❤️ 8
lilactown21:12:21

👌 TIL ancestors!

8
hiredman22:12:29

meta on function objects is problematic

☝️ 1
hiredman22:12:00

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

andy.fingerhut23:12:25

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

andy.fingerhut23:12:37

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

andy.fingerhut23:12:38

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

andy.fingerhut23:12:26

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.

andy.fingerhut23:12:50

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

andy.fingerhut23:12:53

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.

andy.fingerhut23:12:57

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.

mfikes23:12:14

A recent example is the case where you want to see if someone dynamically rebound a function. https://github.com/clojure/clojure/blob/master/src/clj/clojure/main.clj#L275

mfikes23:12:41

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

mfikes23:12:51

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

andy.fingerhut23:12:53

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
andy.fingerhut23:12:13

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

didibus23:12:30

Does anything relies on fn meta?

didibus23:12:06

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