Fork me on GitHub
#clojure
<
2022-06-01
>
pez08:06:07

Why can't I use require in a function like so?

(comment
  (do
    (require '[clojure.set :as c-set])
    (c-set/union #{1} #{2}))
  )

(comment
  (->> ["1" "2"]
       (reduce (fn [acc v]
                 (require '[clojure.set :as c-set])
                 (c-set/union acc #{v}))
               #{})))
The first one works, but the second gives me:
; Syntax error compiling at (lab/sandbox_playground.clj:31:18).
; No such namespace: c-set

vlaaad08:06:05

should be different forms

vlaaad08:06:56

IIRC, aliases are resolved to namespaces when the form is read, but c-set alias is defined in the ns later, on eval

vlaaad08:06:55

woah, first one works? TIL

pez08:06:08

Not following about different forms?

Bart Kleijngeld08:06:21

When I copy/paste your second block of code in my REPL it works fine: (doesn't work indeed)

clj꞉myapp.core꞉> 
(comment
  (->> ["1" "2"]
       (reduce (fn [acc v]
                 (require '[clojure.set :as c-set])
                 (c-set/union acc #{v}))
               #{})))
nil  ;; output

Bart Kleijngeld08:06:01

Is that because it's in the REPL and not from file?

pez08:06:14

That shouldn't make a difference. Did you perhaps first run the do block?

pez08:06:05

Also, you should get #{2 1} or something like that back...

Bart Kleijngeld08:06:38

(I probably evaluated the comment 😉)

pez08:06:50

😂

😁 1
borkdude08:06:37

This is known as the gilardi scenario: https://technomancy.us/143

🙌 1
pez08:06:00

Encouraging title!

borkdude08:06:56

tl;dr you cannot introduce new aliases and use them unless you're in a top level form or top level do

borkdude08:06:11

but you can get around this by just using (resolve 'clojure.set/foo) and then use that

pez08:06:23

Awesome. And why is it called Gilardi scenario? There is nothing on the Internet, that I have found, that tells me that!

borkdude08:06:52

After the man, the legend, @U06C3GHJR who probably described it first?

borkdude08:06:21

I can't find any references on that, but I always assumed that to be the case :)

pez08:06:16

OMG. This is so awesome.

pez10:06:27

If someone runs into troubles unpacking the blog post about the Gilardi scenario, like I did, here is how to make the OP code work:

(comment
  (->> ["1" "2"]
       (reduce (fn [acc v]
                 (require '[clojure.set :as c-set])
                 ((resolve 'c-set/union) acc #{v}))
               #{})))

vlaaad10:06:37

Why not use requiring-resolve at that point? :)

pez10:06:00

I'm new to this. 😃 Yeah, that works too:

(comment
  (->> ["1" "2"]
       (reduce (fn [acc v]
                 ((requiring-resolve 'clojure.set/union) acc #{v}))
               #{})))

borkdude10:06:24

That works now because require is still synchronous in joyride :)

borkdude10:06:59

In the future you may have to do some promesa wrapping around this

pez11:06:57

Asked the question here for easier googling by future me: https://ask.clojure.org/index.php/11922/how-to-require-a-symbol-from-within-a-function Please answer 😃 🙏

pez14:06:45

I got another solution as an answer to that question. It was a bit unclear from the prose of the answer if it worked, but I tried it and it does work. A bit to my surprise...

borkdude14:06:22

That's a clever trick ;)

borkdude14:06:03

Hmm, not sure if it's supposed to work everywhere though

pez15:06:42

Can you explain the clever trick for me? 🥺

scgilardi15:06:13

Hiya! Here's my take: Macro expansion happens early in the process of evaluating the form. Macros take arguments and return "expanded" code. In this case, the expanded code is "nil", but in the process of producing that nil, there is a side-effecting function call to require (the function). The clever trick hooks into the evaluation machinery at a time early enough ("macro expansion time") to allow the alias reference to be properly resolved when it is compiled a bit later. Where the clever trick is a little bit iffy is that the task of understanding Clojure code is simpler if macros are purely functional: transforming "arguments in" (which may include "code as data" (e.g., &body)) to "code out". Calling functions for side effects inside a macro is not in keeping with a purely functional (easier to understand and test) style. However, in this case, the workaround macro is very simple and is intentionally used only for its single side effect. The potential for producing confusing/hard to debug behavior is very small.

🙏 2
pez07:06:26

Awesome. Thanks, @U06C3GHJR! It makes sense and I now learned about a part of the process that I had the wrong ideas about.

pez07:06:08

I now wonder why the answerer said this when I informed about my use case: > Sorry, but my hack with a pseudo-macro will not be useful in this case, even though it gives the good result in the REPL.

pez07:06:13

I tested it, and it does work in the Joyride script where I ran into the Gilardi scenario to begin with. clj-kondo doesn't like it though, but that is probably a configuration issue.

borkdude08:06:30

I'm surprised to be honest that it works in SCI. A better solution is to just use require + resolve imo

borkdude08:06:05

And like I said, require is going to be async so behavior is going to change here

borkdude08:06:24

You might as well prepare yourself for that with promesa right now

borkdude08:06:51

Even though it's still sync, treat the result from require as a promise

pez08:06:06

Can I do that change now, you mean, and be future compatible? Maybe move this part of the discussion to #joyride...

borkdude08:06:53

Ok ping me there

pez08:06:16

I'm not planning to use the pseudo macro thing for this. But I am curious about why it wouldn't work. Given what I now know about it, thanks to @U06C3GHJR, it seems it should work? At least in real Clojure and ClojureScript. SCI might be a different story, even though it is amazingly true to the ”real” things.

borkdude08:06:31

In ClojureScript it won't work

borkdude08:06:08

In bb it works because require is synchronous there

borkdude08:06:15

So it just depends on how you implement require

pez09:06:00

Thanks! Now I can rest about this. 😃

armed11:06:10

Is there any way to know whether a var is a macro? Something like fn? but for macros e.g. (macro? #'or).

delaguardo11:06:34

(:macro (meta #'or)) ;; => true

armed11:06:57

Cool, thanks!

delaguardo11:06:09

you can also use isMacro method of Var it defined just three lines below my link but then you make your code depend on internal implementation details

👍 1
jumar18:06:51

Aren't you also relying on implementation details when using the metadata?

Adam Helins12:06:25

Has anyone tried using Clojure in Excel using https://exceljava.com/docs/index.html ? Looks like an interesting idea

thinking-face 2
Takis_13:06:48

i am looking for clojure(or a clojure like languange) for python, i might prefer to use python data structures, and also to have easy interop, and easily intergrated with python projects

Takis_13:06:11

any suggestions has someone used any of them? or some other clojure-like language for python

didibus15:06:47

I tried both, but just toyed with them. Hylang is not really Clojure, its just a Lisp syntax for Python, everything else is Python, so the way you do things is the same as Python. If you're used to Python semantics and programming constructs over those of Clojure, its a good choice if you just want Lispyness. The Lisp has some limits, for example let doesn't exist, you only get function scope and global scope, no let scope, just like in Python but unlike in other Lisps. libpython-clj is my preferred one, but that's because I much prefer Clojure proper, and that lets you use Python libs from Clojure proper. Its straightforward, everything I tried worked, but I suspect some more esoteric Python libs might have issues, not sure.

didibus15:06:26

Since you said you prefer Python data structure, and want easy integration with Python, HyLang is your best bet.

Takis_18:06:22

thank you didibus i tried only libpython-clj, one test project, worked nicely clojure like. but i was curious if i can give the code i made with libpython-clj to a python programmer, to use it as library

Takis_18:06:47

its ok i guess trying them both is the best way thank you again

didibus21:06:00

I'd be surprise if you could, I think with libpython-clj you only get one way interop, Clojure can use Python, but not the other way around.

didibus21:06:59

In theory, there's also GraalVM polyglot stuff, and if you run Python there, it could make use of other things like Clojure, but you have to run everything with GraalVM including the Python, so it is not standard CPython

👍 1
Martynas Maciulevičius13:06:55

Hey. I found a macro when-class in clojure.core. It's not documented but it's probably used to find out whether a class exists. Can I use it and not import some classes?

delaguardo14:06:45

it is not documented because it is private. And I don't know how private macro can be used in user code

Martynas Maciulevičius14:06:43

Oh. Well I simply found it and didn't even try to call. But even then I could copy the source code. And what I wanted to know if it would be safe to use it.

delaguardo14:06:38

I don't know why it wouldn't ) it is just 4 lines of code so even in case of problems debugging should not be a massive headache

Martynas Maciulevičius14:06:02

Maybe. I was expecting the answer to be something similar to "the class may not be loaded if nobody imported it" or something like that. Because it's possible that the class may be marked as if it doesn't exist before anybody loads it. But I don't need this function now. Simply curious.

delaguardo14:06:52

you can use class without explicit import. Upon symbol lookup clojure will try to load it for you

Martynas Maciulevičius14:06:54

So does it mean that the macro will load the class?

delaguardo14:06:30

Byte code will be loaded, yes.

1
Martynas Maciulevičius04:06:08

I decided to use it. I need to parse some EDN and I'm migrating from joda time. This is my internal representation that I then use to bind serialization/deserialization methods.

(def serializers (merge {"mm/instant" [java.time.Instant java-time/instant]
                         "mm/local-date-time" [java.time.LocalDateTime java-time/local-date-time]}
                        (when-class "org.joda.time.DateTime"
                          {"clj-time/date-time" [org.joda.time.DateTime time-coerce/to-date]})))

nonrecursive14:06:41

what are the best tools for creating interactive documentation for clojure libraries? I’d like to be able to publish docs to github pages so that people can play around with examples in a browser. It seems like a couple options are: • klipse • scittle I also looked at Clerk and it seems like it doesn’t exactly get me there? It looks like you can publish notebooks but I don’t see that mentioned anywhere in the readme

sheluchin14:06:44

https://www.maria.cloud/ offers a very nice experience. Not sure how it's implemented, but might be worth checking out. But sorry, I think this is just clojurescript.

borkdude16:06:47

@U0AQ1R7FG You can achieve this with SCI + your library in the configuration. Let me know if you need any help with that. Possibly you can also use nextjournal's clojure-mode for the code editing.

borkdude16:06:24

And when combining all of this with clerk, it would be quite cool. Maybe a more out of the box experience would help. cc @U5H74UNSF

👍 1
borkdude16:06:44

This comes pretty close: https://twitter.com/hashbrown490/status/1530668983552077824 (read the thread, it's done with SCI btw)

borkdude10:06:54

@U0AQ1R7FG An example: https://nextjournal.com/try/borkdude/confetti It would be cool if you could have your own library in there (sicmutils is included here and you can play around with it) and just front-end, no backend, so it can be hosted on github pages (although maybe you could have the front-end load notebooks from a gist or so)

nonrecursive23:06:15

@U04V15CAJ thank you, this is very helpful!

plexus14:06:36

You can set up a build task to push them to a static site (e.g. gh pages), that's what Clerk does for its own docs. They're not normally interactive in the sense that someone can open the rendered notebook and play with the code. Nextjournal is a better fit for that.

👍 1
mkvlr16:06:36

clerk notebooks can be as interactive as scittle (as they also have access to sci which scittle is built on), and example is https://snapshots.nextjournal.com/clerk/build/814589de0fdc0cda80894d717a5d3a7f36332447/index.html#/notebooks/slideshow.clj. If your goal is to let folks interact with changing code samples, we’re not there (yet).

mkvlr16:06:02

but making Clerk more suitable to interactive docs is high up on our list

wontheone117:06:14

Hello folks, could anyone please answer this question of mine?

(new java.util.Queue)
=> Syntax error (IllegalArgumentException) compiling new at (/private/var/folders/w_/vx0dsld95z72bz6ml4txpgcw0000gp/T/form-init15671582189914750444.clj:1:1).
No matching ctor found for interface java.util.Queue
So I can’t instantiate a Java queue. I am just assuming that I have to give a type parameter. I actually want to instantiate Queue<AsyncContext> type. But I couldn’t find from anywhere how I would specify the type parameter part (`AsyncContext` ). Please share your knowledge with me 🙂

p-himik17:06:40

java.util.Queue is an interface, you can't instantiate it. You have to find a concrete class implementing that interface that satisfies your needs and instantiate that instead.

ghadi17:06:42

also, you do not need to instantiate with a type parameter

ghadi17:06:37

generics (the <AsyncContext> part) are erased in Java, meaning everything is really a plain Object under the hood

wontheone117:06:23

Thanks folks! Indeed. (new java.util.concurrent.ConcurrentLinkedQueue) this was what I needed! Have a nice day! @U2FRKM4TW @U050ECB92 I assume then there is really no syntax for type parameter in Clojure ?

ghadi17:06:24

correct

👍 1
ghadi17:06:30

and it is a fiction at the VM level

ghadi17:06:37

(It's a Java-the-language thing)

wontheone117:06:15

Got it 🙂 thanks again! Learned something new.

Ben Sless19:06:22

Tbf they're working on making generics real in the VM level

😱 1
Drew Verlee19:06:56

Is the reason the clojure.error/source is "NO_SOURCE_FILE" because i'm evaluating individual expressions at the repl? I'm not blocked on anything i'm just trying to sharpen my error reading skills and that bit seems obscure.

Drew Verlee19:06:37

how does it know the line and column but not the source?

#:clojure.error{:phase :execution,
                   :line 306,
                   :column 7,
                   :source "NO_SOURCE_FILE"}
       

dpsutton19:06:51

licenses=> (def x 1)
#'build.licenses/x
licenses=> (meta #'x)
{:line 16,
 :column 1,
 :file "NO_SOURCE_PATH",
 :name x,
 :ns #object[clojure.lang.Namespace 0x6634558a "build.licenses"]}

dpsutton19:06:56

just typing in the repl will do that

dpsutton19:06:17

and if you repeat the definition you’ll see the line number increases

👍 1
seancorfield19:06:16

I think the main REPL uses a LineNumberingPushbackReader, IIRC...

seancorfield19:06:28

https://github.com/clojure/clojure/blob/master/src/clj/clojure/main.clj#L368-L374 -- so it depends on how you start the REPL, but the default is to use LNPR.