Fork me on GitHub
#clojure
<
2020-10-09
>
nivekuil00:10:04

style question: (when-not (= ...)) or (when (not= ...))?

seancorfield00:10:42

@kevin842 For me it depends on what semantics I'm trying to emphasize for the when block: it is a block that executes when a condition is false when-not; or is it a block that executes when a condition is true when.

seancorfield00:10:57

So in the two specific examples you gave, the first says "avoiding executing this block when these things are equal" whereas the second says "execute this block when these things are not equal" -- does that semantic different make sense?

nivekuil01:10:55

yeah the intuition makes sense. I think it's a difference in pragmatics rather than semantics, by the way, as in https://philpapers.org/browse/semantics-pragmatics-distinction

nivekuil01:10:18

it's a frequently useful distinction :)

seancorfield02:10:39

Thanks. Added to my reading list.

FL00:10:47

Hi everyone, Is there a way to convert Java objects to Clojure data easily? We use a lot Java objects - pretty much every single ns - and parsing the data out of the Java objects adds a lot of unnecessary code and complexity. I was trying to use <ps://github.com/clojure/java.data|java.data>, but hit a wall pretty quickly, when I tried to define my hook to convert a field to Clojure data (by defining a defmethod on from-java, the library didn’t call my method, I probably got something wrong). Thanks in advanced. (Still quite new to Clojure and Java)

hiredman01:10:22

Usually I don't bother with "converting" clojure data is java objects and java objects can be passed around and used directly. If I do converting I often times lean heavily on the java reflection api and use it to generate all or most of the conversion code upfront (maybe a macro, but often just print out the clojure code and paste it in a file)

hiredman01:10:41

clojure.reflect is a thing, but I've always just used reflection directly

FL18:10:48

Hey there, I’d take a look at both, thanks!

seancorfield01:10:37

@franklai_slack happy to help with org.clojure/java.data since I'm the maintainer -- but as @hiredman says, it depends what you're trying to do: just using the Java objects through interop is often the best approach.

FL18:10:05

Hello Sean, thanks for replying. I guess in some sense parsing the java object isn’t much different from parsing a clojure map - both can be arbitrarily deep and retrieve by “name”.

plexus09:10:20

got an interesting question from a mentee, is there a particular reason why this yields a bigint?

(type (* (/ 1 3) 3))
;;=> clojure.lang.BigInt

3
Flo10:10:32

(nominator (/ 1 3)) (as well as denominator) are BigInteger internally.

Flo10:10:00

* of a BigInteger and a Long seems to be coerced to BigInt

Flo10:10:32

these are just quick observations tho, I don't know about the specific backgrounds for these circumstances. 🙂

plexus10:10:28

thanks! the nominator/denominator thing makes sense

plexus10:10:54

also interesting, multiplying two big numbers will automatically promote to bigint, but multiplying multiple numbers will integer overflow

user> (* 10000 10000000000000000000)
100000000000000000000000N

user> (*  1000000 100000)
Execution error (ArithmeticException) at user/eval41715 (REPL:202).
integer overflow

borkdude10:10:50

(* 10000 10000000000000000000 1000000000000000000 1000000000000)
100000000000000000000000000000000000000000000000000000N
Maybe this has to do with the reader automatically coercing big numbers into bigints

plexus10:10:09

hmmm yeah that makes sense

telekid12:10:17

whoa, that is subtle and unexpected

arohner13:10:33

I have a reify. I would like to conditionally make the instance implement a second protocol. Is there a way to do that without either defmacro or defining two different reifies?

arohner13:10:46

It looks like extend won’t work, because that wants a type rather than an instance

arohner13:10:24

oh, extend-via-metadata is an option now

Lone Ranger15:10:34

Does anyone happen to know if there is any prior art or papers that Rich based transducers on, or was this an "apple falling on the head" kind of thing?

Alex Miller (Clojure team)15:10:31

He believes it is new and spent some time looking for independent invention in papers but didn’t find it

Lone Ranger15:10:22

I believe it's new, too. It's kinda bannanas. Has he written any papers on them yet? And also is it accurate to say that they were first introduced to the world with Clojure 1.7 on June 30, 2015? Giving a talk about them soon and just trying to get my facts straight

schmee15:10:40

not sure when they were first announced, but “the” talk on transducers is from Strange Loop Sep 2014: https://www.youtube.com/watch?v=6mTbuzafcII

schmee15:10:52

can’t believe it’s been 6 years 😐

zilti15:10:06

Weird, because it seems like such an obvious, "basic" thing, at least in hindsight 🙂

schmee15:10:44

the key word here is “in hindsight” 😄

Lone Ranger16:10:08

yeah. It's really getting me that these aren't bigger news... this seems like a fundamental advancement in computer science.

Lone Ranger16:10:23

Oh, I guess it bears asking, I'm absolutely crediting Rich/Clojure with these but is there any licensing or anything that needs to be done or copyright acknowledgement that goes with them?

andy.fingerhut16:10:16

Are you asking about that for an independent implementation of transducers you are working on, or for copying and reusing Rich's code in another project?

andy.fingerhut16:10:38

If the latter, then Clojure's license allows that if the project copied into is also released under EPL 1.0, or some compatible license.

andy.fingerhut16:10:19

If you are working on an independent implementation of them, to my knowledge under US law that would only be an issue if they were patented, and I'm pretty sure they weren't (but not in a position to know for certain).

Lone Ranger16:10:07

It's the former. Just want to give credit where credit is due in whatever way is appropriate.

Lone Ranger16:10:49

As long as "independent implementation" means "direct ripoff in another language"

zilti16:10:04

It isn't bigger news because it's not Go or JavaScript. Because that's the only two stations the dev hype train services these days.

😂 3
😭 3
Alex Miller (Clojure team)16:10:37

Well we did also release impls in Java, Python, etc

Lone Ranger16:10:42

I get that and I realize it's not a buzzword or whatever but it is genuinely a new discovery in fundamental lambda calculus style work and I would think that the folks would be interested in knowing about it.

Lone Ranger16:10:58

Like for instance, if I didn't know about the concept of an "if" statement, I would hope someone would tell me.

Lone Ranger17:10:29

whoaaaa what the hell!! I didn't know these were available

borkdude18:10:16

There were some news items about transducers ending up in C++ (libraries?) as well at the time

vncz17:10:11

Hello people — I have been playing a bit with Clojure and Datomic, and I am trying to build a server that's just doing a query and store some data. I do have some questions and I'd also love to know if I'm going towards a good direction or not: 1. Is it ok to attach a Datomic Connection and Db Object directly on the request object? I have used the same methodology that's usually applied in Express https://github.com/XVincentX/clojure-playground/blob/10720f8/src/app/interceptors.clj#L18 — I was wondering whether it's a good idea and also — if I need both (I need a conn to transact and a db to query) 2. I am using the late symbol evaluation trick to be able to modify the code on the fly without having to restart the server per each change: https://github.com/XVincentX/clojure-playground/blob/10720f8/src/app/core.clj#L43 — Is that ok to leave it as it is in production or is it something I should really remember to remove when I am done? Is there a way to have the cake and eat it too? 3. I have a Spec for my data https://github.com/XVincentX/clojure-playground/blob/10720f8/src/app/data.clj#L31-L37 — but matching the keywords with a JSON Payload appears to be harder than I thought; right now I am manually reconstructing the object (https://github.com/XVincentX/clojure-playground/blob/10720f8/src/app/core.clj#L26-L30) but I was wondering whether there's a faster way. Also — if anybody sees anything weird while reviewing the code — be my guest! Thanks!

hiredman17:10:35

for #2 that isn't "the late symbol evaluation trick" that is var quote, and because you are calling handle-post inside a function (the #() form) it is not needed

hiredman17:10:15

every def creates a thing called a var, which is a little mutable cell, interned in a namespace, and given the value of the definition

hiredman17:10:32

when the compiler encounters a name like conj it (as long as the name isn't locally bound) generates code to look up the var and dereference it to get the value out of it.

hiredman17:10:50

and if a var with a given name already exists, def just updates its value

hiredman17:10:45

this is why redefinitions are possible. the top level names refer to mutable things, and when you redefine something the next time the mutable thing is consulted you get the new value

hiredman17:10:11

however if you do something like define you routes once, the var holding you handler will be dereference once when the routes are defined, and you won't see updates

hiredman17:10:02

which is why people use var quote there (vars besides being mutable cells are also functions that when invoked just invoke their values which is why this works)

andy.fingerhut17:10:04

Is the difference here due to using a Var name in a macro invocation, versus using it within a function definition?

hiredman17:10:27

I am getting to that

hiredman17:10:53

however, in the code in question, instead of using #'handle-post or handle-post in you route definition, you have a function, which will be invoked to handle every request, and every time it is invoked, a call to handle-post in its body will deref the var and get the new value

hiredman17:10:17

it isn't a macro thing, it is an argument evaluation thing

hiredman17:10:01

if you had a function create-route that takes a handler

hiredman17:10:30

you invoke create-route once and give the result to your webserver to handle requests

hiredman17:10:56

(create-route handler) the argument handler is going to be evaluated once, when create-route is called, which is once, which means you will never see changes to the value of the name handler

hiredman17:10:01

(create-route #'handler) passes the var to create-route instead of the value of the var, and every time the var is invoked as a function it uses the current value of the var

hiredman17:10:22

(create-route #(handler %)) passes an anonymous function to create-route, and every time that anonymous function is invoked (to handle a request presumably) the var #'handler is going to be deref'ed and the current value invoked

hiredman17:10:39

this is basically the same thing as the thing people trip over when using partial

hiredman17:10:14

user=> (def f +)
#'user/f
user=> (def g (partial f 1))
#'user/g
user=> (def f -)
#'user/f
user=>
given that, what is (g 1)

hiredman17:10:26

with

user=> (def f +)
#'user/f
user=> (def g  #(f 1 %))
#'user/g
user=> (def f -)
#'user/f
user=>
as the follow up

andy.fingerhut18:10:38

Is this in a published article somewhere, perhaps? Looking at the last example currently on https://clojuredocs.org/clojure.core/partial it seems that it addresses this issue, at least briefly.

andy.fingerhut18:10:09

I've just double-checked the code snippets in that http://Clojuredocs.org example, and it matches what I see, and appears to be describing the same issue you have above, so at least that is one reasonable place to point people at for some explanation of this.

andy.fingerhut18:10:45

It is a more general issue than partial, of course.

hiredman18:10:06

finding a place to hang this is tricky, because it is a consequence of state + the call by value lambda calculus

hiredman18:10:21

function arguments are evaluated once when a function is invoked, function bodies are evaluated every time a function is invoked

hiredman18:10:11

so many languages exhibit the same kind of thing

hiredman18:10:29

~ % lua
Lua 5.4.0  Copyright (C) 1994-2020 , PUC-Rio
> function add(a, b)
>>    return a + b
>> end
>
> function sub(a, b)
>>    return a - b
>> end
>
> function partial(f, arg1)
>>    return function (arg2) return f(arg1, arg2) end
>> end
>
>
> f = add
>
> g = partial(f, 1)
>
> f = sub
>
> g(1)
2
>

hiredman18:10:35

sicp makes the distinction between the substitution model and the environment model of evaluation when it introduces assignment

andy.fingerhut19:10:52

Understood. Specifically for http://ClojureDocs.org, which is Var-based, not "topic" based, one approach is to pick one representative Var where the issue often arises (e.g. partial), then briefly mention that example from other Vars where it also often arises, e.g. comp

andy.fingerhut19:10:57

Not ideal, I know.

andy.fingerhut19:10:25

But it makes it more likely that someone looking there will come across the issue.

Lennart Buit18:10:10

> I was wondering whether it’s a good idea and also — if I need both You’d want to resolve one request with one database value, even if you do multiple queries to compute your response. So IMO, its good practice to have a database value in your context. Putting in only a connection, which we sadly did, means that there is no consistency guarantee if you need to do multiple queries W.r.t. updating, you probably want to make sure that one rest/graphql/… call does exactly one mutation (for atomicity), after which you can use the transactions db-after to resolve your response. So yeah, good idea, and you do need both 🙂

vncz19:10:33

> W.r.t. updating, you probably want to make sure that one rest/graphql/… call does exactly one mutation (for atomicity), after which you can use the transactions db-after to resolve your response. @lennart.buit Thanks! Any chance you can elaborate more on this? Not too sure I am following

Lennart Buit19:10:49

If you have a REST endpoint that updates some entities, you want to make sure to use (preferably) d/transact exactly once. If you were to use it multiple times, the first could succeed, and the second could fail, leaving your database in a inconsistent state. (Thats the first part of the sentence)

Lennart Buit19:10:20

Datomic’s d/transact gives you a map which contains a db value (`:db-after`), that is guaranteed to include the changes you made in your transaction

Lennart Buit19:10:03

Because this is just a database value, you can use the existing code paths you have for querying (say, GET requests in a REST endpoint), and use that to create your response for the update request

Lennart Buit19:10:10

Does that help?

vncz19:10:29

Hmm hold on, I'll re-read

vncz19:10:08

Ok ok got it

vncz19:10:33

1. Modify all your data all in once 2. Use the :db-after in case you need to require after a successful transaction — is that what you were trying to communicate? @lennart.buit

vncz19:10:44

Now I just need to resolve the Spec problem and I should be all set! 🙂

Lennart Buit19:10:37

Concretely, if you spin out a function here: https://github.com/XVincentX/clojure-playground/blob/10720f8462611044945bdab20ef19eeb8b31dc77/src/app/core.clj#L17-L29 Say, get-person, that looks like this:

(defn get-person [db name]
  (ffirst (d/q '[:find (pull ?e [:person/name :person/surname])
                 :in $ ?name
                 :where
                 [?e :person/name ?name]]
                db name)
You can use it both with the db value in your context, or a :db-after value you get from transacting

Lennart Buit19:10:10

For example, because you decide that your handle-post also needs to return the newly created person:

(defn handle-post [conn data]
  (-> (d/transact conn {:tx-data
                       [{:person/name (get-in data [:person :name])
                         :person/surname (get-in data [:person :surname])}]})
      (:db-after)
      (get-person (get-in data [:person :name])))

Lennart Buit19:10:30

(There are some kinks to iron out here, I hope you get the idea 🙂 )

Lennart Buit19:10:38

Like — it doesn’t fit perfectly in this example with this person name, I’m just trying to show you how you could create small functions that accept db values, and how you can reuse them across routes 🙂

vncz20:10:36

Yes, totally makes sense

seancorfield23:10:05

Just randomly stumbled across https://github.com/cognitect-labs/fern -- seems useful. Anyone using it? Feedback on it? I notice @alexmiller added a PR recently to address the unqualified lib names thing from the recent Clojure CLI updates, so I'm assuming it's still actively being used, at least by Cognitect.

Ludwig00:10:13

I haven't used it, but here is a talk about it https://www.youtube.com/watch?v=uwdtTQ2ky5Q , maybe you find it interesting

seancorfield00:10:59

Thank you -- added to my list.

dangercoder19:10:10

I watched the talk and it looks interesting

Zor12:10:28

It looks like it was developped for https://github.com/cognitect-labs/vase

dominicm13:10:29

I'm a significant contributor to aero, so I made a contrast a little while ago. I really like fern, it addresses some pain points in an interesting way. Particularly I like the extensions mechanism that's suggested, as well as the partial keys that handle a good few problems in interesting ways. It lacks a significant set of useful things like environment readers. I don't think it handles "macro" style conditionals either, but there may be a subtlety above that, using the lazy keys system. I think it might have some legs though, I think it needs a little more progression to be useful out of the box.

Alex Miller (Clojure team)13:10:29

I believe fern and vase were projects spearheaded by Michael Nygard who's not at Cognitect anymore but still tinkers with them. I'm unsure whether they ever got used on Cognitect consulting projects (although those are all winding down)

seancorfield15:10:00

@U716Q56R2 Yup, that's what I said in the main channel after I asked about fern: "Looks like it was spun off from https://github.com/cognitect-labs/vase which is tied into Pedestal...?" @U09LZR36F Thanks for the input. I probably ought to have another look at aero at some point. @alexmiller Thanks. It's hard to tell how widely-used some of the Cognitect OSS projects are and how many actually made it into production. I was mostly curious since I saw your (relatively) recent PR...

👍 3
Alex Miller (Clojure team)15:10:17

that was just me finding and fixing things I have access to, so don't read anything into that

3
seancorfield15:10:07

Yeah, I get lots of questions about random Contrib libs because folks see my commits (that were just updates to readme or contributing files!). 🙂

seancorfield23:10:25

Looks like it was spun off from https://github.com/cognitect-labs/vase which is tied into Pedestal...?