Fork me on GitHub
#clojure
<
2022-05-19
>
pinkfrog00:05:05

When using the or part in map destructuring, one issue I am also facing is,

{:keys [a] :or {a 3}} {:a nil}
When a is nil, it won’t get the default value 3. How do you bypass this issue? Basically what I want to achieve is a guarantee that all vars have a default value. Users might accidentally pass in {:a nil} . This mostly happens when the user also receive a from upstream invocations.

Cora (she/her)00:05:33

(merge-with (fn [a b] (if (some? b) b a)) {:a 1} {:a nil})
;; => {:a 1}

Cora (she/her)00:05:10

it doesn't work with destructuring, though 😞

Cora (she/her)00:05:27

typically I just punt on this and say the caller shouldn't send the value if it's nil

pinkfrog01:05:55

while the or part has its value; but a or-some has its place too. (but unluckily there isn’t)

Cora (she/her)01:05:28

for sure! it's worth a feature request on http://ask.clojure.org

Luke Johnson02:05:23

What about just using an or in a let binding instead?

(fn [m] (let [a (or (:a m) 3)]...))

☝️ 1
Cora (she/her)02:05:50

I've done that as well, but if you have more than a few keys to do this with it pays to use something more general

didibus05:05:28

or-some could be a nice addition to destructuring, but I just want to point out I'm pretty sure the current behavior is on purpose. Take for example:

(get {:a nil} :a 10)
;> nil
(get {} :a 10)
;> 10
I believe it's because conceptually this allows you to model two different things, which normally with static structs or classes you can't do. Basically you can distinguish between isn't defined and is defined as nil. It can be the difference between say, we never collected this piece of info from the customer, to we asked the customer and they didn't fill in this optional field.

👍 1
didibus05:05:25

Generally the way I use it is that if you don't put the key, I default, but if you put the key as nil I throw a validation error, because that's most likely a bug that you specified the option but it was nil

emccue02:05:47

Is there a way to have :as-alias not cause a circular reference? like I only want to require a namespace for its alias, definitely not for access to its vars. (I haven't really confirmed this is an issue with compilation so there is a chance it works just not in cursive)

dpsutton02:05:21

/tmp/circular on :cloud:  (us-east-1) on :cloud:  
❯ echo '{:paths ["."]}' > deps.edn

/tmp/circular via :coffee: v11.0.11 on :cloud:  (us-east-1) on :cloud:  
❯ echo '(ns bar (:require [foo :as-alias f]))' > bar.clj

/tmp/circular via :coffee: v11.0.11 on :cloud:  (us-east-1) on :cloud:  
❯ echo '(ns foo (:require [bar :as-alias b]))' > foo.clj

/tmp/circular via :coffee: v11.0.11 on :cloud:  (us-east-1) on :cloud:  
❯ clj
Clojure 1.11.1
user=> (require 'foo)
nil
user=> (in-ns 'foo)
#object[clojure.lang.Namespace 0x4ed38226 "foo"]
foo=> ::b/b
:bar/b

Alex Miller (Clojure team)03:05:28

Shouldn't be a load cycle. What is the issue?

seancorfield03:05:08

@U3JH98J4R Is Cursive using tools.namespace? That was recently updated to treat :as-alias as non-loading wasn't it?

cfleming03:05:12

This is https://github.com/cursive-ide/cursive/issues/2690 which I’m planning to fix in the next build.

👍 1
henryw37412:05:19

According to the docs, print-dup, when true, means that objects will be printed in a way that preserves their type when read in later Is the following not strange then?

(binding [*print-dup* true] 
   (prn (java.sql.Timestamp. 1))) 

=> #inst "1970-01-01T00:00:00.001000000-00:00" 
Why does the default behaviour for date types subvert print-dup? When at the REPL, seeing things like dates printed with their type info is very useful: whilst conceptually I have a 'point in time' here, my code will be expecting a particular API (there is no common inst api, as there is for e.g. numbers). This contrasts with printing data to convey out of process ofc, where a more abstract representation will generally be preferable. btw if anyone reading this has no idea what I'm talking about, see:
(binding [*print-dup* true] 
   (prn {:a 1 :b 2})) 

=>  #=(clojure.lang.PersistentArrayMap/create {:a 1, :b 2}) 

henryw37415:05:37

🤷 well, it seems like a mistake to me. Added a comment to https://clojure.atlassian.net/browse/CLJ-2224 so hopefully we avoid this with java.time.Instant

wilkerlucio13:05:39

hello, I'm using the cognitect aws api (https://github.com/cognitect-labs/aws-api), but when I use commands to list data (like :ListBuckets or :ListObjects on s3) I always get an empty map as the response, I notice by looking at the meta that the responses are correct (I can see a XML string with all the correct data) but the response clojure map comes blank, how can I get the response as Clojure data?

ghadi13:05:09

can you paste what you evaluated?

wilkerlucio13:05:06

this is what I'm running

(def s3 (aws/client {:api    :s3
                     :region "sa-east-1"}))

(aws/invoke s3
    {:op :ListBuckets})
not sure about the data.xml, is this something that I need to have on my cp?

ghadi13:05:46

No but you might have a conflicting version

wilkerlucio13:05:55

in specific:

com.cognitect.aws/api                      {:mvn/version "0.8.539"}
             com.cognitect.aws/endpoints                {:mvn/version "1.1.12.206"}
             com.cognitect.aws/s3                       {:mvn/version "822.2.1109.0"}

ghadi13:05:18

can you look for data.xml in clj -X:deps tree

wilkerlucio13:05:05

org.clojure/data.xml 0.2.0-alpha6 coming from com.cognitect.aws/api 0.8.539

wilkerlucio13:05:36

com.cognitect.aws/api 0.8.539
  . org.clojure/data.json 2.4.0
  X org.clojure/tools.logging 1.2.1 :superseded
  . com.cognitect/http-client 1.0.110
    . org.clojure/core.async 1.5.644
    . org.eclipse.jetty/jetty-client 9.4.44.v20210927
      . org.eclipse.jetty/jetty-http 9.4.44.v20210927
      . org.eclipse.jetty/jetty-io 9.4.44.v20210927
        . org.eclipse.jetty/jetty-util 9.4.44.v20210927
    . org.eclipse.jetty/jetty-http 9.4.44.v20210927
      . org.eclipse.jetty/jetty-util 9.4.44.v20210927
      . org.eclipse.jetty/jetty-io 9.4.44.v20210927
    . org.eclipse.jetty/jetty-util 9.4.44.v20210927
  . org.clojure/data.xml 0.2.0-alpha6
    X org.clojure/data.codec 0.1.0 :superseded
  . org.clojure/core.async 1.5.644
    . org.clojure/tools.analyzer.jvm 1.2.1
      . org.clojure/tools.analyzer 1.1.0
      . org.clojure/core.memoize 1.0.253
        . org.clojure/core.cache 1.0.225
          . org.clojure/data.priority-map 1.1.0
      X org.ow2.asm/asm 5.2 :superseded
      . org.clojure/tools.reader 1.3.6

ghadi15:05:36

hi @U066U8JQJ, sorry for the delay. can you share the meta on the response?

ghadi15:05:53

transitive deps seem correct

wilkerlucio15:05:21

no worries, sure, the response looks like this:

{:headers
 {"server" "AmazonS3",
  "content-type" "application/xml",
  "x-amz-bucket-region" "sa-east-1",
  "transfer-encoding" "chunked",
  "x-amz-request-id" "C5DWX2XACRK2803D",
  "date" "Thu, 19 May 2022 13:01:53 GMT",
  "x-amz-id-2"
  "k/PwQWs+8VP4WcPbh6CB1B5599HokfnADoosgBam2b0hbgmA+o9vQT+PYF8C3vK0XUa8avvx6Q0="},
 :status 200,
 :body
 #object[java.io.BufferedInputStream 0x5a8a8e5f "java.io.BufferedInputStream@5a8a8e5f"]}

wilkerlucio15:05:50

I did slurp the body, which contains valid XML:

<?xml version=\"1.0\" encoding=\"UTF-8\"?>
 <ListBucketResult xmlns=\"\"><Name>pdf-extract-results</Name><Prefix></Prefix><KeyCount>10</KeyCount><MaxKeys>1000</MaxKeys><IsTruncated>false</IsTruncated><Contents><Key>2020-12/375603_OLGARIALIM...

wilkerlucio15:05:21

also, when I use the command :PutObject, it works as expected, and get data in the result (an :ETag)

ghadi15:05:48

can you try a different op like :GetBucketRequestPayment ?

ghadi15:05:56

or any of the :GetBucket* ops

wilkerlucio15:05:15

:GetBucketLocation works fine, :HeadObject is another one I also have correct results for

wilkerlucio15:05:33

seems something with the listing ops that get me empty results

ghadi15:05:35

so only errors with ListBuckets?

wilkerlucio15:05:56

ListBuckets, ListObjects and ListObjectsV2 are the ones I had problems so far

ghadi13:05:21

(also what version of data.xml you have on the classpath?)

delaguardo13:05:03

Why lists created using quotation contains meta data?

Clojure 1.11.1
user=> (meta '(1 2 3))
{:line 1, :column 8}
user=> (meta '())
nil

Alex Miller (Clojure team)13:05:11

The reader attaches location metadata to lists

delaguardo13:05:14

isn't it a little bit inconsistent? no meta for empty list. I know it is a special class but still strange to see

Alex Miller (Clojure team)13:05:39

the location metadata is used to do line number recording for compiled code and it needs to track function forms most importantly (and empty lists aren't)

🆒 1
Alex Miller (Clojure team)13:05:11

so, not trying to be consistent

delaguardo13:05:28

thanks for explanation!

emccue14:05:35

Most fun bug of the week (artist's recreation) The code works perfectly except it wasn't filtering out the results we expected it to

(defn get-stuff [{:keys [limit offset filter]}]
  (let [filter-pred (if filter 
                      (fn [thing] (check thing))
                      (constantly true))
        stuff       (fetch-stuff)]
    (->> stuff
         (filter filter-pred)
         (drop offset)
         (take limit))))

ghadi14:05:12

s/filter/pred/

potetm15:05:46

friends don’t let friends shadow vars

ghadi15:05:30

i shadow certain names routinely: key, val, and ref

ghadi15:05:53

STM refs stole the good name

😆 4
potetm15:05:54

Even core doesn’t (usually?) shadow key or val.

potetm15:05:49

key val and type specifically have all bitten me multiple times. It’s a little annoying, but I’ve found it easier to just consistently single-letter those.

p-himik15:05:37

Core routinely shadows things, including key and val. E.g. (source assoc). Other names occasionally being shadowed, after a very superficial glance: name, test, cat, next. And I'm at just 10% of the core.clj file.

potetm15:05:29

It also routinely does the opposite then 🙂

potetm15:05:55

There’s no reason to treat core as canon. I just happened to know offhand that key and val are very often abbreviated in there.

potetm15:05:36

Better put: k and v for key and val is really common in clojure code.

Alex Miller (Clojure team)15:05:45

scoping is a thing, shadow away

potetm15:05:16

I know that’s the official line, but then you have the above “fun bug of the week” which I assume consumed more than an hour of time.

potetm15:05:34

I know it’s cost me multiple hours.

Alex Miller (Clojure team)15:05:54

there are only so many good words, what are you going to do?

potetm15:05:27

usually, just abbreviate. k and v for key and val, and so on

potetm15:05:53

do it regularly so a pattern forms

potetm15:05:07

make your var names a little longer if necessary

wevrem15:05:17

shadowing key had bitten me many times. I have a new personal rule to never use it as a local name or as a keyword.

p-himik15:05:45

FWIW, I'm pretty sure that for me thinking about alternative names has consumed significantly more time than dealing with errors induced by shadowing. :) And after all, nothing prevents you from accidentally shadowing your own name.

potetm15:05:18

I basically spend zero time on thinking of alternative names.

potetm15:05:24

Just shorten it.

potetm15:05:56

You have scope. Somewhere in the scope, the full name should be provided.

potetm15:05:04

(e.g. {f :filter})

p-himik15:05:18

Sometimes it's appropriate, but not always. E.g the name can be a part of some API. Renaming that in code also requires effort, a constant one. Whereas I have to deal with shadowing issues once a year at most, and spend not more than half an hour on them.

potetm15:05:00

I dunno what you mean.

potetm15:05:30

{f :filter} is literally fewer characters than {:keys [filter]} , for example

p-himik15:05:01

It's not about the number of characters at all. We might simply have different preferences. I love short names, but the preferred length is usually a function of the size of the scope.

potetm15:05:29

I have no idea what you’re trying to say lol

potetm15:05:41

I’m not talking about naming vars.

potetm15:05:47

Is that what you meant when you said, “E.g the name can be a part of some API.”

potetm15:05:01

If so, yeah we agree that k is a terrible var name.

p-himik15:05:46

I'm talking about names in general. If you receive {:filter ...} from somewhere, you might use {f :filter ...} but it has its own cost. And for me, more often than not that cost is not worth it - so I tend to prefer {:keys [filter ...]}.

Alex Miller (Clojure team)15:05:47

seems like a place where editors could provide context

Alex Miller (Clojure team)15:05:10

function params in a slightly different color might have made that obvious

2
potetm15:05:35

Yeah cursive helps in fn position (core fns are in blue for me).

potetm15:05:44

But in argument position, you get no help

robertfw15:05:45

I'm with you on using short forms and avoiding shadowing. I find that it causes more trouble by tripping someone up than it solves by giving you some better names. I tend to use it only very rarely where a namespace is defining some similar function for operating on specific types of data (e.g., a map function that operates just like core/map but on some specific datastructure. malli.util is a good example of this providing some functions for working with malli schemas).

Cora (she/her)15:05:48

the best reason to shadow vars is that you get to call them shadow vars which just sounds cool 😎

😎 5
1
Ben Sless16:05:10

Send them back to the shadow realm 🙂

👀 1
borkdude15:05:20

> i shadow certain names routinely To clarify, I do too. I initially enabled the shadowed-var linter for myself, but could not get used to it. It's configurable though for the vars you want.

ghadi15:05:19

rules are made to be broken

Joshua Suskalo15:05:54

The most fun variant of this bug is the one where you're in the repl, observe the behavior, turn on CIDER's breakpoint debugger, and the problem vanishes. Turns out when cider compiles your code for debugging it will make symbols in function position ignore shadowing bindings.

wow 3
Joshua Suskalo15:05:28

made quite a few heisenbugs for me.

borkdude15:05:53

I guess you could shadow and append a character like * to represent the shadow (other characters may fit better)

Joshua Suskalo15:05:15

I think ' is the classic character for this application

upvote 1
potetm15:05:46

read: “prime”

borkdude15:05:04

Oh, yeah, let's do that :)

(let [inc' 10])
Oh wait ;)

nice 2
borkdude15:05:21

I guess pound characters are allowed outside of syntax quote as well:

(let [inc# 10])

Alex Miller (Clojure team)15:05:57

They are, but are reserved

Alex Miller (Clojure team)15:05:40

Actually I'm thinking of :

Alex Miller (Clojure team)15:05:59

But # is not listed as a valid symbol char

borkdude15:05:22

You got me there

borkdude15:05:55

I guess a trailing underscore also would work.

jjttjj21:05:08

I've been messing with a development workflow where I'm developing a Clojure program on a remote machine, through a local repl, using a java ssh library to connect to the remote machine and manage the (long running) clojure process there and also set up an ssh tunnel. I'm happy with this workflow when things work but I haven't figured out a great way to deal with unreliability, mainly my local networking going down and sometimes the remote clojure repl not running, and a lot of other little failure modes. Is there anything like a component library that tries to be smart about restarting unreliable components automatically? is the go to approach just have each component keep attempting to reconnect when necessary, and then use backoff/retry everywhere in the dependent components?

lukasz21:05:21

You can try mosh instead of SSH, last time I used it it was pretty great at keeping ssh connections alive

👍 1