Fork me on GitHub
#clojure
<
2020-01-23
>
Gobinath03:01:52

Hello All 👋 I have planned to attend https://inclojure.org/ on Feb14th & 15th and looking for some company 🙂

introom03:01:06

how do you coordinate the format with coworkers working with cursive and emacs ?

cfleming06:01:14

There’s a section in the doc about this: https://cursive-ide.com/userguide/eap/formatting.html#matching-indentation-to-emacs, what I just realised is that it doesn’t cover storing the settings in the project. To do that, under Preferences | Editor | Code Style select Scheme: Project.

mkvlr03:01:58

@i we have a styleguide.clj which covers and documents the formatting and we ask every teammember to configure his editor / autoformatting so there’s no changes when applying autoformat to this file

seancorfield06:01:11

Is there an HTTP client library where requests produce core.async channels? If not, would there be interest in such a thing? (and if not, why not?)

seancorfield06:01:11

Is there an HTTP client library where requests produce core.async channels? If not, would there be interest in such a thing? (and if not, why not?)

erwinrooijakkers09:01:01

I don’t know if such a library exists. I do know about https://github.com/mhjort/clj-gatling, the Clojure wrapper around Gatling where each step in the simulation can return a core.async channel. Probably a bit different use case (the http-kit call is in the go block - actually not completely sure if it’s a good idea to have a HTTP call in a go block? Is that different from I/O which is not recommended since it blocks the thread? I would think so actually). Maybe you can get some inspiration from there. I mainly wanted to mention that library cause I really love using it. Do you know about it? I would definitely be interested in such a library if it indeed a good idea to do http calls in a go block and it works reliably!

ghadi15:01:13

@seancorfield I have a http://java.net.http client for Java 11 that hands you channels of responses

ghadi15:01:32

and those responses might include a channel of bytebuffers, too!

seancorfield17:01:18

@U050ECB92 great! I'll take a look and see whether I think what I was doing adds any value over what your library is doing. I started this before I moved to jdk 11.

ghadi17:01:15

mine isn't public yet

noisesmith18:01:46

the aleph library includes a client, and uses ztellman's manifold lib that integrates cleanly with core.async channels > For HTTP client requests, Aleph models itself after clj-http, except that every request immediately returns a Manifold deferred representing the response. https://github.com/ztellman/aleph

seancorfield19:01:51

@U050ECB92 That explains why I can't find it 😆

seancorfield19:01:46

@noisesmith I want something lightweight that avoids dragging in a bunch of libraries.

ghadi19:01:43

anything that returns a CompletableFuture can be easily adapted into something that returns a channel

noisesmith19:01:28

and you can call put! in a callback arg, that can get you pretty far too

ghadi19:01:41

cognitect.http-client returns channels

seancorfield19:01:11

Sounds like enough options out there that I don't need to bother with my own! Thank you everyone for the feedback/info.

ghadi19:01:18

(But it’s not a general purpose http client)

ghadi19:01:05

I’m working on something more general that acts in a similar way

seancorfield06:01:21

(I was experimenting with this some time ago, as a wrapper around http-kit's client library, but haven't pursued it because I don't know whether it's a good idea and whether the support burden of creating a FOSS library is worth it...)

lispyclouds06:01:29

@seancorfield Wouldn't it help to contrib to a lib like clj-http? they have callback style async support https://github.com/dakrone/clj-http#async-http-request and this can be added? Less maintenance burden 🙂

lispyclouds06:01:58

https://github.com/r0man/cljs-http already does return core async chans with the same api too

seancorfield17:01:44

@U7ERLH6JX I don't do any cljs so I hadn't seen that. Will take a look thanks. As for clj-http, we're trying to move away from that at work because it's too "heavy" and we're stuck on an old version because newer versions break stuff in our code (and after spending a lot of time debugging it, I just gave up).

lispyclouds17:01:15

I totally agree on the heavy part and have pretty much switched over to https://github.com/martinklepsch/clj-http-lite Also it’s Graalvm friendly which the original one isn’t unfortunately

solf07:01:34

I have something like this:

(def a {:func some.ns/foo})
And I would like to print some.ns/foo docstring using a. How can I achieve that?

hiredman07:01:11

You have the value some.ns/func was pointing to, you don't have the var some.ns/func which is what the metadata is attached to

solf07:01:00

I see... maybe I could get all the vars in some.ns, compare their values with (:func a), and see which one matches

hiredman07:01:39

You can't get the namespace

hiredman07:01:35

To illustrate the problems with what you want to do ask yourself what should happen for

(ns a)

(defn f [])

(ns b)

(def g a/f)

hiredman07:01:55

In that case, a/f and b/g are bound to the same object, so assuming you could ask the object, which answer would it give?

solf07:01:24

My clojure/java is lacking here. A java class a$f?

hiredman08:01:36

In some cases, the compiler generates a class name which matches the var name for top level defined functions, but that doesn't address functions that are bound to vars in multiple namespaces, and functions creates other ways like via partial

solf08:01:53

Ah, I see. I somehow got something that works (at least in the REPL), but I guess it depends on how the initial some.ns/foo was created?

solf08:01:56

(let [s (-> a
            :func
            class
            str
            (str/replace "class " "")
            (str/replace "$" "/")
            (str/replace "_" "-")
            )]
  (meta (find-var (symbol s))))

hiredman08:01:40

And for example, I could delete the a namespace, and the function bound to b/g would still have a class of a$f, which doesn't exist anymore, and doesn't match the only namespace the function is bound to a name in

hiredman08:01:35

It is a result of a kind of fundamental mistake in thinking about the relationship between names and the values they access

solf08:01:38

Honestly, I'm very confused. I'm going to read more carefully https://clojure.org/reference/var and https://clojure.org/reference/reader, are those the correct topics to better understand this?

hiredman08:01:03

5 is a value, and might be bound to any number of names in a program

hiredman08:01:29

None of which are some how the true name of 5 which you can ask 5 about

hiredman08:01:49

Same for the string "foo"

hiredman08:01:57

Or any other value

hiredman08:01:11

Which functions are

solf08:01:07

Yes, I understand. I though that clojure would keep some data/metadata about in a about some.ns/foo. I think the better approach would be to simply add the the docstring when initializing a

hiredman08:01:36

The doc string lives on the var

hiredman08:01:03

So maybe you should use the var instead of the value pointed to by the var

hiredman08:01:29

#'some.ns/foo

didibus08:01:51

@dromar56 Try (:doc (meta (resolve 'some.ns/foo)))

solf08:01:10

I can get the docstring when using some.ns/foo, I wanted to see if I could do it using a var a defined with something like (def a some.ns/foo)

didibus08:01:09

When you mean a has value some.ns/foo you mean ypu store the symbol in a ?

didibus08:01:34

Like what do you get if you call (type a)

solf08:01:07

not the symbol, a and some.ns/foo would point to the same thing

solf08:01:20

type would get some.ns$foo

didibus08:01:26

Okay, so the function object itself

solf08:01:59

Yes. I managed to somehow do it like this

(let [s (-> a
            :func
            class
            str
            (str/replace "class " "")
            (str/replace "$" "/")
            (str/replace "_" "-")
            )]
  (meta (find-var (symbol s))))

didibus08:01:28

Right, its gonna be a bit hacky. I don't think there's another way.

solf08:01:33

But I'm not sure it works in all cases.

solf08:01:41

Yeah... at this point it's more for curiosity than anything ahah

solf08:01:51

I will just use some.ns/foo directly

didibus08:01:33

The generated class name will always be namespace-name$fn-name

didibus08:01:45

Except munge is called on it

didibus08:01:04

I think there is a function demunge that reverses that

didibus08:01:51

So you can get back the originating ns and fn name, make a symbol of it and then resolve that back

solf08:01:13

Oh thanks, didn't knew about demunge. It does make my hack look less hacky

didibus08:01:49

Yup, so this should do it: `(-> (class some-ns/foo) (print-str) (clojure.main/demunge) (symbol) (resolve) (meta) (:doc))`

didibus08:01:46

Alternatively, when you get the fn from the original var, you can copy the doc meta onto the fn meta or your new var

didibus08:01:33

As such: (def a (with-meta some-ns/foo (meta #'some-ns/foo)))

didibus09:01:11

And now you can do (meta a)

didibus09:01:32

Or if you prefer to put it on the Var:

(alter-meta!
 (def a some-ns/foo)
 merge (meta #'some-ns/foo))

didibus09:01:36

What's different between find-var and resolve? Given a fully qualified symbol?

deplect11:01:52

goodday/afternoon/evening, I am looking for the arcadia channel, clojurians working with clojure in the field of gaming?

deplect11:01:24

Was redirected here by someone mentioning I could find one here.

andy.fingerhut11:01:39

I do not see any channel/room here by the name Arcadia. You are welcome to create one, or perhaps search under some other likely names if you can think of any that someone else may have created.

borkdude11:01:15

@didibus resolve takes into account namespace aliases and class names:

user=> (require '[clojure.string :as str])
nil
user=> (resolve 'str/join)
#'clojure.string/join
user=> (resolve 'Exception)
java.lang.Exception

zilti14:01:56

Neat, I've just run into the filename length limit of my file system, thanks Clojure compiler...

ghadi14:01:36

Doing what?

zilti14:01:01

Compiling a namespace that uses a macro, which results in quite some nesting of functions

alexmiller14:01:44

yeah, there is a ticket about this

alexmiller14:01:56

core.match can get you there occasionally

alexmiller14:01:24

definitely something we'd like to address

zilti14:01:43

Also you'd think that decades into the 21st century, a modern file system like btrfs would allow for longer file names than 255 bytes...

ghadi14:01:07

❤️ btrfs, but I've been burned by it so many times 🙂

zilti14:01:55

Yea, me as well actually... One hard reset of the machine and it is often just unrecoverably dead

dvingo15:01:02

I have a basic question. Why don't I have to require clojure.string but I do have to require other clojure.* libs?:

$ clj
Clojure 1.10.1
user=> (dir clojure.string)
blank?
capitalize
ends-with?
escape
includes?
index-of
join
...
user=> (dir clojure.set)
Execution error at user/eval150 (REPL:1).
No namespace: clojure.set found
user=> (require 'clojure.set)
user=> (dir clojure.set)
difference
index
intersection
join
...

ghadi15:01:43

because clojure.core loads clojure.string, somewhat unnecessarily

ghadi15:01:49

(there's a ticket about this)

dvingo15:01:21

gotcha, thanks

alexmiller15:01:52

you should not ever rely on any namespace being loaded (except clojure.core, which is auto-required)

dvingo15:01:04

I figured, was just puzzled why that ns worked

borkdude15:01:50

(clj-kondo warns about not explicitly required namespaces since a release or two ago)

jumar19:01:15

I'm puzzled why this is failing on our CI server but works locally:

((fn [{:keys [cron]} {:keys [id]}]
                                                                (format "%s %s"
                                                                        id
                                                                        (.URLEncoder/encode  ^String (:timezone cron)  java.nio.charset.StandardCharsets/UTF_8)))
                                                              {:cron {:timezone  "Europe/Bratislava"}}
                                                              {:id 1})

;; on my computer (Mac OS)
;;=> "1 Europe%2FBratislava"

;; on the CI server (Linux)
;; Execution error (ClassCastException) at user/eval1458$fn (REPL:19).
;; sun.nio.cs.UTF_8 cannot be cast to java.lang.String

jumar19:01:49

I can see that the URLEncoder/encoded method is overloaded and compiler tries to call the other version (where string is the second arg) but I'm not quite sure I understand why...

bfabry19:01:52

I can't find a version of java where that function takes something other than a string

bfabry19:01:16

oh, whoops, 10

noisesmith19:01:26

where does cron get calculated?

jumar19:01:30

I'm using jdk 11

bfabry19:01:30

are you using 10 locally but 9 on CI?

jumar19:01:35

That might be the issu!

jumar19:01:40

I think the server is using jdk 8

jumar19:01:42

let me check

bfabry19:01:50

yeah that overload is only in 10

noisesmith19:01:00

you can avoid a bunch of issues like this by never using AOT

noisesmith19:01:09

(which is easier than you might think in clojure)

bfabry19:01:23

I think this particular issue would still happen (but still don't use AOT)

jumar19:01:07

Yes, it's java 8. @noisesmith what do you mean by not using aot; what would happen in that case?

jumar19:01:06

Yes, found that - thanks a lot @U050MP39D for the hint!

bfabry19:01:27

you're welcome

noisesmith20:01:41

@U06BE1L6T I might misunderstand the problem, but when you don't use aot, clojure won't generate method calls based on a different vm from the one that is used at runtime

bfabry20:01:36

the problem here was he was actually trying to call a method that simply didn't exist on the version of the jvm he was deploying to. there is no #encode(String, Charset) in java prior to 10

noisesmith20:01:09

OK - that's different

bfabry20:01:32

I've come across the issue you're talking about though and it does indeed suck

jumar20:01:51

Can you give a concrete example of such an issue?

didibus20:01:48

The issue is if you AOT with JDK 10 for example, the bytecode tries to call a JDK 10 class, even if that class exists on JDK8, when trying to call it AOT, it might fail, because JDKs ABIs are not always compatible from newer to older

didibus20:01:08

Generally, an old .class can call a new .class, but the other way around not always

didibus20:01:10

If you don't AOT, compilation is delayed, so your code will get compiled not on the JDK used in your build, but the one in Prod, which means JDK versions of all compiled code will match

didibus20:01:48

Just in general though, don't use a different JDK in development, CI, prod, etc. Make sure you run the exact same one, even from the same vendor

didibus20:01:17

That'd be akin to using different versions of Clojure in all three, its just prone for issues

jumar20:01:44

Yep, in this case that definitely make sense. However in some cases (on-prem) we have bunch of JDKs that needs to be supported and I generally just run it with JDK 11 (some other people with JDK 8 )

didibus20:01:45

If you need to support multiple JDK versions, then target the oldest one

didibus20:01:55

Thus have the oldest possible JDK in development

didibus20:01:10

And probably on CI, you want to have them all, so you can run your tests and all on each to be safe

jumar20:01:18

Yep, until you run into issues with module system and similar 🙂

jumar20:01:02

Anyway, thanks everyone for your input - I wouldn't think that this is a problem

didibus20:01:13

Well, ideally, you have multiple JDK on your desktop too, so you can test them all. I mean, that's just the burden you take to support them all.

didibus20:01:46

But still, Java normally goes to greater length to make sure old works on new, but not the other way around. Modules being the exception here :

didibus20:01:22

Same with Clojure versions by the way, not a lot of effort is made to have new work with old

didibus20:01:28

But a lot on old work with new

didibus20:01:26

Someone had recommended jenv once to make switching between JDK locally easier: https://www.jenv.be/

jumar20:01:15

Yes, I use that 🙂

pez21:01:34

I use sdk-man. Love it.

didibus22:01:04

cool, got two things to try

djanus20:01:54

What's the way these days to comment on tickets in the Clojure JIRA? I've created an Atlassian account, but it's denying me access, and I'm not sure how to proceed.

didibus20:01:49

All the Jiras are mirrored there, and you can comment on them and upvote

didibus20:01:12

That's also where you can create new issues, and if approved, an admin will create a corresponding Jira for it

alexmiller20:01:51

we are trying to reserve our (limited) jira accounts to those working on patches

alexmiller20:01:53

but if you have a CA and are doing so, happy to make an account

djanus20:01:32

Not yet, just wanted to chip in on DXML-52

roklenarcic21:01:17

is there a way to bit shift right a byte?

roklenarcic21:01:33

seems that it results in a long value

noisesmith21:01:36

@roklenarcic no clojure function returns a byte directly, for integral types they all return longs

noisesmith21:01:02

you can use interop, and with the right hinting etc. they can stay unboxed even, but it's not going to be the easy path

andy.fingerhut21:01:39

You can then call byte , I think returning a java.lang.Byte object, perhaps? Not sure whether that achieves your goal or not.

noisesmith21:01:00

of course nothing prevents you from converting

(cmd)user=> (byte (bit-shift-right 8 2))
2
(ins)user=> (type *1)
java.lang.Byte

roklenarcic21:01:03

the problem is that I’m bit shifting a negative value

noisesmith21:01:21

also, if you are doing this with one byte, often the real task is over a large number of bytes that get handled in various ways - check out ByteBuffer for arbitrary parsing of arbitrary offsets of that array, and the byte-streams library https://github.com/ztellman/byte-streams

roklenarcic21:01:25

so I get a massive long which will probably convert to 0xff byte?

andy.fingerhut21:01:20

depends upon the long value, and how you convert to a byte. Some ways of converting to a byte simply keep the least significant 8 bits and discard the most significant bits, so would not always convert to 0xff byte

andy.fingerhut21:01:04

Clojure 1.10.1
user=> (bit-shift-left Long/MAX_VALUE 8)
-256
user=> (byte (bit-shift-left Long/MAX_VALUE 8))
Execution error (IllegalArgumentException) at user/eval3 (REPL:1).
Value out of range for byte: -256
user=> (unchecked-byte (bit-shift-left Long/MAX_VALUE 8))
0

bfabry21:01:39

seems to work ok. dunno how good an idea it is though

user=> (byte (bit-shift-right 2r10000001 1))
64

noisesmith21:01:54

why would that be an issue? it looks like the expected usage of bit-shift-right to me

bfabry21:01:40

not saying it's an issue, saying it seems like it would work fine

Graham Seyffert21:01:59

Does anyone know when a future releases the thread that’s backing it? Is it once its Callable returns some result, or after the first time someone derefs it? (assuming of course that it wasn’t cancelled for some reason)

Graham Seyffert21:01:33

It seems like ideally after the Callable finishes executing… but maybe that’s up to the executor vs. the future itself?

noisesmith21:01:51

the thread is released as soon as the callable exits - it goes back into the pool in the clojure.core/future case

hiredman21:01:13

it is up to the executor, but I have never seen an executor that did it any other way

Graham Seyffert21:01:20

Yeah, that makes sense to me.

Graham Seyffert21:01:32

Just sanity-checking myself

noisesmith21:01:06

you can double check with jstack or Ctrl-\ to see what's really running

hiredman21:01:07

technically it is an ExecutorService, Executor doesn't actually have methods that return futures

hiredman21:01:37

Executor is a pretty great interface

Graham Seyffert21:01:37

> technically it is an ExecutorService true

Graham Seyffert21:01:42

> Executor is a pretty great interface There is always beauty in simplicity

Eamonn Sullivan22:01:49

Hi, I have a small app that makes a few Rest API calls, through which it obtains an IP address. I'd then like to exec an ssh command, replacing my current process. What's the best way to do that? clojure.java.shell seems to run a subprocess and tries to wait, so my little app just appears to hang.

noisesmith22:01:26

right, clojure.java.shell turns the output of a command into a string, so can't really do interactive

noisesmith22:01:51

but you can use ProcessBuilder to make a Process, and use the inheritIO method to let a terminal user interact with the resulting application

noisesmith22:01:05

this won't make the vm exit though - it just hands the terminal over to the child

bfabry22:01:28

you can't do the "replace the process" bit. the jvm doesn't provide the Posix exec call

noisesmith22:01:35

there's a one liner I've got somewhere, I can even exec vim, edit a file, then exit back to a repl

noisesmith22:01:03

the gotcha is you have to make the shell (which is reading form the same terminal) wait for the process to exit

Eamonn Sullivan22:01:45

@noisesmith, that sounds like a good workaround. Can you share the one-liner?

noisesmith22:01:54

grabbing it now

noisesmith22:01:45

@eamonn.sullivan

user=> (-> (ProcessBuilder. ["nvim"]) (.inheritIO) (.start) (.waitFor))
0

noisesmith22:01:53

that 0 is the exit code of neovim

noisesmith22:01:15

you can of course add more args to that vector, or run /bin/sh if you need redirects etc.

noisesmith22:01:03

the waitFor prevents the editor and the repl from fighting over the same characters on input

Eamonn Sullivan22:01:12

Awesome, thank you very much!

noisesmith22:01:11

you can even continue to run other things in other threads while that executes (of course you probably want to make sure they aren't reading or writing stdio)

bfabry22:01:00

another option I guess would be to ask the system to launch a new terminal application with the built up ssh command as its initial command. xterm -e ssh X.X.X.X or what have you

noisesmith22:01:53

yeah, that's windowing system / ui specific though, where the processbuilder just relies on stdio being connected to any terminal

noisesmith22:01:41

eg. it works inside tmux while sshd'd from another host

bfabry22:01:38

true. I'm still kind of amazed that your PB trick works. but it certainly does

noisesmith22:01:56

it's all files, right?

hiredman22:01:40

or you could just write an ed clone inside of clojure

noisesmith22:01:13

haha right - original usage was ssh, vim is "feature complete" for tty I figured

bfabry22:01:23

I guess. I just kind of assumed that when java says "inheritIO" it means inherit java streams for IO. not low level pass over the fd's. anyway it's cool

Gobinath23:01:12

"The fundamental unit of information in Clojure is data." https://medium.com/@lwhorton/why-clojure-script-e4a69cc5168a So, is that different in other languages? How?

andy.fingerhut23:01:19

You may need to ask the author that question. Most programming languages I would say deal with data. One thing that many programming languages almost always deal with is mutable objects, whereas Clojure makes it easy to keep the default as dealing with immutable values, including collections, while maintaining pretty good efficiency of making new collections with small differences from existing collections, without mutating them.