Fork me on GitHub
#beginners
<
2020-07-02
>
sova00:07:14

ns refer... i'm not sure what you mean @nsaritzky... externs? please provide an example

noisesmith00:07:16

@sova

(ins)user=> (doc ns-refers)
-------------------------
clojure.core/ns-refers
([ns])
  Returns a map of the refer mappings for the namespace.
nil

noisesmith00:07:14

it lets you introspect / reflect on what the visible bindings are

yokoyama.km01:07:15

I recently came across the var-set function available for vars. What are the practical differences between var-set and defing a var again?

seancorfield01:07:48

@yokoyama.km I have never seen var-set or var-get in any code I've seen. They operate on a Var -- so (var foo) or #'foo -- and that Var must already exist.

seancorfield01:07:02

And it must be a thread-local binding apparently.

seancorfield01:07:14

def creates a global:

user=> (def foo)
#'user/foo
user=> (var-set #'foo 42)
Execution error (IllegalStateException) at user/eval152 (REPL:1).
Can't change/establish root binding of: foo with set
user=>

seancorfield01:07:00

(just to show that var-set can't be used like def)

yokoyama.km01:07:17

Yes, I saw a mention of this in Clojure Applied (pg. 68) as the set function of a Var, but I haven't see any examples of its usage so far

yokoyama.km01:07:06

The doc states that "the var must be thread-locally bound", and the examples show its usage along with with-local-vars

seancorfield01:07:31

Interesting. Ah, that is also very rarely used I think...

yokoyama.km01:07:10

Yes, I've never seen vars used that way. Thank you, @seancorfield

seancorfield01:07:33

OK, here's the use case I guess:

user=> (def ^:dynamic foo)
#'user/foo
user=> (binding [foo 42] (println foo) (var-set #'foo 13) (println foo))
42
13
nil
user=> foo
#object[clojure.lang.Var$Unbound 0x18fdb6cf "Unbound: #'user/foo"]
user=>

seancorfield01:07:25

The var has to be dynamic, and you can only use var-set in a context where it has a thread-local binding.

seancorfield01:07:45

(thank you -- I've never had cause to figure that out in nearly a decade of using Clojure!)

seancorfield01:07:36

Updated the REPL session above -- I had a stray foo definition, so that's now a correct fresh REPL session.

seancorfield01:07:06

I can't imagine when I would ever use this tho' @yokoyama.km

andy.fingerhut11:07:38

I used var-set in some performance sensitive code where I was implementing some imperative pseudocode from a research paper, and it would have taken me many hours to change the algorithm into a different form: https://github.com/jafingerhut/cljol/blob/master/src/clj/cljol/ubergraph_extras.clj

andy.fingerhut11:07:11

@yokoyama.km ^^

andy.fingerhut11:07:48

I am not saying that is recommended way to write code from scratch, but it seemed appropriate in this case to me.

andy.fingerhut11:07:36

I believe it only uses var-set on vars created via with-local-vars IIRC

noisesmith14:07:44

usually when people want these semantics, I see them using atom in a let, but when the let isn't starting new threads, local vars and var-set do the same thing but faster

noisesmith14:07:18

I wonder how it compares to volatile! though

dpsutton02:07:56

I think I remember seeing it used in clojure core or maybe the server ns. Maybe to establish root bindings for the print vars?

dpsutton02:07:32

i'm misremembering apparently. i thought i remembered some thread-local stuff and a var-set recently. i try to read lots of code

bones02:07:43

Just wondering the best way to use a Java class (of my own) from Clojure at the REPL?

seancorfield02:07:02

@bones You'll need to compile it (using javac for example) and then make sure the folder where the .class file ends up is on your classpath when you start the REPL

bones02:07:46

Can I specify that folder within deps.edn easily enough?

bones02:07:32

as in, is the paths key the right location to specify that folder?

seancorfield02:07:41

@bones Yes, :paths should includes the directory where the class file(s) can be found.

seancorfield02:07:22

(I can't test that right because I don't have a Java compiler of the right version on my Windows box)

bones02:07:22

That’s all good, thanks for your help. If/when that file is stored in Github, do I then use the deps key to locate and use it?

yokoyama.km02:07:24

Thank you, @seancorfield, for investigating this further. Maybe with-local-vars has the same effect (and does not require the var to be dynamic)? I'll try it out on repl later

seancorfield02:07:06

@bones OK, fixed my local javac :

[email protected]:~/clojure/bones$ cat example/Greet.java
package example;

public class Greet {
        private String message;
        public Greet(String n) {
                message = "Hello, " + n + "!";
        }
        public String say() {
                return message;
        }
}
[email protected]:~/clojure/bones$ javac example/Greet.java
[email protected]:~/clojure/bones$ ls -l example/
total 4
-rw-r--r-- 1 seanc seanc 810 Jul  1 19:54 Greet.class
-rw-r--r-- 1 seanc seanc 172 Jul  1 19:54 Greet.java
[email protected]:~/clojure/bones$ clj -Sdeps '{:paths ["."]}'
Clojure 1.10.1
user=> (import 'example.Greet)
example.Greet
user=> (def x (Greet. "bones"))
#'user/x
user=> (.say x)
"Hello, bones!"
user=>

bones02:07:09

Oh whoa, that’s awesome. Thanks for that.

seancorfield02:07:14

Note that the current directory is the "root" for finding example.Greet as an example.

seancorfield02:07:43

(hah, you wouldn't believe how many attempts it took me to get that Java code to compile! I haven't written Java for years!)

bones02:07:06

Ah okay, so because you refer to it as example/Greet you can specify the current dir (`"."`) as the root.

seancorfield02:07:52

Yes, because the Java class name is example.Greet and it matches the directory structure -- like Clojure 🙂

bones03:07:24

How about that 👍

bones03:07:16

So does all that change once I want to fetch from Git rather than locally?

seancorfield03:07:25

You mean via a git dependency in deps.edn? You can't "depend" on Java source. You need to arrange for it to be compiled before you can use it from Clojure.

bones03:07:55

Ah okay, so is it safe for me to think :deps is for Clojure files and as for Java I’ll follow what you’ve exampled above? I also noticed you used import as opposed to require , is that also a Java specific thing?

seancorfield03:07:42

Yes, you require Clojure, you import compiled classes.

seancorfield03:07:00

[email protected]:~/clojure/bones$ rm example/Greet.class
[email protected]:~/clojure/bones$ ls -l example/
total 0
-rw-r--r-- 1 seanc seanc 172 Jul  1 19:54 Greet.java
[email protected]:~/clojure/bones$ clj -Sdeps '{:paths ["."]}'
Clojure 1.10.1
user=> (require '[clojure.java.shell :as sh])
nil
user=> (sh/sh "javac" "example/Greet.java")
{:exit 0, :out "", :err ""}
user=> (import 'example.Greet)
example.Greet
user=> (def x (Greet. "compiled!"))
#'user/x
user=> (.say x)
"Hello, compiled!!"
user=>

seancorfield03:07:44

(not recommended -- just showing it is possible 🙂 )

bones03:07:30

Gotcha! Thanks again

seancorfield03:07:26

And just to confirm -- above I rm'd the .class file before I started and

user=> (sh/sh "ls" "-l" "example/")
{:exit 0, :out "total 4\n-rw-r--r-- 1 seanc seanc 810 Jul  1 20:04 Greet.class\n-rw-r--r-- 1 seanc seanc 172 Jul  1 19:54 Greet.java\n", :err ""}
user=> (println (:out *1))
total 4
-rw-r--r-- 1 seanc seanc 810 Jul  1 20:04 Greet.class
-rw-r--r-- 1 seanc seanc 172 Jul  1 19:54 Greet.java

nil
user=>
so you can see the sh/sh call to run javac created it in the expected location.

bones03:07:44

Yip I did see that. Thats crazy stuff

jonoflayham03:07:09

Hi, all. Can anyone give me a hint on how to set the content-type of an S3 object put using aws-api? I’m clutching at straws now…

(defn- put-object [s3 bucket-name prefix content-string content-type]
  (aws/invoke s3 {:op      :PutObject
                  :request {:Bucket  bucket-name :Key prefix
                            :Body    (.getBytes content-string)
                            :Headers {"content-type" content-type}}}))
… does nothing for the content-type, which the AWS console tells me is stubbornly “application/octet-stream”.

raspasov07:07:14

use :ContentType

raspasov07:07:18

{:op      :PutObject
 :request {:Bucket      bucket-name
           :Key         k
           :Body        body
           :ContentType content-type}}

jonoflayham08:07:32

You star - thanks very much. Just in time for my demo!

stopachka03:07:56

Hey team, noob q: I am watching Rich's "Maybe Not" talk -- Really liked where he was going with the idea of separating the schema with what should be required. Has there been some progress on his exploration? Would love to read / watch along if there's usergroup that's discussing it, or try it out if there's a beta

stopachka04:07:43

veery cool, thanks Alex!

alexmiller04:07:33

kind of off on other things for the last few months but planning to get back to it soon

alexmiller04:07:51

also see #clojure-spec if you want to discuss

dstephens14:07:05

Has anyone had success using :parameters metadata in gen-class, I found this https://clojure.atlassian.net/browse/CLJ-970 after finding this example https://groups.google.com/forum/#!topic/clojure/Xv1pKATfP0c which I think suggests it is in place since 1.5, I'm on 1.10.0 My actual reason is a bit more complicated but the following was the simplest thing I could think to try

(gen-class
  :name worker-operator.beans.IntList
  :state state
  :init init
  :prefix "int-list-"
  :main false
  :extends ^{:parameters [java.lang.Integer]} java.util.ArrayList)

(defn int-list- [] [[] nil])
But it still seems to compile a class file like: public class IntList extends ArrayList { Which means the java .getGenericSuperclass doesn't work

alexmiller14:07:19

that is an open ticket, it has not been applied

alexmiller14:07:39

so I wouldn't expect this to work

dpsutton14:07:10

"it has not been applied" means there's a patch on the ticket though?

dstephens14:07:04

Ahh okay, thanks, I thought the 'affected versions' was when it had merged

dstephens14:07:23

Do you know if there is any alternative in the meantime or do I need to just write java?

alexmiller15:07:59

I don't know of any alternative

vincenz.chianese15:07:50

Hello people! I am relatively new to Clojure (mostly playing it during my nights) and I am really liking it so far. To be fair, I ended up learning it because I was really interested in trying out Datomic, and Java/Scala was not really an option. Anyway — I am coming from TypeScript and Haskell (just to give some minimal background) and I do have some questions that I would love to get clarified: 1. I fail to understand if it's a good idea or not to use an effect system in Clojure. I see blog posts saying that in general you just should not. Coming from Haskell (where it's almost mandatory) and from TypeScript (where I decided to use one, with very good results: https://dev.to/vncz/forewords-and-domain-model-1p13) I'd be interested in understanding if people generally use one or are ok with thrown exceptions here and there. I have read around that "error is just data" (at least in Datomic). I would be super ok with it but I haven't been able to see/understand if this is the best practice or if somebody does it and somebody just throws exceptions. Any clarification would be super appreciated! 2. Coming from mostly JavaScript/Node — non blocking the thread is really the thing. I can see that CSP are a thing (curiously enough, I did a presentation about how using CSP in JavaScript is totally possible and doable) and just like effects, I fail to understand what is the best practice. Go with channels whenever there's an async operation or just block the thread since the JVM can allocate another one if required and handle another web request. Thanks a lot!

noisesmith15:07:28

These are big questions. The problem with effects is that the underlying VM allows exceptions anywhere and clojure makes little attempt to hide or prevent access to the functionality of the VM. You can opt in and do it by convention, but that's not nearly the same as the promises a compiler makes.

noisesmith15:07:47

clojure's core.async can be used for coordination, but is not a general purpose tool for not blocking. In particular, IO or CPU intensive operations inside go blocks (the one context where core.async provides non-blocking semantics) can starve the thread pool and lead to pathological behavior

noisesmith15:07:27

core.async isn't a tool for non-blocking code, it's a coordination device that helps you code be clearer (and more likely correct) when some other aspect of your code requires asynchronous execution.

noisesmith15:07:15

I think it's best to think of async as a form of design / tech debt in your architecture, and core.async helps you pay less interest on that debt

noisesmith15:07:38

(I am suspecting my answers are too cynical now ... core.async can be used for making non blocking code, but is more often than not used wrong, because of incorrect assumptions about what it provides)

vincenz.chianese16:07:12

Thanks a lot for the clarification @noisesmith On the point 1 — in Node/JavaScript there's essentially the same problem that is — everything can throw and there's no way to know ahead of the time if not by inspecting the source code. The same goes with Haskell (sounds weird, but it is). It's possible to deal with it though, even throwing code can be wrapped, with the typical example being try { return Either.right(operation())} catch(e) {return Either.left(e)} ; That does not discourage me. I was more asking if there were other reasons for that 🙂

noisesmith16:07:41

so that's true, but without a compile time type checker, we run into another variataion of the same problem: you can opt into using Either, but there's no guarantee that you've used it correctly or haven't forgotten a value was an Either vs. the raw contents

noisesmith16:07:03

so once again, it's a convention, the compiler and vm won't help you except by blowing up when the path is hit at runtime

vincenz.chianese16:07:09

@noisesmith Ok let's put the situation in turn, what do you usually do? 🙂

noisesmith16:07:24

I usually use immutable values and operations that don't throw. When I use code that is prone to throwing, or values that mutate, I try to segregate them and write a different kind of code, that's more verbose and makes subtle errors less likely.

noisesmith16:07:04

I treat it as a problem of style rather than an enforcement of language features (I think Zach Tellman's book "Elements of Clojure" articulates this approach better than I could)

vincenz.chianese16:07:05

Yeah ok essentially keeping the side effects at the margin of the system

noisesmith16:07:19

precisely, doing that as much as possible

vincenz.chianese16:07:48

Yes, that's what I'm doing at the moment, seems like we're on the same page.

vincenz.chianese16:07:56

Thanks for the book title, I didn't know about this one. Very helpful!

ghadi16:07:21

almost no one uses effect systems or explicit monads

vincenz.chianese16:07:08

@ I noticed! I'd be interested in understanding why 🙂

noisesmith16:07:08

a core philosophical choice in clojure is that functionality isn't closed off, we are "consenting adults" and can use all underlying features of the vm

vincenz.chianese16:07:11

That's a good point effectively. Go use it if you want, nobody will prevent you that 🤔

noisesmith16:07:53

there's a trade off to the choice, of course

noisesmith16:07:29

I think Scala does a good job of demonstrating some of the costs of the alternate choice, when you didn't author your own vm

vincenz.chianese16:07:46

I do not know, I'm not familiar with Scala.

vincenz.chianese16:07:03

Thanks a lot for he answers, they all make sense and I thought the same — just wanted a confirmation 🙂

noisesmith16:07:32

the compiler breaks compatibility frequently, and is full of complex and hard to use constructs that try to enable strictness while also allowing access to the things users need in the vm

noisesmith16:07:01

there are some things that are enforced by the compiler, that have no reified existence in the bytecode

noisesmith16:07:31

which means that languages other than Scala can't easily integrate with all Scala code

raspasov20:07:10

This is a good discussion, I enjoyed reading it 🙂

raspasov20:07:34

In terms of dealing with exceptions not on the main thread, I remembered this blog post: https://stuartsierra.com/2015/05/27/clojure-uncaught-exceptions

kbosompem16:07:46

any reason why

(future (doseq [thing things] ....))
this would only pull the first thing ?

mss16:07:55

what are people using to manage static sites in clojure? looking for something extremely simple that supports hiccup with live reloading

beders21:07:28

Would this fit the bill?

noisesmith16:07:07

@kbosompem try using @ to deref the future afterward - if it blows up you won't see the exception until you deref

noisesmith16:07:39

and most likely if your doseq stops at the first item, there was a hidden error in the future that @ would expose

noisesmith16:07:21

(ins)user=> (def f (future (/ 1 0) (println "OK")))
#'user/f
(ins)user=> (realized? f)
true
(ins)user=> @f
Execution error (ArithmeticException) at user/fn (REPL:1).
Divide by zero

kbosompem16:07:17

@noisesmith thanks!

noisesmith16:07:16

@kbosompem if your code doesn't rely on accessing the return value of the future, often the best thing is to add a try/catch, and log from there on failure.

noisesmith16:07:41

remember that Error is not a subtype of Exception and will require a separate catch clause

lukas.block16:07:01

Hey, can someone explain what's the rationale behind using naming like var# instead of var inside of macros?

ghadi16:07:58

it's not a naming thing -- it's a special feature of syntax-quote https://clojure.org/guides/weird_characters#gensym

andy.fingerhut17:07:18

The 'special characters' section of the Clojure cheat sheet mentions this, with a link to more info, along with other special syntax in Clojure, and most of the core functions and macros: http://jafingerhut.github.io/

mss17:07:37

how would one dynamically re-evaluate a form required from another namespace?

noisesmith17:07:50

usually (require 'some.ns :reload) or (load-file "/absolute/path/to/some/file.clj") depending on where the ns came from

noisesmith17:07:09

most people just use editor shortcuts, but I think it's useful to know how to do this in a repl directly (if nothing else it means I can help people no matter what editor they use)

mss17:07:30

so that would ostensibly work in a repl, but doesn’t seem to in a command-line-launched/clj context

mss17:07:14

maybe I’m just doing things wrong

noisesmith17:07:29

if it's not working, the namespace wasn't loaded from the location you thought it was, using "load-file" usually forces the right thing

noisesmith17:07:51

or, the code that is using the definition from that ns captured the value in a var, and doesn't see updates to it

noisesmith17:07:32

(common issue with web servers and handler functins, solved by passing #'app to the server starting fn, rather than app)

mss17:07:37

yeah, I’ve requireed and aliased the namespace I’m trying to reload in my ns declaration. is that what you mean by capturing the value in a var and not seeing updates?

noisesmith17:07:38

the exercise function here sees changes when passed the var, but not when passed the functionitself

user=> (defn report [n] (println "this is execution" n))
#'user/report
user=> (defn exercise [f change-f]
         (future (dotimes [i 5]
                   (f i)
                   (Thread/sleep 100)))
         (Thread/sleep 200) (change-f))
#'user/exercise
user=> (exercise report
                 #(defn report [n]
                    (println "this is the new definition executing" n)))
this is execution 0
this is execution 1
this is execution 2
#'user/report
user=> this is execution 3
this is execution 4

user=> (exercise #'report
                 #(defn report [n]
                    (println "this is yet another new definition executing" n)))
this is the new definition executing 0
this is the new definition executing 1
this is the new definition executing 2
#'user/report
user=> this is yet another new definition executing 3
this is yet another new definition executing 4

noisesmith17:07:10

future / sleep are used in order to demonstrate a change mid exeuction

mss17:07:17

that makes sense. really appreciate the help

noisesmith17:07:04

of course never call defn inside another function like that, I'm just breaking rules in order to construct a clearer example

stopachka20:07:07

noob q: If I require [clojure.string :as str] I can still use the clojure.core/str function in the same namespace. How does this work?

stopachka20:07:53

(ns your.namespace.here
  (:require [clojure.string :as str]))

(defn foo [name] (str "hi" name))

ghadi20:07:35

when the compiler sees an unqualified symbol str it resolves to clojure.core/str, but if the compiler processes a namespaced symbol str/includes? it looks to see if there is an alias defined for the namespace part of the symbol

stopachka20:07:22

I see! Thanks : )

stopachka23:07:09

Hey team, noob logging question. I am trying to set up logging for google cloud. To do that, I followed their tutorial, and saw that I needed to configure a logback.xml file, which includes an appender that routes the logs over to gcloud https://github.com/stopachka/jt/blob/master/resources/logback-production.xml One thing I notice: 1. If I actually called it logback.xml, in dev it would try to send the logs to gcloud, which would fail So what I did: I called it logback-production.xml , and in my deploy, I run the following command

java -Dlogback.configurationFile='logback-production.xml' ... -cp jt.jar clojure.main -m jt.core
(This seems to have worked (don't see logs yet but am giving it some time before i look deeper.) (and this does work!) --- Main question: Is this the idiomatic approach to logging? Do people do it differently?