Fork me on GitHub
#clojure
<
2018-03-04
>
noisesmith00:03:55

macros don't add namespaces to things, but does, to avoid namespacing of unresolved symbols inside you can use ~'ProductGenerators

noisesmith00:03:03

but that's often a sign you are doing something error prone

emccue00:03:38

Can I declare things in a (do ..) and still have them be read by java when compiled?

emccue00:03:24

When I try and import ProductGenerator Cursive gives me warnings

emccue00:03:10

So that is my first suspicion

schmee00:03:35

what warnings?

emccue00:03:19

If I do a standalone defrecord it all gets detected fine

emccue00:03:21

its either A. that im in a macro B. In a do C. Its an ide issue and its not at the clojure level or D. Im doing something else wrong

schmee00:03:07

there’s at least one thing wrong with the macro: the _ is not quoted so it will expand to (get [your-ns.core/_] ...)

schmee00:03:26

have you checked that you get the expected output with macroexpand?

noisesmith00:03:36

@emccue in that code where does "Supllier" come from?

emccue00:03:52

java.util.function

schmee00:03:33

again, since the Supplier symbol is not quoted it it will be resolved in the current namespace, ie your-ns/Supplier

schmee00:03:11

try using 'java.util.function.Supplier instead

emccue00:03:51

Heres how it resolves right now

noisesmith00:03:54

@schmee no it gets filled with the fully qualified thing in scope

noisesmith00:03:03

+user=> (import (java.util.function Supplier))
java.util.function.Supplier
+user=> `Supplier
java.util.function.Supplier

schmee00:03:01

[(quote com.ish.test.factories.factories/_)] is wrong, ~'_ will do the trick

noisesmith00:03:22

@emccue one thing that would simplify that code is that you can use interface# as the interface name - every instance of foo# in one backtick is replaced by the same gensym with that base string foo

noisesmith00:03:41

also _# works just fine :P

noisesmith00:03:14

@emccue I recommend using interface# instead of gensym, but if you do use gensym (symbol x) for gensym x is redundant, it's always already a symbol

emccue01:03:10

(im editing the most recent snippet with changes as I go instead of flooding the chat)

noisesmith01:03:05

also another fun thing is that defining a method impl for defrecord inside a macro is common enough that defrecord actually accepts namespace qualified method names - it is smart enough to just use the name part and ignore the ns part

noisesmith01:03:54

so

(~'generate ...
on line 10 can just be
(generate ...

noisesmith01:03:19

also if you change ~'this in both places to this# and avoid the question of capture / shadowing

noisesmith01:03:46

if you can't tell, I'm fond of eliminating ~' from code - it improves readability and long term correctness

emccue01:03:16

okay im going to make those improvements now

emccue01:03:25

any idea about the non resolving though?

noisesmith01:03:54

@emccue I was trying to replicate to help with that but there were too many references to undefined functions

emccue01:03:08

I can give you some

noisesmith01:03:19

I can stub them, it doesn't matter what they really do

noisesmith01:03:27

or if it does, then I narrowed that down

emccue01:03:36

actually yeah ill leave you to do the stubs

emccue01:03:56

ill try and break it down to a minimal example on my end too

emccue01:03:38

Im not beyond thinking its just my broken build system

noisesmith01:03:56

@emccue I have it stubbed enough to not find Product - where would that have been defined?

emccue01:03:02

which has been a fun time today

emccue01:03:16

its a class like this

noisesmith01:03:38

and it actually exists in that scope before running that code?

noisesmith01:03:57

because the Product symbol passed to the macro is resolved in expansion

noisesmith01:03:48

OK - so that exists and is visible when the macro is run?

noisesmith01:03:13

if so your error is in one of those functions I stubbed

emccue01:03:16

Yep its imported

emccue01:03:29

can you pass your stubbed version

emccue01:03:39

because if that doesnt work then its definitely my build system

emccue01:03:05

yep does not work

emccue01:03:10

back into maven land i go

schmee01:03:00

be brave 🙏

emccue01:03:02

Even that isnt working

emccue01:03:36

But this does

noisesmith01:03:53

could justify a bug report on Cursive? the dev hangs out here

emccue01:03:54

possibly, but Im quicker to assume that its me or my config

emccue01:03:55

im not sure which is in a more depressing state

emccue02:03:03

...though the dev probably knows how maven works

emccue02:03:37

I dont know his uname, but if I did I would @ him

noisesmith02:03:56

there's the #cursive channel

emccue02:03:28

hoping on now

emccue02:03:51

also worth asking so I might be able to abandon this project

emccue02:03:10

Do you know any good test data generation tools

emccue02:03:14

for javaland

noisesmith02:03:31

tag is "cfleming" but he should reply in that channel

cfleming02:03:57

@emccue Unfortunately Cursive has problems with macro forms like that. You can tell it to treat your macro as one that it already understands, but from what I can tell yours doesn’t look like anything that currently exists.

cfleming02:03:00

@talios is the author of clojure-maven-plugin, I haven’t used it much.

emccue02:03:36

hmm, well my entire team uses intelliJ, and I was trying to sneak some clojure as part of a project

cfleming02:03:38

There’s a lot of chat here, what’s the executive summary of the problem?

emccue02:03:55

but if I cant make their imports resolve its kinda bust

emccue02:03:43

1. My clojure code isnt building in step with the java code 2. Records I define through my macro dont become visible to java

cfleming02:03:29

Ok. So #1 will probably have to be @talios, or someone with more experience of clojure-maven-plugin, sorry.

cfleming02:03:49

For #2, can you get away without using the macro, or make your macro look more like an existing one?

emccue02:03:42

The whole point was to write a macro to define a way to feed test data that isnt parsing through json files

emccue02:03:50

so what I want to achieve is

emccue02:03:27

Then from my the java code for e2e tests I can get a list of all the possible matchups of those things

emccue02:03:48

so I would get a instance of Product for each possible maintainer

emccue02:03:58

and so on for every sub generated field

emccue02:03:26

and I can also give a few different examples from real life data

emccue02:03:38

(moving the colon over to make it valid edn)

emccue02:03:15

The :for is just me being cheeky

emccue02:03:37

Noone else on my team uses clojure so I would have to be able to get away without having them make the investment in Cursive up front

cfleming02:03:23

@emccue Could you make a simple repo with a self-contained example and I’ll take a look at it?

cfleming02:03:21

I don’t fully understand what those are doing - is the idea that it will generate a class for each of the :name elements?

emccue02:03:49

no it just generates one class

emccue02:03:58

in this case UserGenerator

emccue02:03:13

each call to defgenerator only needs to be able to define one record

emccue02:03:41

the rest is recursive map flubbery and thats all on me

cfleming02:03:22

Why does each instance of the class require its own interface? Is that used in generate-examples?

emccue02:03:49

(but if you are curious the idea is to have a list of functions (list #(assoc % key val) ...) or optionally another sublist of functions that I would flatten [somehow] to produce a list of lists of functions that i can apply to the map to build it)

emccue02:03:08

well, Im not sure it does but I went that route because apparently

emccue02:03:33

A. Theres no way I can type hint List<Thing> in clojure, only List<Object>

emccue02:03:43

B. The best I can do is an array of Thing

cfleming02:03:55

In fact, it’s really only List

emccue02:03:22

and the only way to add that annotation to a record is through an interface

emccue02:03:30

(or so I think)

noisesmith02:03:31

List<Thing> doesn't directly exist in bytecode (yes, there's some reflectable data that some tools use but that's not the same as having the typeper se)

emccue02:03:56

yeah, jvm bytecode is the bees decrepit knees

cfleming02:03:18

Right, but @emccue is looking to provide a typed return for Java clients using IntelliJ, which will use it.

emccue02:03:22

but I still want to somehow let java programmers use my code and get normal type hints

cfleming02:03:29

If I understand correctly.

noisesmith02:03:03

ahh, so intellij is one of those tools that would pick up that metadata

cfleming02:03:08

I don’t think there’s any way to create a class which does what you want in Clojure.

cfleming02:03:29

Specifically, generic return types.

emccue02:03:36

well, the arrays of data thing is fine

emccue02:03:44

i can live with that comprimise

cfleming02:03:14

In terms of Cursive support, I can’t think of a good way to achieve what you want. I think the best compromise would be to just use raw defrecords. I don’t think the interface in your example adds any value, so you’ll just end up with a more verbose solution.

cfleming02:03:02

Something like:

(defrecord UserGenerator []
  Supplier
  (get [_] (generate-examples <your data>)))

noisesmith02:03:28

I think the real problem here is they need to implement specific method names, and that can't be done in clojure without defining an interface

cfleming02:03:38

Which isn’t quite as nice as a DSL, but isn’t terrible, and anyone can look at it and immediately see what it does.

cfleming02:03:06

So the specific method names are called reflectively from Java, or something?

emccue02:03:30

actually im converting the map to json and back through jackson into the class

emccue02:03:57

And yes, im going to hell for that

cfleming02:03:36

So… I’m totally lost 🙂

cfleming02:03:24

What are you actually returning to Java? An array of UserGenerators, or an array of Users?

cfleming02:03:06

I assume Supplier is a Java interface, what does that look like?

emccue02:03:15

an array of Users

emccue02:03:32

supplier is for me internally because I cant implement IFn on a record

emccue02:03:58

and i need to have some notion of recursion here

cfleming03:03:42

Ok - what do your Java clients actually need?

emccue03:03:01

a UserGenerator class that has a method that will return a sequence of Users

cfleming03:03:07

When they call this code, what will that look like?

cfleming03:03:58

So something like:

public interface Supplier<T> {
  T[] generate();
}

cfleming03:03:31

Ok, I still can’t see a way to have UserGenerator implement Supplier<T> - it’ll only ever implement Supplier.

emccue03:03:11

Thats fine

cfleming03:03:15

I think to do what you want, you’ll need to have UserGenerator defined in Java, and then in its generate method you can call into Clojure using Clojure.var(), and then your generation functions can just be functions and everyone will be happy 🙂

emccue03:03:42

with the annonymous interface I can do it though

cfleming03:03:48

If your class only implements Supplier, your for loop above won’t work, your u will have type Object.

emccue03:03:04

^"L[complete.path.Apple;"

emccue03:03:14

is a typehint for array of Apples

cfleming03:03:19

I might be wrong (this is getting a little esoteric) but I don’t think that even returning a typed array will help, because Java won’t see the type of the object statically.

emccue03:03:24

or maybe I switch the L and the [

cfleming03:03:04

Hang on, I’m going to need to test this.

emccue03:03:56

Nope, works fine at least on the intelliJ level

cfleming03:03:16

Unfortunately I can’t think of a good way to make Cursive understand what you’re doing.

cfleming03:03:45

Additionally, even if you can get it to work, your teammates will still need Cursive for this to work, unless you’re building an artifact that they depend on.

cfleming03:03:21

If you’re building a jar or something that they they depend on, that should work.

emccue03:03:30

I can do that, but they would still need to write the data examples in the test repo

emccue03:03:41

which means calling into the macro from clojure

emccue03:03:37

without full cursive debugging thats fine so long as intelliJ can see the classes

emccue03:03:05

I think theres simpler plugins that just do syntax highlighting

emccue03:03:30

but if there isnt a way to make intelliJ see the generated classes then its kinda bunk

cfleming03:03:45

So here’s what I have:

cfleming03:03:35

public interface Supplier<T> {
  T[] generate();
}

public class User {
  public String name;
}

cfleming03:03:45

And:

(ns test
  (:import (test Supplier)))

(definterface Foo
  (get ^"[Ltest.User" []))

(defrecord FooGenerator []
  Foo
  (get [_])
  Supplier
  (generate [_]))

cfleming03:03:56

Which is what you’re trying to generate, right?

emccue03:03:12

The supplier part is kinda off

emccue03:03:40

java.util.function.Supplier is all in clojureland I dont care about typing it

emccue03:03:55

But otherwise, yes

emccue03:03:38

its Foo that will be used from java (swap get and generate)

cfleming03:03:38

Ok, ignore the Supplier part, we’re just trying to get the static bit back to Java

emccue03:03:00

okay I got it working doing "[Ljava.lang.String;"

emccue03:03:12

Make sure to add the semicolon, that might be the issue

cfleming03:03:07

I now have:

(definterface Foo
  (get ^"[Ltest.User;" []))

(defrecord FooGenerator []
  Foo
  (get [_]))

cfleming03:03:21

But I still get the same from Java.

emccue03:03:06

move the ^ back

emccue03:03:10

it should be

cfleming03:03:10

Actually, the type hint should be on the name symbol

cfleming03:03:20

but it’s still not working for me.

cfleming03:03:37

Actually, after editing the Java it does.

cfleming03:03:50

That’s a Cursive bug, looks like.

emccue03:03:21

oh it also exposed me to a fun bytecode bug

emccue03:03:58

what a mismatch

cfleming03:03:11

so… I still can’t see a good way to make Cursive understand this, at least until I have an API for adding macro support.

cfleming03:03:46

IMO your best option is still to define UserGenerator in Java, where you can have your types easily without jumping through hoops.

cfleming03:03:28

That can call Clojure.var(“test-ns”, “generate-users”) and do a dirty cast as required.

cfleming03:03:00

Your Clojure code can just look like:

(defn generate-users []
  (generate-from {<all your data>})

cfleming03:03:30

There’s more boilerplate, but I can’t see another way of doing it.

emccue03:03:16

well, all these records are already in a namespace that has :gen-class

emccue03:03:45

so it feels like I can abuse that somehow

cfleming03:03:47

Perhaps, but I think that calling a var is simpler, and doesn’t require any abuse.

emccue03:03:08

yeah but this is all about giving people who dont want to learn clojure a pretty interface

emccue03:03:58

I am not against calling a var

emccue03:03:22

but I at least want that syntax or something close to it

emccue03:03:02

The pitch should be "heres this pretty syntax" followed by "heres the steps to use it" and optionally install this one plugin.

emccue03:03:14

If I can get it down to that I think I have a chance

emccue03:03:53

at the very least, you have at least one bug report and one use case for better macro interpreting support

emccue03:03:34

(im totally fine making the java side a bit more complicated than a for loop : thats a tar pit anyways)

emccue03:03:04

ooooh what if I had a second macr

emccue03:03:34

(somedumbmacro UserGenerator [])

emccue03:03:46

And I tell cursive to treat that like defrecord

emccue03:03:58

then have that macro yield nil

emccue03:03:00

nope no dice

cfleming03:03:14

Sorry, I have to head out - family is home 😞

emccue04:03:37

enjoy your fufilling life

emccue04:03:51

ill be watching fraiser and sobbing

dominicm10:03:05

Is there any proposals for/information about/rejection of supporting multiple versions of a namespace / multiple classloaders for a namespace in Clojure? I've tried searching google groups, jira & confluence. I haven't turned up anything of interest.

foxlog12:03:21

how to hotfix html page by nrepl? It’s good when I evaluate clj file in local and it worked in remote server, but how to let local html page updated to remote server?

noisesmith14:03:04

the floating point spec requires that NaN is not equal to NaN

tbaldridge14:03:55

It’s not a number, but it may not be equal to my thing that is also not a number 😀

noisesmith14:03:39

there's actually multiple NaN values in the spec - but in practice hardly cares which one you have and they are not even equal to themselves

benzap14:03:19

It's odd, but it's unclear how to test against NaN

jmckitrick14:03:21

Is there an organized way to view the available lein templates? I need to decide what to use for a new project, and they’ve grown tremendously.

benzap14:03:53

and there doesn't appear to be any clojure.core function to test against NaN, is this a grave undersight, or am I going crazy?

noisesmith14:03:03

(Double/isNaN ...)

noisesmith14:03:13

it's different in cljs

benzap14:03:08

ah ok, this will do for now

noisesmith14:03:11

(js/isNaN x)

noisesmith14:03:23

well, that's the only way to do it that I know of

noisesmith14:03:21

it might be possible to use (and (not (pos? x)) (not (neg? x)) (not (zero? x)) (number? x)) portably?

noisesmith14:03:40

+cljs.user=> (js/isNaN 1)
false
+cljs.user=> (defn nany [x] (and (number? x) (not (neg? x)) (not (pos? x)) (not (zero? x))))
#'cljs.user/nany
+cljs.user=> (nany 1)
false
+cljs.user=> (nany (/ 0.0 0.0))
true

noisesmith14:03:57

but Double/isNaN and js/isNaN are more efficient

benzap14:03:07

That's an interesting workaround 🙂

Alex Miller (Clojure team)15:03:12

In Java, positive and negative infinity are special values too (##Inf, ##-Inf)

Alex Miller (Clojure team)15:03:03

not sure if nany might catch those too, but not sure in cljs/js

noisesmith16:03:40

oh, I'll have to try

noisesmith16:03:09

yeah, those are not detected by those checks, but Double/isNaN returns false for them too

noisesmith16:03:24

you could detect an inf by asking if (= x (dec x)) maybe

noisesmith16:03:57

with interop that's Double/isInfinite

noisesmith16:03:56

also less a problem because in the fp spec ##Inf and ##Inf are in fact considered equal

whilo16:03:27

i think (= x (dec x)) will fail on large numbers in js, as the floating point grid delta is larger than 1 there

noisesmith16:03:55

oh, good point

Wes Hall17:03:49

Could somebody please sanity check me? I could have sworn that I have seen a core api function that will return a function that just returns it's first argument, the equivalent to (fn [x & _] x), but now I am thinking that I might have dreamt it.

Wes Hall17:03:10

Yes, I think maybe constantly is what I am thinking of, but that does something slightly different of course, returning a fixed value and ignoring all arguments. Maybe this is what my tired brain is referencing. It's not really a problem, the above form is simple enough, for some reason I just thought it was already there.

bronsa17:03:47

there's no function like the one you're thinking of in core

manutter5117:03:55

Oh you said a function that returns a function etc. But that would just be a function that returns constantly, no?

bronsa17:03:24

he wants (fn [x & _] x), constantly is (fn [& _] c)

manutter5117:03:18

(fn [x & _] (partial constantly x)) ? 🙂

manutter5117:03:02

Nope, that won't work

Wes Hall17:03:48

(fn [x & _] x) works OK, it was just for some reason I thought there was a core function that generated this function for you. Happy to write it like this (it's often the dispatch function for my multimethods). Luckily my laptop memory is more reliable they my own 🙂. Thanks forlks.

noisesmith17:03:45

you can compose it point free as (comp first list)

noisesmith17:03:24

perhaps there's a more clever version that also avoids forcing the arg list for indefinite arg count

Wes Hall18:03:10

Ahhh, yes. That's nice. Thanks.

fmnoise18:03:07

hi! any chance to do type upcasting in clojure?

(defprotocol P (p [_]))
(defrecord R [x]
  P (p [_] "Hello from R"))
(extend-type Object
  P (p [_] "Hello from Object"))
(p (R. 1)) ;; => "Hello from R"
(p (Object.)) ;; => "Hello from Object"
(p ^Object (R. 1)) ;; => "Hello from R"
I need to call protocol method on R instance upcasted to Object

tbaldridge19:03:45

These dispatches are dynamic, so no.

tbaldridge19:03:22

Your best bet is to call the object specific function manually via a normal function call.

schmee19:03:38

this just in: microbenchmarking is hard

schmee19:03:49

seems like the JVM just constant folds my entire program 😂

Alex Miller (Clojure team)19:03:29

There’s that Java benchmarking framework you might want to look at, I think there is a clojure helper with it

Alex Miller (Clojure team)19:03:57

Covers a lot of the important issues (I assume you’re already familiar with Criterium)

schmee19:03:48

yes, Criterium is what I’m using now. I’ve seen JMH mentioned a lot so I guess it’s time to double down and learn it, thanks 🙂

mbjarland20:03:05

any easy way to do the following:

(map vector [1 2 3] [6 7 8 9 10])
=> ([1 6] [2 7] [3 8])
but get [nil 9] [nil 10] as extra elements at the end of the result?

noisesmith20:03:53

(map-indexed (fn [i x] [(nth v1 i nil) x]) v2) ?

mbjarland20:03:03

@noisesmith nice! I had a nasty reduce, figured there was a better way

noisesmith20:03:09

it gets a big uglier when you need to flip it for the other one to be the shorter coll, you could make a multimethod that dispatches based on (> (count a) (count b))

noisesmith20:03:49

or you can just map on (range (max-key count colls)) and use nth for both

noisesmith20:03:45

err, make that (apply max (map count colls))

emccue21:03:05

Going at my problem from a different angle now

emccue21:03:47

how can I use clojure.java.api.Clojure and clojure.lang.IFn to construct a record

Alex Miller (Clojure team)22:03:54

IFn eval = Clojure.var("clojure.core", "eval");
eval.invoke(Clojure.read("(defrecord R [a b])"));

Alex Miller (Clojure team)22:03:22

will kind of work but the default namespace is clojure.core so you’ll create clojure.core.R

emccue22:03:00

sorry not define

emccue22:03:15

so if I have a record that is already in a namespace

Alex Miller (Clojure team)22:03:30

if you already have the constructor, then that’s just a function you invoke named ->R or whatever

Alex Miller (Clojure team)22:03:13

IFn newR = Clojure.var("my.ns", "->R");
newR.invoke(1, 2);

Alex Miller (Clojure team)22:03:28

or the record is itself just a class with a positional constructor so simply:

Alex Miller (Clojure team)22:03:43

my.ns.R(1, 2); should work too

emccue23:03:15

@alexmiller That ended up working out in the end

emccue23:03:05

another thing I want to do with my current code is just allow raw json, but I cannot for the life of me figure out macros to that extent

emccue23:03:31

basically I just need to turn trailing : into whitespace

emccue23:03:54

Its definitely a nice-to-have and not a requirement though

noisesmith23:03:17

this task might not be in scope for clojure macros

noisesmith23:03:50

but if you need to translate symbols in a macro, you can call name on the symbol to get the string, then edit it, then call symbol again

noisesmith23:03:55

this gets messy fast though

lwhorton23:03:41

is it a bad idea to use repeatedly or any of the infinite sequence functions for io? say I wanted to read a file line by line, forever, for example… could I quite simply do something like the following:

(defn get-file [path]
  ;; make the file available as a lazy-seq of strings
  (repeatedly
    #(let [rdr (io/reader (io/resource path))]
       (letfn [(read-next []
                 (lazy-seq
                   (if-let [line (.readLine rdr)]
                     (cons line (read-next))
                     (do (.close rdr) nil))))]
         (read-next)))
    )
  )
Or am I going to wind up with some un-collected resources?

noisesmith23:03:33

that code is basically line-seq btw

noisesmith23:03:48

but yes, io and laziness are a bad fit

noisesmith23:03:58

also that comment is incorrect - the get-file gives you a lazy-seq of lazy-seqs of strings

Ryan Radomski23:03:34

I might recommend a Stuart Sierra component to manage your side effects. If anybody has a better alternative feel free to weigh in

Ryan Radomski23:03:27

Although the downside is you would really have to buy into the component system to see the best results. You have to weigh the tradeoffs

emccue23:03:06

quick stdlib question. whats a function that can take [['a] ['b 'c 'd] ['a 'e]] and return [['a 'b 'a] ['a 'b 'e] ['a 'c 'a] ['a 'c 'e] ['a 'd 'a] ['a 'd 'e]]