Fork me on GitHub
#clojure
<
2020-04-26
>
didibus02:04:50

It seems Clojure install guide doesn't reall walk you through to how to install Java

didibus02:04:22

And it appears however you install Java, the clj cli expects you to set JAVA_HOME

didibus02:04:53

Does that seem correct? If someone has Clojure installed and working, thus I can safely assume that JAVA_HOME is set as well ?

andy.fingerhut02:04:17

You are asking if it is possible to set up an environment that runs Clojure without setting the JAVA_HOME env variable? I would guess it is possible, at least. I do not know whether it is common to do so.

theeternalpulse02:04:18

I don't really install java, just download the jdk and set my bashrc to add the path to the bins

didibus02:04:26

Hum, not exactly. I'm asking if setting up Clojure requires you to setup the JAVA_HOME environment variable.

theeternalpulse02:04:07

clojure uses java, and java has to be on the path. I think java_home is a fallback used by many programs if java isn't available?

theeternalpulse02:04:46

through installation I mean

andy.fingerhut02:04:26

I am attempting to try out a fresh install of Clojure on an Ubuntu Linux system to confirm, but I am pretty sure I can follow the Clojure getting started steps, installing a JDK in a way that does not assign a value to the JAVA_HOME env variable, and the clj and clojure commands will still work. Will report back what I find.

didibus02:04:16

This is the code for the clojure command to find java:

# Find java executable
set +e
JAVA_CMD=$(type -p java)
set -e
if [[ ! -n "$JAVA_CMD" ]]; then
  if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then
    JAVA_CMD="$JAVA_HOME/bin/java"
  else
    >&2 echo "Couldn't find 'java'. Please set JAVA_HOME."
    exit 1
  fi
fi

didibus02:04:25

So it seems it first tries $(type -p java) which I do not know what that does. And if that didn't work, it tries to look for JAVA_HOME, I think that's correct, my bash isn't great

theeternalpulse03:04:44

yeah, if you didn't install java via some installer, where it sets the path so it appears callable, then it falls back to the java_home. I ran into this long ago with intellij tools

didibus03:04:00

Ok, type is a linux command, I'm guessing available on mac as well

andy.fingerhut03:04:55

On an Ubuntu 18.04 system where I had never installed Clojure before, but did have OpenJDK 11 installed via an Ubuntu/Debian package, and there was no JAVA_HOME variable assigned a value in the environment, I can follow the Linux install instructions on Clojure's get started page, and both clojure and clj commands run without complaint.

andy.fingerhut03:04:18

The output of type -p java on that system is /usr/bin/java , which is where the java executable is installed by the Ubuntu/Debian package

andy.fingerhut03:04:07

The bash command page describes what the type built-in command does.

andy.fingerhut03:04:13

buried within that many-pages-long man page 🙂

didibus03:04:49

Ok cool. I could probably use that logic too. Though it seems there is also a way using java itself: java -XshowSettings:properties -version which prints a bunch of info, one of them being where java is installed

hindol07:04:14

This assumes java executable is already in path, if that matters.

didibus07:04:13

Ya, my install instructions will mention that. I need it in the path for other reasons already.

hindol07:04:18

It is more common to expect JAVA_HOME to be defined and then figure out the path to java ($JAVA_HOME/bin/java).

hindol07:04:48

But I am thinking about Windows more. For Linux and MacOS, java will be available in path already. In Windows, I often had to add java to path manually.

didibus07:04:36

Ya, I think I remember that for Windows. I might add a fallback to JAVA_HOME in the future, but I think it wouldn't be too bad to ask to add java to the path. Especially in Emacs, you can add stuff to your path within your emacs config as well, so that might be a better way to go.

didibus03:04:00

Seems more reliable, I might go with that

andy.fingerhut03:04:12

If you are assuming bash exists, then type command exists, too.

didibus03:04:17

I think that will work on windows as well

andy.fingerhut03:04:10

Wndows without WSL tends to be enough different from macOS/Linux that it often needs it own way of doing things.

didibus03:04:51

ya, I don't know how much I should bother for windows. Do people even use Emacs on windows 😛

jjttjj03:04:53

I'm at least one 😊

didibus03:04:07

Haha, well, I'd like to support it. And I'll try, not sure how to test it though

hindol07:04:19

I use Emacs via WSL.

didibus07:04:36

Ok, through WSL I think that should work the same as linux so shouldn't be an issue

hindol07:04:40

Yes, it's the same as Linux.

andy.fingerhut03:04:29

I have before, but not recently. It exists.

andy.fingerhut03:04:58

If you do not polish/test/support Windows, you will not be alone 🙂

jaide04:04:09

Is there a simple way to have clj install the deps and do nothing else?

p-himik04:04:18

clj -Spath IIRC

jaide05:04:30

Awesome, thanks!

p-himik05:04:01

How can I debug "Error building classpath. Could not find artifact ... in central"? I'd like to know what the resolved dependency is that doesn't have its own dependency in central.

p-himik05:04:11

Found it manually, but it would still be nice to know if there's any other way.

solf10:04:12

Anyone knows what is this error when using aws-cli?

(def s3 (aws/client {:api :s3}))
(aws/validate-requests s3 true)

(aws/invoke s3 {:op :ListBuckets})

=>
{:cognitect.anomalies/category :cognitect.anomalies/fault,
 :cognitect.anomalies/message nil,
 :cognitect.http-client/throwable
 #error
 {
  :cause nil
  :via
  [{:type java.nio.channels.ClosedChannelException
    :message nil
    :at [.FillInterest onClose "FillInterest.java" 150]}]
  :trace
  [[.FillInterest onClose "FillInterest.java" 150]
   [.AbstractEndPoint onClose "AbstractEndPoint.java" 354]
   [.ChannelEndPoint onClose "ChannelEndPoint.java" 216]
   [.AbstractEndPoint doOnClose "AbstractEndPoint.java" 225]
   [.AbstractEndPoint close "AbstractEndPoint.java" 192]
   [.AbstractEndPoint close "AbstractEndPoint.java" 175]
   [.ssl.SslConnection$DecryptedEndPoint doClose "SslConnection.java" 1132]
   [.AbstractEndPoint doOnClose "AbstractEndPoint.java" 220]
   [.AbstractEndPoint close "AbstractEndPoint.java" 192]
   [.ssl.SslConnection$DecryptedEndPoint onFillable "SslConnection.java" 425]
   [.ssl.SslConnection onFillable "SslConnection.java" 305]
   [.ssl.SslConnection$2 succeeded "SslConnection.java" 159]
   [.FillInterest fillable "FillInterest.java" 103]
   [.ChannelEndPoint$2 run "ChannelEndPoint.java" 118]
   [org.eclipse.jetty.util.thread.strategy.EatWhatYouKill runTask "EatWhatYouKill.java" 336]
   [org.eclipse.jetty.util.thread.strategy.EatWhatYouKill doProduce "EatWhatYouKill.java" 313]
   [org.eclipse.jetty.util.thread.strategy.EatWhatYouKill tryProduce "EatWhatYouKill.java" 171]
   [org.eclipse.jetty.util.thread.strategy.EatWhatYouKill run "EatWhatYouKill.java" 129]
   [org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread run "ReservedThreadExecutor.java" 388]
   [org.eclipse.jetty.util.thread.QueuedThreadPool runJob "QueuedThreadPool.java" 806]
   [org.eclipse.jetty.util.thread.QueuedThreadPool$Runner run "QueuedThreadPool.java" 938]
   [java.lang.Thread run "Thread.java" 834]]}}

solf10:04:52

I think it might be some kind of SSL issue? But I have no idea how to solve it. I can list the buckets using aws-cli on the command line without issue

Crispin10:04:39

if I have a fully qualified symbol, how do I get a non-qualified symbol? I want to strip the namespace off.

potetm12:04:16

Whatever you’re doing here, it’s almost certainly not what you want.

potetm12:04:52

You’re taking something that’s very specific and has well-defined semantics and turning it into something unspecific with loose semantics.

potetm12:04:32

I’ve only ever seen people do this because they, they programmer, wanted things a certain way.

Crispin10:04:56

but I want the output as a symbol

sogaiu11:04:07

i guess not this?

(def a 'clojure.string/split)
(symbol (name a))

Crispin11:04:56

that works 🙂

Cameron12:04:13

I think that's the most straightforward way too lol as Symbol has

final String ns;
final String name; 
so (unless I'm forgetting some hack) there's no mutating the ns (EDIT: not that anyone would want to anyways, I'm just diving into 'what's the most hypothetically direct you could get with this'), you end up needing to create a new symbol. The fastest hypothetical situation one could hope for is just to directly get name , and directly call some sort of simple Symbol(string name) constructor Well, name in this case, name indeed will call
public String getName(){
	return name;
}
and (symbol ..) will call
static public Symbol intern(String nsname){
	int i = nsname.indexOf('/');
	if(i == -1 || nsname.equals("/"))
		return new Symbol(null, nsname);
	else
		return new Symbol(nsname.substring(0, i), nsname.substring(i + 1));
}
Granted, there's some extra checks there but there is no public constructor to directly give you Symbol(null,name) I don't believe, so that's your most direct constructor for this. And technically, yeah, there's some extra checks in Clojure's symbol and name that could be bypassed if you really wanted to by using java interop (assuming you know the type of everything, as that's the extra checks Clojure does; both symbol and name do type checks first and dispatch on that type of their argument to create the symbol or get the name, respectively) but the idea is the same; getting that new symbol will be some combination of grabbing the name , and passing it to a Symbol constructor, as done here. ...I have no idea why I'd dive into this, as you already have your answer and this is a small thing ahahahah I apologize. This sort of thing just interests me, as I just like when I can establish the 'limits' on something, and use that to feel comfortable knowing 'well, it doesn't really get better than this, so no matter what happens I don't feel like I can improve this solution, I can rest knowing I'm done thinking about this "forever"' Especially as someone who can end up bikeshedding too much without that sort of closure

sogaiu12:04:15

fwiw, i also looked at the Symbol.java file to see if there might not be some interop / obvious better way 🙂

potetm12:04:01

It’s not a single fn call for a reason, I think.

potetm12:04:12

You’re taking something that’s very specific and has well-defined semantics and turning it into something unspecific with loose semantics.

potetm12:04:50

It’s a good recipe for wasting time.

sveri17:04:40

Hi, is it possible to have a catch all declaration in a compojure route? Something like: (POST "/api/invoke/:rest-of-thing/" [rest-of-thing req] (invoke-post rest-of-thing req)) where rest-of-thingcan be any uri like foo/bar/baz or /buz/3/5/id?

sveri18:04:42

Nevermind, I just found the wildcard function. Just adding a * to the route will behave like I want: /api/invoke/* will match all routes that start with /api/invoke/

athomasoriginal17:04:50

I’m testing a function that uses a core.async channel. For the tests, I wrote a simple timeout-chan function. Here is a simple version of its implementation:

(let [in-chan  (a/chan)]
    (a/go (a/<! (a/timeout 1000))
          (a/close! in-chan)
  (apply function-using-async in-chan))
Ultimately: keep the channel open long enough for me to pass things in and than close it. The problem: If you pass n items to the channel, and want to be sure n items get through before the channel closes, the tests might pass sometimes and not other times Q: how are people handling scenarios like this?

phronmophobic18:04:51

relying on timeouts doesn’t seem what you want. what kind of process are you trying to test?

athomasoriginal18:04:24

I have a consumer function like

(defn consumer-fn [chan]
  ; take from chan 
  ; increment counter
  ; transform val from chan and return it
)
My goal is to test that the increment counter step in the above is working as expected

phronmophobic18:04:23

you shouldn’t need a timeout to check to if that’s working

phronmophobic18:04:00

for the test, you could use something like poll!

phronmophobic18:04:07

from a design perspective, it seems like consumer-fn might be doing too many things

phronmophobic18:04:37

typically, you try to separate the “flow” of your program from the “logic” of your program

phronmophobic18:04:13

which makes things easier to test, but more importantly, makes your system easier to reason about and easier to change

phronmophobic18:04:37

specifically, I would consider having your consumer-fn not take from a channel and then return a value

phronmophobic18:04:00

either take a value and return a value or take a channel and return a channel

phronmophobic18:04:18

preferably, taking a value and returning a value

athomasoriginal18:04:39

> either take a value and return a value or take a channel and return a channel This is a good point, one which I am keen to look into.

athomasoriginal18:04:11

I’m currently trying to understand poll! a little better

phronmophobic18:04:13

I guess poll! would make more sense if you’re taking a channel and returning a channel*

phronmophobic18:04:41

but since you’re returning a value, it might not be the right fit

markmarkmark18:04:26

if you want to put a certain number of items onto a channel, then onto-chan might make sense. you pass it a coll, and once all of the items from the coll have been put on the channel it will close the channel.

athomasoriginal18:04:01

For clarification, if I want the channel to close after n items have been taken off the channel, would it be better to use to-chan?

phronmophobic18:04:03

to-chan and onto-chan deal with putting items onto a channel. they won’t know about items being taken off of a channel

athomasoriginal18:04:09

So in the case of https://clojuredocs.org/clojure.core.async/to-chan where it says that: > closing when exhausted What does “exhausted” mean in this scenario?

phronmophobic18:04:20

pretty sure that means if the sequence passed to to-chan ends, it closes the channel

phronmophobic18:04:18

but the channel isn’t guaranteed to be closed, because you could potentially pass an infinite sequence to to-chan. (eg. (to-chan (range)))

markmarkmark19:04:00

if you pass a channel tht has no buffer into onto-chan, then the channel that is returned will only be closed once all of the items have been received

markmarkmark19:04:18

since the channel will be a synchronization point without a buffer

phronmophobic20:04:55

i guess it’s worth pointing out that there’s a subtle distinction between 1. putting n items on a channel and then closing the channel 2. closing a channel after n items have been taken off the channel as @U1XTUTPMY alludes to, the difference is most obvious when you have a channel without a buffer

athomasoriginal18:04:21

I have seen this pattern used in the wild:

(let [v _] (async/alts!! [chan (async/timeout 1000)]))
  (assert (nil? v))
  (is (= 5 (count v)))
  ; ...other assertions
If I understand the premise here correctly, this is just keeping the channel open long enough to give our items time to pass through the channel :thinking_face:

phronmophobic18:04:16

timeouts aren’t generally used for giving time for items to pass through a channel

phronmophobic18:04:55

they’re typically used if there is some time constraint that can’t be met

phronmophobic18:04:28

also, in a distributed system, you may send a request and the only way to detect failure might be to have a timeout.

athomasoriginal18:04:38

> they’re typically used if there is some time constraint that can’t be met Do you have an example off hand of a scenario that required this? Just trying to better understand your suggestion :thinking_face:

phronmophobic18:04:57

this is the classic example of using timeout (and some of the other goodies)

(defn search [query]
  (let [c (chan)
        t (timeout 80)]
    (go (>! c (<! (fastest query web1 web2))))
    (go (>! c (<! (fastest query image1 image2))))
    (go (>! c (<! (fastest query video1 video2))))
    (go (loop [i 0
               ret []]
          (if (= i 3)
	    ret
	    (recur (inc i)
	           (conj ret (alt! [c t] ([v] v)))))))))

phronmophobic18:04:27

basically, the goal is to return a response in 80ms or less.

phronmophobic18:04:36

if all the results have returned, don’t wait for the timeout, just return, otherwise, wait up to 80ms and return any results that are available

arohner18:04:37

Are classes not valid keys in a case?

Alex Miller (Clojure team)19:04:53

No, they are not compile time constants

arohner18:04:02

(case Integer Keyword "keyword" Integer "integer") 
=> Execution error (IllegalArgumentException) at spectrum.repl/eval27661 (form-init14549903441455335896.clj:514).
No matching clause: class java.lang.Integer

phronmophobic18:04:20

they are not. case promises constant time dispatch and I don’t think that’s possible with classes

didibus18:04:47

Hum, I think it might not, but it's also about the equality check here maybe

didibus18:04:18

Integer gets evaluated, but then it gets compared to the non evaluated clauses

didibus18:04:01

I don't think the reader can read a literal into a class that would make it equal, so probably not possible.

phronmophobic18:04:06

from the docs:

Unlike cond and condp, case does a constant-time dispatch, the
  clauses are not considered sequentially

didibus18:04:20

But maybe if you did (str Integer) and your clauses were strings?

phronmophobic18:04:55

the expression gets evaluated, but the test constants do not

phronmophobic18:04:17

also from the docs:

The test-constants are not evaluated. They must be compile-time
  literals, and need not be quoted.  If the expression is equal to a
  test-constant, the corresponding result-expr is returned

didibus18:04:07

Ya that's what I meant. So if you did (case (str Integer) ("java.lang.Integer" ...))

didibus18:04:11

It might work

didibus18:04:32

But there might also be a literal way to represent the class which I don't know off. Basically the evaluated result of expression gets compared to the read value of the clauses

phronmophobic18:04:50

maybe. you might have to do (.getName Integer) , but you’re probably better off using something other than case at that point

phronmophobic18:04:35

(get {Keyword "keyword" Integer "integer"} Integer) 

didibus18:04:31

What I'm not sure is if tagged literal are possible targets for case

Alex Miller (Clojure team)19:04:45

Tagged literals are a reader form, not a type

Alex Miller (Clojure team)19:04:56

They are read into objects

didibus19:04:19

I guess my question is, say I do a case on some custom object. Like (case (Person. "John") (#person "John" "John")) But I have a tagged literal for creating object of type Person. Would this now work with case? Since my clause is read into a Person object and my expression returns a Person object, would both object now be compared for equality?

didibus21:04:31

Do we know why? Is it that the reader literal doesn't happen before the case macro? Or is it because the object returned by the literal will then be passed to the macro in a printed form?

Alex Miller (Clojure team)22:04:19

No, person objects are not compile time constants

Alex Miller (Clojure team)22:04:40

They cannot be stored in bytecode

didibus22:04:35

Oh, does case compile to Java switch ?

didibus22:04:20

Anyhow, I see what you mean, it has to be a valid byte code literal

Alex Miller (Clojure team)22:04:30

It compiles to tableswitch bytecode

didibus22:04:04

So how is Symbol and Keyword handled? To they compile to strings in the byte code for the comparison?

Alex Miller (Clojure team)22:04:26

There is a lot of code to implement case - it is a nontrivial transformation and involves both a macro doing about half the work and the compiler doing more

Alex Miller (Clojure team)22:04:17

I don’t recall all of the details

didibus22:04:57

I see, I hadn't realized case* was a special form

potetm18:04:03

@arohner I believe those “classes” are resolved as symbols.

potetm18:04:08

(case 'Integer
  Integer :int
  String :str)

potetm18:04:16

In Clojure, you can’t refer to classes as primitives – they’re referred to through symbols.

👍 4
potetm18:04:40

So, to answer your question: I don’t think use case to dispatch as you would want.

arohner18:04:12

Thanks. I’ve made it work with symbols of classnames

potetm18:04:57

Curious what you did.

potetm18:04:27

(let [c Integer]
  (case (symbol (.getSimpleName c))
    Integer :int
    String :str))

Alex Miller (Clojure team)19:04:20

Classes are not constants (or unique - they are per-classloader) so you can’t use them as cases

Alex Miller (Clojure team)19:04:08

I usually get the name of the class and dispatch on that string

potetm19:04:42

Yeah. But I assume this would often be faster using a protocol. (I have zero data to back that up.)

Alex Miller (Clojure team)19:04:55

Yeah, depends exactly what you’re doing

eudis19:04:31

Whats the equivalent of a "rest" let destructure for map destructuring? For example, for list its simple: (let [[x & rest] [:a :b :c]] (println rest)) This prints (:b :c) as expected

eudis19:04:46

However, I can't seem to find how to do it for map destructuring

eudis19:04:24

so that: (let [{x :a & rest} {:a :a :b :b :c :c}] (println rest)) returns {:b :b :c :c}

eudis19:04:25

Doesn't seem to work 😞

eudis19:04:06

I want the object thats left after remove :a from the original map (ie the rest)

Alex Miller (Clojure team)19:04:16

That’s not how it works

eudis19:04:04

so {:a :a :b :b :c :c} (pick :a out and return rest operation) => {:b :b :c :c}

Alex Miller (Clojure team)19:04:01

You can dissoc :a from the full map in a second binding

eudis19:04:34

ah gotcha, thats what I need thanks!

Jakub Holý (HolyJak)20:04:49

Hi! I have a weird compile error: > Syntax error (IllegalArgumentException) compiling . at (src/../run.clj:7:28). > Unable to resolve classname: clojure.core$double@4d1947e7 in this code

(Math/round (months-between-approx from to))
When I remove the call to Math/round, it is OK. The fn is:
(defn ^double months-between-approx [^Instant i1 ^Instant i2] (/ .. 30.5))
Why? Is there a problem with the ^double type hint? But it works until I try to round it.... Thank you!!!

4
seancorfield20:04:37

Move ^double after the function name (so it's in front of the arglist).

seancorfield20:04:36

Putting metadata ahead of the function name causes it to be evaluated (to the clojure.core/double function) and then added as a :tag I think. Putting it before the arglists will do what you want.

seancorfield20:04:13

^ @holyjak

user=> (defn ^double foo [] 1.0)
#'user/foo
user=> (+ 1 (foo))
Syntax error (IllegalArgumentException) compiling . at (REPL:1:1).
Unable to resolve classname: clojure.core$double@5da7cee2
user=> (defn foo ^double [] 1.0)
#'user/foo
user=> (+ 1 (foo))
2.0
user=>

Jakub Holý (HolyJak)20:04:54

Thanks a lot! Frankly, I never remember where to put the return value tag. My reasoning was that if it was before the arglist, it would be metadata on the arglist, not on the function. I should have just checked the docs 😅

seancorfield21:04:28

And metadata on the arglist is exactly where you want it 🙂

seancorfield22:04:28

user=> (defn ^double foo [] 1.0)
#'user/foo
user=> (-> #'foo meta)
{:tag #object[clojure.core$double 0x187e2f02 "clojure.core$double@187e2f02"], :arglists ([]), :line 1, :column 1, :file "NO_SOURCE_PATH", :name foo, :ns #object[clojure.lang.Namespace 0x5b5babb "user"]}
user=> (defn foo ^double [] 1.0)
#'user/foo
user=> (-> #'foo meta)
{:arglists ([]), :line 3, :column 1, :file "NO_SOURCE_PATH", :name foo, :ns #object[clojure.lang.Namespace 0x5b5babb "user"]}
user=> (-> #'foo meta :arglists first meta)
{:tag double}
user=> 

seancorfield22:04:04

For a lot of things, it will work before the function name -- when the metadata is the same evaluated as unevaluated. And of course there are situations where you want the metadata on the function instead of the arglist.

❤️ 4
👍 4
Chris K23:04:10

Just found this: clojure bot for eval clojure code on slack. Maybe we can have this on our group? https://github.com/verma/clj-slackbot

seancorfield00:04:55

No, sorry, the Admin team are not granting tokens/integrations for any additional programs.

seancorfield00:04:50

The restrictions on the free plan mean that it would be very hard for us to grant integrations to some programs but then have to deny so many others.

seancorfield00:04:57

(and, to be honest, most remote code bots are open to all sorts of abuse, including causing them to spam channels and generally be rather annoying)

seancorfield00:04:01

And, hey, every Clojure developer already has at least one running REPL on hand to try out code, right?

Chris K00:04:21

haha yeah It wasn't like I wanted to have this on, but in case the admins didnt' know about this

seancorfield00:04:40

Hah, trust me, over the years we've been running this, we've seen most everything 🙂

Chris K00:04:24

yep that's true