Fork me on GitHub
#clojure
<
2017-06-06
>
qqq00:06:29

in the context of clojure, what does 'dependency injection' mean ?

qqq00:06:46

in particular, I don't even knowwhat 'dependency' and 'injection' mean in the clojure context

joshjones02:06:18

@qqq it's not a term used often in the context of clojure, but I would presume it means what it means in other contexts -- instead of a function/object reaching outside its local context to acquire a resource (dependency), the dependency is passed in to the function as an argument (in OO, typically at object creation via the constructor). I would say that the spirit of DI is used quite often in clojure, since we often avoid global state and instead prefer to pass functions as arguments, with the expectation that the passed function will perform some service to the function being called (we also pass database connections, etc).

noisesmith02:06:59

I think the best example of DI in clojure is ring - when you run a ring server you can deploy to tomcat or run self hosted without editing code, thanks to the injected dep of the http server

noisesmith02:06:42

or I guess that's more like the kind of thing java folks mean when they talk about di (eg. spring)

qqq03:06:35

I think I get it now. For something like system, we have this map {:web-server ... :db-server ... :blah-server ... } which we pass around. Otherwise, we'd have to globally define a web-server, db-server, blah-server, then require the right namespace, and ensure everything is loaded in the right order. In the "DI" method, this "ordering" is not as important, since we can construct the *-server whenever we want, then just pass it all in as a map. Is this roughly correct ?

qqq03:06:27

https://www.youtube.com/watch?v=13cmHf_kt-Q <-- are 'components' basically 'objects done right' ?

seancorfield04:06:30

I would not say that, no. Components address a very specific application lifecycle use case — they’re not any sort of general purpose “object”.

seancorfield04:06:14

Clojure already addresses some aspects of OOP in a “better” way (arguably, depending on your p.o.v.). It has a la carte polymorphism, for example.

qqq04:06:55

@seancorfield : you're absolutely right; I only watched part of the talk when making thea bove statements

qqq04:06:10

2. stuart sierra is a genius; 2014 stuart sierra solved problems 2017 me ran into

kevinludwig05:06:05

Is DI even a thing in clojure (or any dynamic language for that matter)? Typically the thing you inject in java is some sort of state for an object, and you do it because if you dont theres no other way to get ahold of it for the purpose of mocking in a unit test. Clojure uses functions not objects, and dynamic languages can easily mock dependencies without having to resort to DI.

kwladyka06:06:45

Personally i prefer mount over component, like less favor to OOP 🙂

delitescere06:06:56

One could think of DI is a way of providing a sort of partial application in languages without HOFs. Even in a dynamic language, the explicit declaration of the dependencies required by a unit of execution is not a bad thing. My years of monkey-patching Ruby apps, while fun, were not particularly principled.

delitescere06:06:58

Having a function “reach out” to some implicit state leads to less reasonable code, and I think the point of Stuart’s components is to make that dependency explicit and reasonable.

curlyfry06:06:31

I'm a huge fan of https://github.com/weavejester/integrant. To me it feels like the sweet spot between mount and component. It feels more Clojurey (less OOP-ish) than Component, but has the same benefits.

mping08:06:07

greetings fellows, I’m looking for a quick’n’dirt db to use with clj, with the less friction possible. Something like the old days’ embedded hsqldb but without the sql 😄 any advice?

pesterhazy08:06:10

why not just use that or sqlite? robust, well-understood, and can be used as a k/v store if needed

mping08:06:25

I’d prefer something schemaless, a bit tired of sql ddl/dml

mping08:06:44

and I want to use something new 😏

leonoel08:06:40

filesystem + edn is pretty frictionless

mpenet08:06:12

depends on your (query) requirement

mping08:06:44

lookup by id, query by attributes, range query

mping08:06:15

I’d give datomic a go if there was an in-process/embedded version

frederic09:06:26

Hi! I’m just writing a piece of code that relies on a given set not suddenly changing its traversal order, and I realised that that might be an implementation detail and not guaranteed by sets' contract.

frederic09:06:10

E.g., can I be sure that (is (= {1 1, 2 2} (let [s #{1 2}] (zipmap s s)))) ?

frederic09:06:47

or should I expect to possibly get {1 2, 2 1} because s doesn’t have to have a consistent order?

leonoel09:06:01

there is no way this assertion could fail

leonoel09:06:18

call seq several times on the same unordered collection, and the order will always be the same

frederic09:06:14

Thanks @leonoel . It seemed kind of obvious when I was writing the code, but come review time I was like ‘wait I sec, I don’t actually know that’

leonoel09:06:54

actually I can't remember where is it documented

frederic09:06:06

You also suggested suggested a good safety measure: I can actually throw in one explicit seq and use the result in both places.

frederic09:06:13

That way, I won’t have to wonder I won’t have to wonder again if the order is stable

frederic09:06:25

when I come back to the code a few months from now 🙂

Oliver George10:06:11

Can someone point me at an example of using pprint and controlling where the wrapping happens. I'm doing some EDN config generation and I'd like to shape it a little.

Oliver George10:06:26

The main docs and api are quite intimidating.

leonoel11:06:33

@frederic interestingly it doesn't work if collections are equal, they have to be identical

dm312:06:54

probably because (= (hash "Aa") (hash "BB"))

leonoel12:06:51

yes, in case of collision ordering seems to be determined by insertion order

Lambda/Sierra12:06:00

@olivergeorge I think with clojure.pprint your options for customization are really just *print-right-margin* and *print-miser-width*

qqq13:06:48

I've watched through stuart sierra's component; and am convinced it's a good idea. What does "system" provide over "component" ?

gonewest81816:06:58

qqq: I hadn’t heard of “system” until now, but having looked at the repo my concern would be the effort required to keep it up to date.

gonewest81816:06:34

personally I prefer mount over component, but I don’t have a reason other than the differences listed by the the author: https://github.com/tolitius/mount/blob/master/doc/differences-from-component.md

qqq17:06:26

@U4PUTN69G : in other words, is your concern with 'system' the main 'system' repo has to wrap all the libraries its using into components therefore, if the main 'system' repo is not up to date, you're forced to use old libraries ?

qqq17:06:00

thanks for the mount vs component link; quite an interesting read

gonewest81818:06:36

You’re welcome. Yes, basically that’s the concern.

qle-guen13:06:13

how to access a method from the super-class? JFrame inherits from Component and I need to access getWidth in Component

vsvn13:06:19

anyone uses apache camel?

lxsameer14:06:13

recently I found out that the avg number of commits on clojure itself, is about 10 per month. Comparing it to other langs like Golang, it is very small. I really want to know your opinion on this. how do you guys see the future ?

captainlexington15:06:06

As in, perhaps the small number of commits reflects waning momentum in the Clojure community?

mpenet15:06:11

clj dev is mostly lead by "core", and mainly behind closed doors, always been like this

lxsameer15:06:53

are there going to be new features this way ? how fast clojure is going to grow ?

ghadi15:06:36

clojure is not driven by adding features or growing the language outwards as an explicit goal.

mpenet15:06:38

question is more do we need a crazy pace of addition

ghadi15:06:18

it is driven by solving problems of complexity tho...

captainlexington15:06:20

90% of what I use was in 1.0

ghadi15:06:32

spec is a Major Feature.

ghadi15:06:50

That isn't really available anywhere else in the same powerful form

mpenet15:06:13

that's another subject, a disscussion about spec/core.async design in the open might have been nice imho

mpenet15:06:55

but as long as the outcome is/was good it's not so important, arguably

captainlexington15:06:15

I mean, I don't know much about golang, but two languages I know of that have gone through major revisions in the last few years C++ and JavaScript

captainlexington15:06:32

And these revisions addressed (or attempted to address) longstanding complaints about the language by its users

captainlexington15:06:45

I just don't know of any such complaints about Clojure

mpenet15:06:51

golang repo includes all the compiler dev + some of the tooling, I guess if you add openjdk/oracle-java stuff to clojure it'd be (sort of) close. I like golang a lot (use it at work happily) but I am not sure the longstanding complaints are getting addressed 🙂 (hi generics, not that I care personnally)

mpenet15:06:51

golang and clojure are actually kind of similar about the conservative way of (not) adding features

ghadi15:06:03

golang also has a compatibility promise...

mpenet15:06:42

Something missing imho is really open design discussions (confluence exists but it's kind of lacking), missing the days where rich was available on irc from time to time to answer questions directly too

seancorfield16:06:33

But how many people are actually qualified to have those design discussions? When the Clojure community was very small, it attracted a lot of experts (or at least senior devs willing to work on the bleeding edge). Now the community is much bigger, and some of those original folks have moved on (for whatever reasons), it would be much harder to have those discussions “at large”.

seancorfield16:06:21

Look at the mailing list — compare the quality of discussions from six years ago about new features and/or changes with the discussions of the last year. You can clearly see the shift of expertise level.

seancorfield16:06:35

That’s to be expected as a language community grows over time.

bronsa16:06:57

clojure-dev is still mostly self-selecting as not many know about it :)

mpenet16:06:32

I was thinking about something like PEPs

seancorfield16:06:58

I think we have a great core language and a strong, stable set of contrib “standard” libraries and any major new features are going to require a huge amount of hammock time. Clojure is a higher quality language because of that.

seancorfield16:06:44

@bronsa Heh, true, and those design discussions do indeed happen on the -dev list, amongst those who’ve signed CAs and asked to join.

mpenet16:06:31

point me to the links about c.async/spec discussions 🙂 ?

captainlexington16:06:47

And even major new features of late, e.g. async and spec, are really just libraries that ship with the language. They didn't involve any (major) changes to what was already there

seancorfield16:06:00

Someone mentioned C++ earlier — that was (and still is) designed by an ANSI Standards Committee and revisions (or at least regular reviews) are mandated by the ANSI process.

mpenet16:06:04

one day, bang here it is

bronsa16:06:29

they both evolve with community feedback tho

seancorfield16:06:34

(I was a voting member of the ANSI C++ Committee for eight years so I’ll claim reasonable insight into the process)

seancorfield16:06:25

The community feedback on spec was sufficient to drive the decision to split it into an alpha library to evolve separately from Clojure itself.

mpenet16:06:37

I am not sure it's what drove the split. Having it separate is good, but it's unrelated to what I mentioned (initial design).

mpenet16:06:59

But I get, the "it's rich baby" and that s how he likes to deal with it. I just wish it was more open.

jsa-aerial16:06:45

So, how do you feel about, say, Python in this regard?

rgorrepati17:06:43

Does transit differentiate between longs and integers?

noisesmith17:06:30

1, (int 1) and (byte 1) all serialize to 1 in the output, I just checked

alricu18:06:45

hi all, Is there any tutorial or help about how to secure an API (Rest webservice) with a Certificate?

weavejester18:06:40

@alricu There might be something on nginx, which might be easier than using certificates directly with Clojure.

alricu18:06:42

I have a REST webservice working in Clojure; but I do not know how to secure it!!! It has to be something with certificates

weavejester18:06:12

The Ring Jetty adapter does have client certificate support.

weavejester18:06:50

Though in general web services tend to be secured not through client certificates, but HTTPS + a username/password or a token system like JWT.

weavejester18:06:16

Or OpenID for that matter.

weavejester18:06:09

If you’re starting from scratch and have no preference, what about HTTPS (so just normal, server-side certificates), combined with basic auth.

weavejester18:06:27

I believe Buddy has some middleware for HTTP basic auth

alricu18:06:51

mm the problem is that the webservice is going to be used by other backend services

alricu18:06:09

so I something that does not use passwords or usernames!

alricu18:06:25

for that reason I was thinking about certificates

weavejester18:06:09

client-side certificates might be a good solution, then. There may be a tutorial about how to add them to nginx, which could prove to be a more general solution than one specific to a Ring adapter.

adamvh18:06:27

is there a function (transform-map m f) that returns a map with the function f applied to all the values and the same keys?

adamvh18:06:10

or do i have to roll my own with seq, map, and ... whatever is the inverse of calling seq on a map?

noisesmith18:06:10

@adamvh it’s built into a lib called specter

wiseman18:06:22

@adamvh (reduce-kv #(assoc %1 %2 (f %3)) {} m) or (into {} (for [[k v] m] [k (f v)]))

noisesmith18:06:32

or you can do it via (into {} (map (fn [[k v]] ...) m))

noisesmith18:06:56

or, to use a transducer (into {} (map (fn [[k v]] ...)) m)

nathanmarz19:06:19

those code snippets change sorted maps to unsorted maps and are quite inefficient https://gist.github.com/nathanmarz/b7c612b417647db80b9eaab618ff8d83#file-results-clojure-1-7-0-clj-L28

nathanmarz19:06:03

with specter: (transform MAP-VALS f m)

noisesmith19:06:45

@nathanmarz any reason not to show into with a transducer arg for comparison there?

noisesmith19:06:02

(I assume it would be similar to reduce-kv perf… but don’t know)

nathanmarz19:06:15

that's in the benchmark

nathanmarz19:06:21

it's much slower than reduce-kv

noisesmith19:06:22

I don’t see it

nathanmarz19:06:37

the last one

nathanmarz19:06:37

(into {} (map (fn [e] [(key e) (inc (val e))]) data))

noisesmith19:06:42

the into call shown isn’t using a transducer

noisesmith19:06:01

that paren next to data would need to move one to the left

nathanmarz19:06:07

oh my bad, couldn't read my own code

noisesmith19:06:18

yeah, transducers are tricky that way

nathanmarz19:06:19

i'll add that to benchmark

nathanmarz19:06:31

but i think it would be much slower than reduce-kv

noisesmith19:06:38

:thumbsup: - I;m sure specter is still faster, just curious

nathanmarz19:06:46

just cause it has to wrap key/value pairs in []

ghadi19:06:00

or write a mapentry transducer and apply it to the reduce-kv example

(defn map-vals [f]
  (fn [rf]
    (fn [result k v]
      (rf result k (f v)))))

(reduce-kv ((map-vals inc) (fn [m k v] (assoc m k (inc v)))) {} data)

nathanmarz19:06:08

@ghadi you mean (reduce-kv ((map-vals inc) (fn [m k v] (assoc m k v))) {} data)?

nathanmarz19:06:00

that seems to be the same as (reduce-kv (fn [m k v] (assoc m k (inc v))) {} data) but with an additional layer of indirection

nathanmarz19:06:22

here's benchmark with transducer:

Benchmark: transform values of a small map (500000 iterations)

Avg(ms)		vs best		Code
68.193 		 1.00 		 (transform MAP-VALS inc data)
87.001 		 1.28 		 (map-vals-map-iterable data inc)
126.68 		 1.86 		 (reduce-kv (fn [m k v] (assoc m k (inc v))) {} data)
129.62 		 1.90 		 (map-vals-map-iterable-transient data inc)
142.09 		 2.08 		 (persistent! (reduce-kv (fn [m k v] (assoc! m k (inc v))) (transient {}) data))
175.33 		 2.57 		 (reduce-kv (fn [m k v] (assoc m k (inc v))) (empty data) data)
270.34 		 3.96 		 (into {} (map (fn [e] [(key e) (inc (val e))])) data)
387.06 		 5.68 		 (zipmap (keys data) (map inc (vals data)))
420.07 		 6.16 		 (into {} (for [[k v] data] [k (inc v)]))
438.79 		 6.43 		 (into {} (map (fn [e] [(key e) (inc (val e))]) data))
475.66 		 6.98 		 (transform [ALL LAST] inc data)

********************************

Benchmark: transform values of large map (600 iterations)

Avg(ms)		vs best		Code
116.18 		 1.00 		 (persistent! (reduce-kv (fn [m k v] (assoc! m k (inc v))) (transient clojure.lang.PersistentHashMap/EMPTY) data))
121.99 		 1.05 		 (persistent! (reduce-kv (fn [m k v] (assoc! m k (inc v))) (transient {}) data))
125.07 		 1.08 		 (transform MAP-VALS inc data)
139.34 		 1.20 		 (reduce-kv (fn [m k v] (assoc m k (inc v))) (empty data) data)
141.92 		 1.22 		 (reduce-kv (fn [m k v] (assoc m k (inc v))) {} data)
147.69 		 1.27 		 (into {} (map (fn [e] [(key e) (inc (val e))])) data)
148.12 		 1.27 		 (map-vals-map-iterable-transient data inc)
172.44 		 1.48 		 (map-vals-map-iterable data inc)
190.33 		 1.64 		 (into {} (for [[k v] data] [k (inc v)]))
195.83 		 1.69 		 (into {} (map (fn [e] [(key e) (inc (val e))]) data))
222.11 		 1.91 		 (zipmap (keys data) (map inc (vals data)))
245.87 		 2.12 		 (transform [ALL LAST] inc data)

nathanmarz19:06:53

about 4x slower than optimal for small maps, 30% slower than optimal for larger maps

adamvh19:06:45

specter seems dope thanks @nathanmarz 🙂

adamvh19:06:29

do i have to do something special to use specter in cljs?

adamvh19:06:49

i put a [com.rpl/specter "0.7.1"] in my project.clj

nathanmarz19:06:59

well, for starters use 1.0.1

nathanmarz19:06:19

but no, don't need to do anything special

adamvh19:06:45

i was worried because lein deps downloaded a jar but i guess cljs knows what to do with those

noisesmith19:06:12

all deps are jars

noisesmith19:06:22

(as far as I’ve seen at least?)

plins19:06:50

this is a stupid question but i wasnt able to find any good explanation this far .. if i can use an atom to hold a map (hence, any number of data), why id use a ref ? theres any intrinsic disadvantage in storing all state inside a map? (besides the fact that my code can get more organized if i split things up) is there some situation where is clearly better to use separated refs and use dosync? im asking this because i think ive seen a cljs app storing all app state inside a (atom {})

noisesmith19:06:31

if you have high contention for alteration, separate refs will perform better

noisesmith19:06:47

but if you don’t hit that scenario, atoms are definitely simpler

noisesmith19:06:04

I’ve never had code that was using a mutable container agressively enough for refs to improve performance, but I’m sure there are scenarios that exist

john19:06:17

CLJS doesn't have refs, so you'll see all state managed with atoms there

john19:06:54

If you have two atoms representing two different bank accounts, how do you transfer money from one account to another while ensuring that no party ever sees an inconsistent view of who has what money when?

john19:06:39

Two refs can be updated in an coordinated fashion so that before, during and after the transaction both states are always providing a consistent view of their states to all parties.

hiredman19:06:42

javascript is single threaded

hiredman19:06:39

if you are using core.async in cljs, the easiest thing to do is to put all read/writes of accounts behind a single go block, again serializing all actions

john19:06:18

@plins oh, and refs can also hold maps.

adamvh19:06:22

how would specter replicate clojure's group-by, or is that orthogonal to its purpose?

rinaldi19:06:30

With get, is there a way for me to throw an exception when a key is not found instead of simply providing a default value as the second argument?

john19:06:49

Not that I'm aware of, though you could check for the default value afterwards and throw there or pass it through

hiredman19:06:18

I mean, of course there is a way

hiredman19:06:54

given get, you can write a function that uses get and throws an exception if it returns the not-found value

rinaldi19:06:36

Makes sense, just wanted to ask more experienced people before going for it. Thanks.

noisesmith19:06:24

peregrine.circle=> (.getValue (find {:a 0} :a))
0
peregrine.circle=> (.getValue (find {:a 0} :b))
NullPointerException   clojure.lang.Reflector.invokeNoArgInstanceMember (Reflector.java:301)
one way to do it without a new function

noisesmith19:06:07

not a very helpful error htough

hiredman19:06:43

.getValue is val

plins19:06:24

thanks everyone for the insightful info 🙂

hiredman19:06:35

even if you are using core.async in cljs, core.async on javascript is cooperative, if you don't do any channel takes or puts, you don't yield control to other go blocks

hiredman19:06:10

so if you read from one account and write to another, without async operations in the middle, it will execute effectively atomically

nathanmarz20:06:55

@adamvh that's orthogonal

nathanmarz20:06:40

specter is for when you want to change a piece of a data structure and leave everything else the same

hcarvalhoaves20:06:56

@rinaldi there's a good reason why get returns nil instead of throwing an exception on missing key though

rinaldi20:06:33

@hcarvalhoaves Yeah, I'm sure there is. Was just wondering if that would work for my use case.

hcarvalhoaves20:06:15

with exceptions many idioms fall apart - like or, some->, etc

adamvh20:06:38

i see - so in my case i would run group-by and then use specter to transform my newly-grouped map

adamvh20:06:31

hmmm sadly it seems like spectre has its arguments in the wrong order for swap! but c'est la vie

adamvh20:06:34

oh but it has ATOM for that, nice!

joeharrow20:06:34

Hey there, any one have and advice for getting Environ to work on boot? I'm pretty new to clojure, so probably something I'm missing, but I can't seem to get it to work. The issue might actually be setting the right environment in boot.build, but I'm not sure.

nathanmarz21:06:41

@adamvh yea I do something similar in a few spots

cap10morgan21:06:02

anyone have any clever tricks for converting, filtering, or preventing non-EDN from getting into strings produced by pr-str? especially regexes. (e.g. (edn/read-string (pr-str #"foo")))

cap10morgan21:06:28

producing them via pr-str is NOT a requirement if there's a better way

noisesmith21:06:06

you could use clojure.walk/post-walk to replace all inputs that are not in the whitelist of supported types with a placeholder

noisesmith21:06:07

so that [:foo :bar #[object …]] could become [:foo :bar ::unreadable] or whatever

cap10morgan21:06:22

it really seems like there should be something built in for this...

noisesmith21:06:01

there are actual persistence libs, like transit

noisesmith21:06:12

which allows a per-class config for how to write/read

cap10morgan21:06:32

yeah but I want to use EDN here

cap10morgan21:06:46

it's like you can't safely generate EDN from clojure, which is... weird

noisesmith21:06:34

transit is a superset of edn - you can safely generate edn, but it’s up to you how to handle data that doesn’t belong in edn

noisesmith21:06:51

and walk/postwalk really makes this easy

wiseman21:06:42

there’s one jira issue for a function specifically to generate EDN