Fork me on GitHub
#clojure
<
2019-02-01
>
andrea.crotti10:02:26

when writing a clojure library, should we just have all the dependencies declared as :provided?

borkdude10:02:30

I tend to make deps that are not strictly needed for the library to be usable provided.

borkdude10:02:58

An example would be in clj-http that cheshire is not strictly needed, unless you want to do json parsing

borkdude10:02:09

Sometimes people make these part of the test / dev dependencies. I think provided is the intended use for this.

andrea.crotti10:02:24

nice thanks, I'm trying to add a library to a project now, and I get a million conflicts like [io.confluent/kafka-schema-registry "5.1.0" :exclusions [org.slf4j/slf4j-log4j12]] -> [io.confluent/rest-utils "5.1.0"] -> [org.eclipse.jetty.websocket/javax-websocket-server-impl "9.4.12.v20180830"] -> [org.eclipse.jetty.websocket/javax-websocket-client-impl "9.4.12.v20180830"] -> [org.eclipse.jetty.websocket/websocket-client "9.4.12.v20180830"] -> [org.eclipse.jetty/jetty-client "9.4.12.v20180830"] -> [org.eclipse.jetty/jetty-http "9.4.12.v20180830"]

andrea.crotti10:02:44

the actual version numbers from the main project will however still take precedence right?

andrea.crotti10:02:25

would be nice to just exclude everything for that library

borkdude10:02:46

the ones you define in your own project take precedence, I think

borkdude10:02:16

e.g. if your main project says clojure 1.10, but a lib says clojure 1.9, you will get 1.10

phill11:02:28

When deps A and B bring along distinct versions of C it's not too hard to exclude C from one or the other or to state a direct dependency on one particular version of C. The problem is, what happens later?! When updating A or B, will you notice the extraordinary measures, back them out, and reintroduce only if necessary? Or will your project stay forever pinned to C-version-1 long after A and B have moved on to C-version-6?

phill11:02:01

A deps-resolver syntax that makes clear both the problem and the solution, and applies the solution only when circumstances match the stated problem, would allow tidier engineering

andrea.crotti11:02:45

yeah it's a fun problem in every ecosystem really

andrea.crotti11:02:59

btw is there a library to generate a graph of the dependencies (with graphviz) that works?

andrea.crotti11:02:26

vizdeps seems to be the best one but it's not working anymore, and I could not really find one that works apart from that

Wes Hall12:02:53

Often wondered if (at least on the JVM), it would be possible to isolate all this stuff with some classloader magic. One CL for each dep and a CL graph that mirrored the dependency graph etc. There's a fun project for somebody who gets bored.

borkdude12:02:45

@wesley.hall Maybe boot is already doing this with pods

Wes Hall12:02:36

Oh, I didn't know this. Will investigate. Thanks.

hmaurer13:02:42

Hi! How can you match all routes with a specific prefix in Bidi? e.g. match all routes that start with /app. So all routes of the form /app/*/*...

Ben Hammond13:02:13

I am trying to implement clojure.lang.ILookup in a record like this

(defrecord StaticMapService [m]
  sim/Service
  (start_service [this process] this)
  (finalize_service [this process] this)

  IFn
  (invoke [_ ident]
    (get m ident))

  ILookup
  (valAt [_ k] (get m k))
  (valAt [_ k not-found] (get m k not-found)))
and I get a Duplicate interface name "clojure/lang/ILookup" in class file compile__stub/services/entityid_service/StaticMapService error can't for the life of me see what I am doing wrong Any suggestions?

vemv13:02:37

my guess: defrecord already impls that interface. so you have to use the lower-level deftype or such

👍 5
Ben Hammond13:02:16

Thanks very much

Alex Miller (Clojure team)13:02:09

Records already implement ILookup

Alex Miller (Clojure team)13:02:40

If you want custom lookup behavior you need to drop down to deftype

👍 5
Ben Hammond13:02:40

oh so I can just merge upon start_service

hmaurer15:02:25

Is there a nice clojure lib to go back and forth between UUIDs and base62-encoded (or similar) UUIDs? (to put in URLs)

eval202015:02:39

There’s the built-in murmurhash (https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Murmur3.java) - not sure if it would fit the requirements

Alex Miller (Clojure team)15:02:46

I would consider that an implementation detail of Clojure and not something to rely on calling into

eval202015:02:35

ah - good to know!

seeeturtle15:02:49

i have something not quite understood while reading Clojure Programming (it's really a great book) it explain one of the usage of recur is when performing mathematical operation. > In addition, because it allows you to work with numerics without the overhead of boxed representations, ... what overhead of boxed representations means?

seeeturtle15:02:01

ah, it was a java stuff

leontalbot18:02:24

Hey guys! In clojure jdbc, I am trying to understand :return-keys How can I get only [{:a … :b ...} ...] returned?:

(db-do-prepared-return-keys
  db
  (into statement updates)
  {:multi? true
   :return-keys {:columns [:a :b]}})

noisesmith18:02:52

just checking, this is clojure.java.jdbc not clojure.jdbc right? - the name of the latter makes this unfortunately ambiguous

noisesmith18:02:13

(in fact I think the naming is downright hostile)

leontalbot18:02:00

yes clojure.java.jdbc

noisesmith18:02:47

np - I don't expect most people to know clojure.jdbc exists (I certainly don't recommend using it) - just checking in case there's been miscommunication ;D

leontalbot18:02:52

:return-keys [:a :b] didn’t work for me either

hiredman18:02:47

if I recall the behavior of return-keys is highly database dependent

hiredman18:02:24

I don't think :return-keys is actually an option you can set on db-do-prepared-return-keys

hiredman18:02:53

it always returns keys

hiredman18:02:24

db-do-prepared-return-keys calls prepare-statement with return-keys set to the boolean true, regardless of any options passed in

hiredman18:02:46

you can change that by calling prepare-statement yourself and passing that in

hiredman18:02:19

the prepare-statement docstring has a blurb about the behavior being driver dependent

leontalbot18:02:31

ok, I can do :row-fn #(select-keys % [:a :b]) though

Jan K18:02:59

@leontalbot It looks like you only want to select specific columns. In that case you should do that in your SQL statement (instead of SELECT * FROM do SELECT a,b FROM ...). The purpose of :return-keys is to get keys generated by inserts from the DB.

leontalbot19:02:06

@jkr.sw my statement is an UPDATE one though

seancorfield19:02:41

@leontalbot :return-keys can accept a vector of column names on some (but not all) databases. You should not use db-do-prepared-return-keys really -- it's very low-level (and, I think, assumes you already have a PreparedStatement -- where you would have set those options prior to calling it!).

seancorfield19:02:01

insert! and execute! should accept :return-keys with a vector (if your DB allows it).

seancorfield19:02:24

Also, there's always #sql for deeper clojure.java.jdbc questions (I'm more likely to see it there).

leontalbot19:02:46

ok changing channel

jrychter20:02:54

@hmaurer I use a very nice library by @tonsky that produces compact 26-char UUIDs: https://github.com/tonsky/compact-uuids

hmaurer20:02:36

ah brilliant, ty!

dpsutton20:02:56

i thought i remembered ghadi making or showing off someone else's uuid -> words library? does that ring a bell with anyone

markw20:02:57

If I wanted to hook into an existing protocol on a built-in type to provide some additional functionality, is there a better way than wrapping the protocol's method I want to extend?

noisesmith20:02:39

well, there are no protocols or methods there

noisesmith20:02:38

(there are protocols and methods in the implementation of the object passed to swap!, and swap! itself is an object extending IFn but I don't think that's what you mean)

noisesmith20:02:12

but if you mean generally, you want to do an AOP style wrapping of an existing function, yeah that's what it typically looks like

noisesmith20:02:00

though idiomatically wrap-foo means "hand this thing a function, it returns a new one", which isn't your semantics here, but that's a naming issue

markw20:02:47

poor choice of nomenclature on my part... I basically just mean if I have a type that implements a protocol, and I want to do something extra with one or all of the functions implementing the protocol on that type, is that the best way

markw20:02:00

yeah i named it wrap but it's more a passthrough method, agree on the naming

Alex Miller (Clojure team)20:02:13

I don’t think protocols have anything to do with your question?

noisesmith20:02:56

@markw once again that's not a method

noisesmith20:02:28

method and function aren't interchangeable in Clojure, they mean specific things and we use both

markw20:02:12

i didn't literally mean method, sorry... "passthrough methods" is just naming convention for functions (or methods in other languages) that do nothing but delegate to another function

noisesmith20:02:53

sure, most AOP style wrappers will take the original function and then use apply with a variable arg list like in your code

noisesmith20:02:47

more commonly the function itself is passed as one of the args, which makes the wrapper more reusable (one reason functions are more flexible than methods)

noisesmith20:02:33

@markw this is also visible in the signature of swap! itself, and alter-var-root - the function signatures make it easy to pass in a function that exists already, and return an "enhanced" version

markw20:02:51

I noticed that actually...

noisesmith20:02:19

oh, and I don't know why a is in your function signature there - it's valid to call swap! with a single arg function, and your definition makes that impossible

cupello20:02:31

Hi! Is there a clever way to convert this {:sku [111111111 22222222], :storeId 88888888} to this {:sku 111111111 :storeId 88888888}, {:sku 22222222 :storeId 88888888}?

ghadi20:02:11

do it the simple way with mapcat

jens21:02:46

I am not sure about your expected result type. Perhaps you can use ((fn [m] (map #(assoc m :sku %1) (:sku m))) {:sku [111111111 22222222], :storeId 88888888}).

markw21:02:02

((fn [m] (map #(hash-map :sku %1 :storeId %2) (:sku m) (repeat (:storeId m)))) {:sku [111111111 22222222], :storeId 88888888})

markw22:02:36

A general version (couldn't think of a good name for the function, gave it my best shot)

markw22:02:39

(defn explode-key [m explode-key anchor-key] (reduce #(conj %1 {explode-key %2 anchor-key (get m anchor-key)}) [] (get m explode-key))) (explode-key {:sku [111111111 22222222], :storeId 88888888} :sku :storeId)

parrot 5
jumpnbrownweasel22:02:28

(defn add-store-id-to-skus [{:keys [sku storeId]}]
    (map #(hash-map :sku %, :storeId storeId) sku))

markw22:02:45

@alexmiller Stepped away for a minute... you're right I guess the question is less about protocols and more about conrete implementations. Maybe an example would be better than me fumbling around with incorrect terminology: If I have a single-function protocol implemented concretely by several different objects, and I want to inject some minor changes to one of the implementations (and don't have access to the code to modify it directly), is wrapping the way i've described generally the best way to go about that? From an OO perspective, I would just subclass and then override the method of interest, do whatever extra stuff I needed to do, and then call the parent's overriden method from the child.

Alex Miller (Clojure team)22:02:43

again, I don’t think the fact that this is a protocol function makes any difference

Alex Miller (Clojure team)22:02:00

if there is a function, and you want stuff to happen before that call, wrap it in another function

jumpnbrownweasel23:02:12

@alexmiller, why do you specifically call out macros with asterisks? I thought the asterisk was just a way to name a variant of a function, like X and X prime, without any conventional meaning. But what you said makes me think there is a convention.

jumpnbrownweasel02:02:55

Oh, maybe you meant with exceptions.

Alex Miller (Clojure team)03:02:43

Yes, I just meant there were exceptions in the case of macros as you can’t use them as higher order functions

Alex Miller (Clojure team)03:02:51

So the abstraction leaks

Alex Miller (Clojure team)22:02:17

Clojure intentionally abstracts invocation over functions, multimethods, protocol methods, macros (with asterisks), collections that are invokable, etc so that callers don’t know or care

lambdalove 5
Alex Miller (Clojure team)22:02:16

in general in the core team we take it as a rule of thumb these days to wrap all protocol function invocations in an outer function that gives you a hook to handle special cases

👍 5
Alex Miller (Clojure team)22:02:21

for example, clojure.datafy/datafy is a wrapper function that calls clojure.core.protocols/datafy

Alex Miller (Clojure team)22:02:47

protocols are best at the bottom, not at the top

5
markw22:02:44

OK thanks for the clarification.

noisesmith22:02:39

if you want something that swaps atoms differently, you can wrap swap! to get a new function, if you want a new thing that swaps differently than an atom you can implement IAtom, and that implementation could even be done compositionally by allocating and using an atom internally

noisesmith22:02:09

but good usable clojure code tends to wrap functions when possible, rather than extending protocols