Fork me on GitHub
#clojure
<
2018-07-31
>
Tiago Dall'Oca01:07:43

thank you so much!

Tiago Dall'Oca01:07:20

Using clojure 1.10.0-alpha6 and added -init to the robot class

seancorfield01:07:45

You've piqued my interest so I may take a look at that at the weekend and write a robot too 🙂 @mrdalloca

jjttjj02:07:40

I'm trying to make a macro that can "make" a reify call. assuming data here is the correct syntax that should go in reify, how do I make this into an actual macro call?

(defn make-reify [data]
  `(reify
     com.my.Class
     ~@data))
the output of this function is code that is a correctly formatted reify call that i can then copy and paste into the repl and it works. But I can't seem to find the right incantation to make this work as a macro. Just changing the defn to a defmacro I get a
IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Symbol  clojure.lang.RT.seqFrom (RT.java:542)

noisesmith02:07:08

sounds like the body of make-reify isn't a form, but a binding that has the value of the form at runtime?

noisesmith02:07:42

Clojure 1.9.0
(ins)user=> (defmacro make-reify [data] `(reify ~@data))
#'user/make-reify
(ins)user=> (make-reify (Object (toString [this] "hi")))
#object[user$eval150$reify__151 0x732f29af "hi"]
(ins)user=> (def body '(toString [this] "hi"))
#'user/body
(ins)user=> (make-reify body)
CompilerException java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol, compiling:(NO_SOURCE_PATH:4:1) 

noisesmith02:07:45

@jjttjj that's the most likely way to get that error from that code, and the answer is that a macro can only use the data that is visible during compilation, it doesn't see run-time data unless called at runtime

noisesmith02:07:01

and it isn't called at runtime unless you force clojure to compile (eg. eval)

noisesmith02:07:13

and eval is a big heavy expensive tool that you usually don't want to use

jjttjj02:07:57

thanks! that seemed to lead my down the right path I think

cddr11:07:02

Is it possible in deps.edn to specify global dependency exclusions like you can in lein?

lsund12:07:13

Hi! I'd like to know how to create a multiline-string that does not implicitly get newlines. I tried the following:

"Hello
world" 
=> 
Hello
world

Hello\
world
=> RuntimeException
I would like something like
Hello \
World
=> Hello World

thheller13:07:14

(str "Hello"
     " World")

🙂 4
slipset13:07:16

I kind’a know this is a faq, but I can’t seem to find the discussions on it ATM.

slipset13:07:38

(keyword " ") is valid and returns a keyword which is unreadable by the reader.

slipset13:07:01

I seem to remember reading arguments of why this is desired.

slipset13:07:20

Anyone have links to discussions on this?

slipset13:07:54

Preferably with input from “official” sources.

slipset13:07:55

Also, what would a use case be for a keyword like :foo bar

weavejester13:07:14

@slipset I don’t have any official sources for it right now - I’d advise doing a search on JIRA - but I believe the issue is performance. If you have keyword check its inputs, you potentially slow things down a lot. The gradual integration of spec in Clojure code should eventually solve this, as you’ll be able to turn on checks when needed, and turn them off for production or performance-critical code.

slipset13:07:50

Thanks for the Jira tip

mg15:07:31

Is it idiomatic/a good idea to return an eduction, particularly in cases where it’s going to be used as a value and not for composition?

noisesmith16:07:18

@slipset in my experience the real solution to keyword creating unreadable objects is not to call keyword on freeform runtime input.

noisesmith16:07:52

if the value is flowing through at runtime, a string should work just fine

henrik16:07:23

If you’re not allowed to use clj-http, what would you pick? I’m using it to poke and prod REST APIs.

tbaldridge16:07:37

I’d us the Apache async HTTP libs. They’re simple

đź‘Ť 4
lilactown17:07:38

only bummer is I seem to remember it doesn't support some proxy or cert configs

đź‘Ť 4
đź‘Ž 4
johnj17:07:00

@tbaldridge do you use jackson directly too?

tbaldridge17:07:27

not really since cheshire adds enough value it's not worth going directly against the Java libs

tbaldridge17:07:52

but before all of that I reach for data.json since it has fewer deps

đź‘Ť 4
cjsauer17:07:33

Could someone please verify if this namespace organization strategy is idiomatic? I'm attempting to make my code a bit more structured with the use of protocols, while sill remaining relatively easy to consume. - Single protocols.clj namespace that defines all the major protocols in the project - Various my-app.thing.impl-a, my-app.thing.impl-b namespaces that extend the above protocols - "Cosmetic/aggregate" namespaces such as my-app.thing that collect and expose the above implementations in a user-friendly way. Am I on the right track here?

noisesmith17:07:29

as long as protocols.clj is actually my-app.protocols, or some other multi segment thing, sure

cjsauer17:07:21

@noisesmith cool, thank you. That is indeed how I've written it. There is a bit of duplication in my-app.thing in which I have to "point to" all of the protocol definitions again, but I think it's a small price to pay. Protocols should be relatively stable I imagine.

noisesmith17:07:24

right - both your top level ns and each implementation should be written in terms of the protocol

cjsauer17:07:31

Also, if there are any good "structured Clojure" resources out there that anyone knows of I'd love to read through them. My very first Clojure app is really suffering from having zero protocols defined...very difficult to refactor.

noisesmith17:07:29

the most common thing is to reuse the built in protocols (IFn, PersistentVector, PersistentCollection, PersistentHashMap) by using functions and vanilla data

noisesmith17:07:50

(at that point it doesn't even look like you are using protocols)

cjsauer17:07:37

@noisesmith that's something I'm already noticing...one of my protocols in this pet project is very similar to a Set, but is stateful. Another is very similar to a map (assoc/dissoc semantics) but again is a stateful map...I haven't figured out how to reconcile this yet...if I extend the PersistentHashSet (or whatever the interface is called), how do I regain my stateful semantics?

noisesmith17:07:02

no, it's an immutable data type

noisesmith17:07:35

err, you could own a set, and change which one you own (this is what refs, atoms, agents, vars all do)

cjsauer17:07:05

So basically if I'm modeling a stateful thing, then stick with stateful semantics? For example, the fact that I'm using a ref is an implementation detail that I'd like to remain hidden, because I have a different strategy for the same component that doesn't use refs.

tbaldridge17:07:49

mutating and non mutating "things" should have completely separate interfaces

tbaldridge17:07:19

at least on the update side.

tbaldridge17:07:10

But most many Clojure functions work fine with mutable Java interfaces (get, count, to name two) so you can code against the Java interfaces, which are mutable

cjsauer18:07:27

@tbaldridge >mutating and non mutating "things" should have completely separate interfaces This makes sense. I think I fall into the trap of trying to make my protocols cover too many things. I'm not very familiar with classic Java interfaces, but it sounds like I need to explore them more.

Alex Miller (Clojure team)22:07:00

Theory: The platonic ideal protocol/interface has one method

tbaldridge18:07:05

They're way too broad (the Java interfaces) but you can always stub out the ones you don't want, and clojure does this automatically for you

tbaldridge18:07:20

For example: a persistent hash map is a Java Map, just all the mutation stuff throws exceptions

tbaldridge18:07:04

But something to think about, if you have more than two or three methods in a protocol, you should probably split the protocol.

tbaldridge18:07:28

A good example of this is Clojure vs ClojureScript. Clojure tends to have a lot of methods in its interfaces, these are split out in CLJS

cjsauer18:07:31

It seems that the process of splitting protocols is very iterative...this is partly why I think I'll enjoy having them all defined in one protocols.clj namespace, in that I can tease them apart without being distracted by concrete implementations.

cjsauer18:07:34

I've also discovered a cool way to discover interfaces at the REPL, e.g.:

(use 'clojure.reflect)
(:bases (reflect (type (ref {}))))
;; returns
#{clojure.lang.ARef java.lang.Comparable clojure.lang.IFn clojure.lang.IRef}

cjsauer18:07:10

The thing I'm modeling is very "ref-like", in that it needs to be able to support coordinated concurrent updates, so IRef looks very promising...

cjsauer18:07:46

@tbaldridge do you tend to design your protocols all up front when you start a project, or do you tease them apart over the course of weeks/months while still writing implementations? How much can you hope to "get it right" in design?

tbaldridge18:07:30

They change over time, but the more you pull them apart the less you have to change them in the future.

tbaldridge18:07:08

For example, if I'm defining a collection I may have get-item and set-item, how much do those need to change in the future? They shouldn't much at all.

tbaldridge18:07:30

If the protocols are changing too much, the argument could be made that the abstraction is tied too closely to the implementation.

tbaldridge18:07:44

Abstraction is about providing a common interface to disparate "things". So I'd say make sure you have more than one "thing" if you are using a protocol. Abstraction over a single "thing" is just adding complexity

tbaldridge18:07:14

If you do have multiple things to abstract, then the abstraction is a unification of the common operations supported by all the implementations.

tbaldridge18:07:32

I find that sort of design (and though process) results in protocols that don't need to change often

cjsauer18:07:15

@tbaldridge >Abstraction over a single "thing" is just adding complexity This seems to be the crux of my folly. I currently only have 2 protocols, and each has 5+ methods. I definitely have some teasing to do. I can definitely tell when things are complected whenever I use the word "and" in my comments. e.g. "This is a collective and stateful and associative thing that x y and z".

cjsauer18:07:54

I appreciate your advice.

mauricio.szabo19:07:41

Hi everyone. Currently I'm in a project where I need to consume events from other systems and then transform then into a CSV report sometime in the future. I was just consuming every event, creating somekind of database "snapshot" of the data so that I'll be able to query DB in the future and extract some CSV file. Will this be a good fit for a rule engine like Clara-Rules or some other library?

mauricio.szabo19:07:34

I've never used a rules engine before, so I simply don't know of the implications (and the data I'll have to work is kinda big - we're talking about millions of rows)

hiredman20:07:06

something like apache derby can give a sql engine in process with data entirely in memory, and then if you need to it can switch to storing data on disk

jjttjj20:07:48

i'm still having issues writing a beastly macro to make a reify call with extensive type hints in the parameters of the methods. Is there anything special about the type hints such as ^int or ^java.lang.String or can i just generate them as symbols?

noisesmith20:07:37

to emit metadata from a macro you need to explicitly use with-meta

noisesmith20:07:57

just putting a metadata on a symbol isn't sufficient for a macro to put metadata on the right thing

jjttjj20:07:46

so in

(reify
  my.class
  (myMethod [^int x]))
the ^int x is equal to adding {:tag 'int} to the symbol x?

jjttjj20:07:13

starting to think i might be better off emitting source as a string and eval'ing for this use case

hiredman20:07:00

what are you actually trying to do?

noisesmith20:07:48

user=> (binding [*print-meta* true] (prn ^:int []))
^{:int true} []
nil

noisesmith20:07:59

he's trying to make a reify form with type hints from a macro

hiredman20:07:16

I mean, what are you trying to do that you are making a reify form with type hints in a macro

hiredman20:07:21

like, back up a level

hiredman20:07:12

why aren't you making a function with a reify in it, or a deftype or whatever, and having a macro expand in to calling that?

jjttjj20:07:43

i'm trying to wrap this java api which requires implementing a wrapper interface which has a bunch of method callbacks that you're supposed to implment. they provide an example stub implmentation. i basically want to get all the methods and parameters from that java file, and somehow end up with an implementation that results in calling a single function on a clojure map of the method's parameter names -> values

hiredman21:07:14

like, don't write a macro for that

hiredman21:07:39

at the repl, use reflection to generate a scaffolding of a deftype or a defrecord, then c&p that in

jjttjj21:07:04

ok cool, i have the reflection stuff working to get the data i need, just not sure exactly what to do with it

jjttjj21:07:44

i would kinda prefer to not have to manually copy and paste anything and do it all programatically

hiredman21:07:42

if you want to do it right, then do it right, don't generate a string and call eval (which will fail anyway because eval works on datastructures not strings)

hiredman21:07:46

if you want to be really dynamic about it, maybe check out https://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Proxy.html

jjttjj21:07:00

sorry, what do you mean by do it right here? are you saying since I'm generating source it's better to treat it as source and just spit it into a file somehow?

hiredman21:07:44

no, I just mean doing that is better than "starting to think i might be better off emitting source as a string and eval'ing for this use case"

jjttjj21:07:05

ohh ok gotcha

hiredman21:07:26

and likely much easier than banging your head against a macro

hiredman21:07:00

for scaffolding a single thing, that likely will never change, so generating it once should be fine vs. re-generating it everytime you do macro expansion

hiredman21:07:21

are you sure you need all the type hints?

jjttjj21:07:54

yeah im definitely seeing scaffolding as the way to go

jjttjj21:07:20

for a very small minority of the methods it seems that the type hints are required as the method signatures are ambiguous otherwise

jjttjj21:07:41

(for some context I'm basically trying to implement this https://github.com/jsab/ib-re-actor/blob/master/src/ib_re_actor/wrapper.clj#L111 but the thing it's wrapping has a lot of new changes and I'm trying to think about accomplishing the same thing without having to maintain ~250 lines of code going forward)

jjttjj21:07:49

i think just a dev tool to "create wrapper file" would be good enough. thanks for the input!

Lone Ranger21:07:06

does anyone know if there's a way to get a reference for the data encapsulated by the :or API in a destructured map?

;; this doesn't work :(
(defn foo [& {:keys [a b] 
              :as m
              :or {a 1 b 2 :as this}}]
  (merge this m))

Lone Ranger21:07:29

it seems like a lot of duplicated code to do something like

(defn foo [& {:keys [a b] :or {a 1 b 2} :as m}] (merge {:a a :b b} m))

ghadi21:07:50

You should macroexpand it to see what the :or looks like. It may be surprising

đź‘Ť 4
hiredman21:07:11

don't do the destructuring in the function arguments

hiredman21:07:18

destructure the map after the merge