Fork me on GitHub
#clojure
<
2016-09-25
>
machty01:09:41

I have a question that ties into a previous one I asked about whether encapsulation (a value being opaque or having private fields) might still be a useful concept for Clojure. As I understand, it takes some work (i think usually involving closures) to hide data from your consumers, so I was wondering, if you're a library maintainer and you want to be careful about the data you reveal (since by default everything is public and people might come to rely on "internal bits" of the objects you expose), is it common practice to strip returned objects of various keys/values for things you don't want to expose? or is there some other common practice?

seancorfield01:09:48

@machty I’m just not sure that’s much of a problem in real world Clojure.

seancorfield01:09:50

For a REST API, you might present a specific set of keys in a result map, but the chances are you’d be calculating at least some of those so you’d be proactively constructing the resulting map anyway.

seancorfield02:09:21

And if you do need just a subset of keys, select-keys is built-in so it’s easy.

seancorfield02:09:37

The really big difference between OOP and FP is that you mostly just stop worrying about encapsulation — your data is your API. In OOP, the reason you care about encapsulation is primarily to stop client code modifying stuff it shouldn’t touch. In FP, that is not relevant: the client can’t modify anything. So you only care about not exposing sensitive data (like passwords or certain IDs etc).

machty02:09:36

I understand; I think the big remaining issue for me is that even if the danger of willy-nilly mutability goes away, Clojure encourages a model where it's common, relative to OOP, to have lots of different parts of the code depend on a piece of data and how it is internally structured

machty02:09:33

you want to encourage reusability and hence you shy away from OOP encapsulation, but that comes at a cost where if you ever want to change that read-only immutable data blob, the chances that you'd be breaking something that depends on its internal format is much higher

machty02:09:30

this really isn't a criticism, but just a tradeoff that i have trouble convincing clojurists to acknowledge 🙂

machty02:09:55

and the concept of offering semantic versioning of a clojure lib got me thinking about it again

seancorfield02:09:51

Have you read Rich Hickey's pieces about clojure.spec, or listened to his interview on the Cognicast?

seancorfield02:09:31

Clojure maps are open to additive changes and only closed to subtractive ones.

seancorfield02:09:19

You're still thinking about data with an OOP mindset, it seems to me.

seancorfield02:09:54

To give you some background, I did FP back in the early 80's but learned OOP to stay employed with the rise of first C++ and then Java before moving to Groovy, then Scala (yay, some FP again), then Clojure since 2010.

seancorfield02:09:39

So I've had a couple of decades of OOP bookended by FP.

machty02:09:41

I haven't read/heard those

machty02:09:10

> Clojure maps are open to additive changes and only closed to subtractive ones.

seancorfield02:09:21

I was doing OOP for years before the "Gang of Four" patterns book appeared.

machty02:09:29

as a matter of taste/style/technique to not accidentally break other things?

seancorfield02:09:51

So I'm pretty familiar with the promised benefits of OOP and where it hasn't delivered too. And it did not bring the reuse it promised. Mostly because the element of reuse was too coarse-grained.

seancorfield03:09:18

I see far more reuse in Clojure because of composability and isolating (or removing) mutable state.

machty03:09:20

oh, i'm definitely not arguing that OOP reusability/composability comes anything close to Clojure's, just trying to understand the story/tradeoffs re: dependency management

seancorfield03:09:35

Based on my experience with both paradigms I'm comfortable asserting "the data is the API" simple_smile

machty03:09:40

which in my mind boils down to "i need to change this, is it safe to change this or am i going to be playing whackamole for an hour"

seancorfield03:09:48

If you add keys to a map, it's safe (as long as consumers operate idiomatically) -- hence my suggestion to read Rich's notes about spec and listen to the podcast.

machty03:09:01

gotcha, will give it a listen, thanks 🙂

seancorfield03:09:56

Consider adding keys to a map like deriving a class in Java. Anything that accepts the base map / class can accept the extended / derived one.

machty03:09:50

makes sense

machty03:09:00

i could probably read this elsewhere, but how early could clojure have achieved widespread adoption? are there performance reasons that mutable OOP took over (persistent datatypes being too slow for production tasks relative to imperfect but fast mutable paradigms)?

seancorfield05:09:09

Well, part of the problem back then -- in the 80's -- was that every university created its own FP language to explore the paradigm. Cfront E -- the first educational license of C++ -- was released in 1984. Haskell -- designed as the "common language" for academics -- appeared in the early 90's.

seancorfield05:09:49

So OOP with C++ was well-established by the time FP even had a shot at the mainstream.

seancorfield05:09:39

And that was an FP that was far-removed from Lisp (which of course had experimented with OOP thru CLOS etc).

seancorfield05:09:40

When Java happened, Haskell was still young and very niche. And Haskell didn't have a big US company pushing it.

seancorfield05:09:46

All the time chips kept getting faster no one cared about concurrency and parallelism. Then, suddenly, we hit a wall and had to go multi-core. And then concurrency and parallelism mattered. And it's hard in traditional OOP.

seancorfield05:09:26

So I think the answer to your question is that Clojure happened at the right time.

sofra05:09:24

[ANN] data-scope https://github.com/jsofra/data-scope - tools for interactively inspecting and visualizing data Hi all I build this little library inspired by Spyscope, I have been using it at work for quickly visualizing data in charts, it has been really useful so far.

cdine10:09:05

@sofra . that's pretty neat.

pythonruby12:09:27

hi, I am new to clojure and implemented (myor) with defn while the bulit-in (or) is implemented with defmacro. Below is my defn implementation:

pythonruby12:09:30

user=> (defn myor #_=> ([] nil) #_=> ([x] x) #_=> ([x & next] #_=> (if x x (apply myor next))))

pythonruby13:09:02

any specific reason that built-in "or" needs to be implemented with defmacro?

hans13:09:31

pythonruby: indeed there is a reason. or only evaluates its arguments until one is truthy. when you implement it as a function, all arguments are evaluated before your function is called.

hans13:09:59

pythonruby: this is why something like (or (some-function) (throw (Exception. "failed"))) works.

pythonruby13:09:19

@hans I see. I just tested it with (myor false false 3 4) and not with (myor false 1 (println 1 2) 3 4) so I cannot see the difference. Thanks.

keithsparkjoy15:09:07

@seancorfield thanks for pointing out the Cognicast about clojure.spec. I’m pretty new to Clojure and was excited to see such a fundamental problem (change, in general) being addressed in a simple, composable way. I love Rich’s idea of getting away from version numbers on libraries and just renaming them when they break compatibility (at least I think that’s what he was describing).

keithsparkjoy15:09:07

I found that post searching on “dependencies” because I’m trying to find some guidance around library dependencies, specifically on Clojure itself.

keithsparkjoy15:09:08

I’m building a little app with the current released version of Clojure (1.8). I’d also like to use a third party library on Clojars, which has a dependency on Clojure 1.4.

keithsparkjoy15:09:08

I’m curious about this both from a consumer perspective - how does one deal with these sorts of dependency clashes, but also from a library author perspective. If I author a library that only uses really basic Clojure features, should I depend on the earliest possible version of Clojure to broaden the available audience? I’m sure there’s a great article somewhere that talks about these sorts of things, but I’m having trouble finding one...

yonatanel16:09:06

Does anyone has experience with amazonica and kinesis? I keep getting empty records and putting records seem to work because I get a normal response, but I don't know how to verify that.

raspasov17:09:04

@yonatanel: I do a little bit

raspasov17:09:46

Amazonica is great but I eventually gave it up, bit the bullet and did raw interop - simply the examples in AWS docs are much more abundant and it's hard to beat that

borkdude18:09:38

@raspasov I did the same. Interop wasn’t that painful

seancorfield19:09:23

@keithsparkjoy: dependency on an early version of Clojure often signals a library that hasn't been updated in a long time. The consensus seems to be that the vast majority of Clojure shops tend to be only one or two versions behind the leading edge, although there are outliers. For libraries that really do want as broad an audience as possible it's worth supporting three or four versions back.

seancorfield19:09:12

For example, as of January 2016, over 90% were already on 1.7 while 1.8 was in prerelease http://blog.cognitect.com/blog/2016/1/28/state-of-clojure-2015-survey-results#clj

keithsparkjoy19:09:32

What happens if I pull in that lib along with its dep on Clojure 1.4?

keithsparkjoy19:09:47

Would that even work?

keithsparkjoy19:09:18

(sorry for the newb question)

hans19:09:37

is there a clojure function that combines remove and filter, returning two sequences - one for which the predicate is true, another for those where it is false?

dominicm19:09:16

split-with is so close, but not quite.

andy.fingerhut19:09:03

Yeah, group-by can be used in that way, e.g. user=> (group-by odd? [1 2 3 4 5 6]) {true [1 3 5], false [2 4 6]}

hans19:09:25

does group-by retain the order of elements?

andy.fingerhut19:09:04

Yes, and its doc string even says it does, interestingly enough.

hans19:09:12

cool, thanks!

andy.fingerhut19:09:26

(I say interestingly enough, because such behavior is not always promised in doc strings)

yonatanel20:09:57

@raspasov Did you use the client and producer libraries or the lower level streams api?

keithsparkjoy21:09:20

@hans careful though as group-by is not lazy...

hans21:09:40

@keithsparkjoy can't really be lazy if it returns a map 🙂

keithsparkjoy21:09:05

Hehe yep, I’m a newb and got taken in by that once before 🙂

seancorfield21:09:20

@keithsparkjoy: if you have a direct dependency on a more recent version of Clojure that will win over the transitive 1.4 version. But you can also exclude transitive dependencies on each library you bring in.

keithsparkjoy21:09:06

Thanks @seancorfield - that makes sense.