Fork me on GitHub
#clojure
<
2022-03-16
>
zackteo09:03:44

Hello, is there a way to do redirection for GET/POST requests with ring? Like I want to be able to temporarily preserve backwards compatibility such that GET/POST to /_crux/query to become GET/POST in /_xtdb/query . I know there is ring.util.response/redirect that doesn't seem to help for when it is a query. And I quite seem to get the handler needed unless I am to modify the library

magnars09:03:11

{:status 302 :headers {"Location" "/_xtdb/query"}}

zackteo09:03:24

Thanks! That pointed me to the right direction. Trying to figure out the body now

👍 1
zackteo10:03:42

Actually. After tinkering with it, I think in this method I still need to have the handler fn

zackteo10:03:49

(ring.util.response/redirect "/_xtdb/query) was just producing {:status 302 :headers {"Location" "/_xtdb/query" :body ""}} which isn't surprising i guess that's how ring works

walterl13:03:57

HTTP 307 is a https://softwareengineering.stackexchange.com/questions/99894/why-doesnt-http-have-post-redirect, but wouldn't help if clients don't handle it. How about just hooking up _xtdb/query's handler to the _crux/query route, or calling the former from the latter's handler? I.e. accept the request and handle the backwards compatibility server-side.

slipset13:03:17

Honest question: We tend to prefer requiring stuff with :as over using :refer or use. Why?

mruzekw13:03:09

To easily know where a function is coming from. This is why I like using import * as _ for lodash, so I know exactly where a function lives just by looking at its use

👍 2
noisesmith18:03:25

@U04V5VAUN it makes reading code without using an IDE or having 20 tabs open so much easier

❤️ 2
Alex Miller (Clojure team)13:03:07

both remove context from the function at the point of use

Alex Miller (Clojure team)13:03:21

if you just see foo ,you have no indication of whether that's in the current namespace or from elsewhere (and with use, you really need some level of static analysis to even tell where it came from). with refer you at least have a list at the top of your file

Alex Miller (Clojure team)13:03:24

for functions/macros acting as syntax, this removal of context can actually be a feature (like refer'ing >! from core.async or a defwhatever macro)

dpsutton14:03:04

Namespace prefixes aide me in both reading and writing code. Knowing where a function comes from at a glance is so nice. I really miss this every time i take another stab at using a scheme or other lisp. For writing, the prefix helps for autocomplete so much. And I just recently felt the pain of use when a var with the same name was in both namespaces. I copied a var from the test ns and put it into the main namespace, and then later went into the test namespace. Since it used :refer :all on the original namespace, I had a collision of var names. Super annoying. And this pitfall exists everywhere you use use. Changing one namespace could break consumers anywhere. It is a nightmare

dpsutton14:03:03

(I think that macro as syntax heuristic is really tasteful. Gonna incorporate that)

Alex Miller (Clojure team)14:03:03

a little bit goes a long way :)

dpsutton14:03:21

I never get dogmatic in my style. And its a good rule of thumb for how to decide when to use refer. So thanks 🙂

ghadi14:03:39

use CMD-Shift-Enter to post a snippet

dpsutton14:03:45

that’s an enormous block of text and I think you are asking for help making javascript code, which we aren’t really experts at here.

dpsutton17:03:31

Interesting discussion about identifying special forms in editor for highlighting, #{let fn letfn loop} all report :special-form true in their metadata despite not being special forms and not being in (clojure.lang.Compiler/specials).

🙏 1
hiredman17:03:06

if it has metadata it isn't technically a special form

hiredman17:03:19

special forms have no vars to hang metadata on

hiredman17:03:32

macros and special forms behave differently in regard to shadowing

dpsutton17:03:08

agree. so it sounds like anything reporting to be :special-form is lying almost by definition

hiredman17:03:36

special forms basically define an interface between the language and the compiler and it is not surprising that you might want some kind of facade over that, and macros are an attractive way to implement that facade

hiredman17:03:55

but yeah, you cannot substitute a macro for a special form and have it work the same in all situations

hiredman17:03:23

and you cannot really hide the "real" special forms anyway

dpsutton17:03:00

yeah. i think this is just for syntax highlighting. and someone pointed out some things were both a macro and a special form which caused a double take leading us here

dpsutton17:03:00

i don’t believe any of this is semantic and just started with thrown being highlighted as a macro.

hiredman18:03:28

it is a whole thing and a half

hiredman18:03:01

"special form" is not some much of a prescriptively defined notion, but something that just kind of arises out of writing a lisp with macros, the macros have to bottom out at some base language, and that base language needs to be understood by eval

Sam Ritchie18:03:33

Hey all; does anyone know how I can set a namespace, if I am calling into the clojure compiler from java? I am trying

Compiler.eval (RT.readString ("(in-ns 'user)"));
and seeing
Exception in thread "main" java.lang.IllegalStateException: Can't change/establish root binding of: *ns* with set

p-himik18:03:40

Don't remember all the details, but it requires some wiring around it: https://stackoverflow.com/a/70838660/564509

noisesmith18:03:43

my vague notion here is that *ns* is something that's designed to be part of a REPL context, and Compiler.eval is lower level, and doesn't have the kind of state tracking / session that a REPL provides and `*ns* is useful in

Sam Ritchie18:03:03

this came up because java was trying to call some clojure that, internally, ran (eval '(clojure.core/+ 2 3)), and threw a

Caused by: java.lang.RuntimeException: No such var: clojure.core/+

Sam Ritchie18:03:54

in this case it was because *ns* is clojure.core by default, and the java code first evaluated a require form that replaced the + definition

Sam Ritchie18:03:05

so my goal here is to change namespaces FIRST to user or something. checking that link now

hiredman18:03:30

yeah, *ns* isn't bound, so you have to bind it yourself

hiredman18:03:27

Var.pushThreadBinding(..make-map-of-ns-to-some-ns...);
try  {
...eval..
} finally {
Var.popThreadBinding();
}

hiredman18:03:34

I think ideally you wouldn't use Compiler.eval, RT.readString, or Var.* whatever, but would use the java api (https://clojure.github.io/clojure/javadoc/clojure/java/api/Clojure.html) to get a handle on clojure.core functions that do those things

Sam Ritchie18:03:45

hey, nice, that is better. I did get this working but let me give this a try

p-himik18:03:56

Using java.api.Clojure works only if you need to do something simple, like call a known function with known arguments. Without eval, you can't do even (+ 1 2).

hiredman18:03:23

eval is a function in clojure.core

p-himik18:03:56

And... calling it on (+ 1 2) will not complain about + being missing?

hiredman18:03:21

depends on the value of *ns*

p-himik18:03:50

Which you will need to bind, thus going to the very start of the discussion? :)

hiredman18:03:42

user=> (.invoke (clojure.java.api.Clojure/var "clojure.core/eval")
         (clojure.java.api.Clojure/read "(+ 1 2)"))

3
user=>

p-himik18:03:27

Alright, then I was doing something wrong during my previous experiments... Thanks!

hiredman18:03:59

user=> (try
  (.invoke (clojure.java.api.Clojure/var "clojure.core/push-thread-bindings")
           (.invoke (clojure.java.api.Clojure/var "clojure.core/hash-map")
                    (.invoke (clojure.java.api.Clojure/var "clojure.core/resolve")
                             (.invoke (clojure.java.api.Clojure/var "clojure.core/symbol")
                                      "clojure.core/*ns*"))
                    (.invoke (clojure.java.api.Clojure/var "clojure.core/create-ns")
                             (.invoke (clojure.java.api.Clojure/var "clojure.core/symbol")
                                      "whatever"))))
  (.invoke (clojure.java.api.Clojure/var "clojure.core/eval")
         (clojure.java.api.Clojure/read "(+ 1 2)"))

  (finally
    (.invoke (clojure.java.api.Clojure/var "clojure.core/pop-thread-bindings"))))
Syntax error compiling at (REPL:1:1).
Unable to resolve symbol: + in this context
user=>

Sam Ritchie18:03:40

my problem is that I was importing sicmutils.env which knocked out + from clojure.core., since *ns* was bound to clojure.core by default

Sam Ritchie18:03:10

so when I THEN tried to do (clojure.core/+ 1 2) from java, clojure.core/+ had not just been shadowed but basically knocked out

Sam Ritchie18:03:23

// Needed to allow creating new namespaces.
    Var.pushThreadBindings(RT.mapUniqueKeys(RT.CURRENT_NS, RT.CURRENT_NS.deref()));
    try {
      Compiler.eval(Clojure.read("(ns user" +
              "(:refer-clojure :exclude [partial zero? + - * / ref compare = numerator denominator])" +
              "(:require [pooty :as p] [sicmutils.env :refer :all]))"));
      Compiler.eval(Clojure.read("(println \"in\" (ns-name *ns*)))"));
      System.out.println(Compiler.eval (RT.readString ("(clojure.core/+ 1 2)")));
      System.out.println
              (Compiler.eval
                      (RT.readString
                              ("(->infix (((exp D) (literal-function 'eff)) 'x))")));
    } finally {
      Var.popThreadBindings();
    }

p-himik18:03:29

Ah, no - I was evaluating different code. Eval'ing (ns user) doesn't work, because it still requires *ns* to be bound.

Sam Ritchie18:03:32

but, VERY annoyingly, :refer-clojure :exclude does not silence warnings here

Sam Ritchie18:03:38

WARNING: = already refers to: #'clojure.core/= in namespace: user, being replaced by: #'sicmutils.env/=

Sam Ritchie18:03:01

ignore the space in that snippet!!! warnings still occur. edit, fixed.

hiredman18:03:12

@U2FRKM4TW my next example shows how to use clojure.java.api.Clojure to setup binding for *ns*

hiredman18:03:59

it is slightly incorrect, because I think you want the push outside of the try/catch, but I am too lazy to add the extra do to make it a nice single form for copying and pasting into the repl

p-himik18:03:58

Makes sense! So in the end, my Java version on SO seems to be doing the same thing but via a less ideal API.

hiredman18:03:44

and, despite commonly referring to special forms by the symbol that is the first part of the list, it is the whole list that is the special form (why it is a special form and not a special symbol)

hiredman18:03:36

*ns* is basically "compilation context", it is used when compiling forms to resolve symbols, so if you are outside of a place where compilation is known to be happening (the repl, loading a file) then it won't be bound and not settable

💯 2
hiredman18:03:50

you can bind it yourself

hiredman18:03:13

the same way the compiler does var binding

hiredman18:03:33

for syntax highlighting the best thing to do is come up with your own name "most favored symbols" and then make those whatever you want, and it will always be correctly because it is your name and you define it

borkdude22:03:32

@hiredman The issue is a bit more general than syntax highlighting: the clj-kondo static analyzer emits information that other tools (like clojure-lsp) use. Should clj-kondo say in its analysis that when clojure.core/fn is called, that this was a special form invocation or not? The medadata :special-form makes this a bit blurry.

borkdude22:03:20

Perhaps it should follow what tools like tools.analyzer do, I haven't checked there, but I expect that it only says "special form" for special-symbol? stuff

borkdude22:03:46

Hmm, no it seems tools.analyzer doesn't add that info

borkdude22:03:46

Yes, that thing isn't really used in the output

bronsa21:03:42

each special form maps to a unique node type

bronsa21:03:10

ugh, sorry, just noticed how old this was. my slack was jumping around

borkdude22:03:09

But I would say it's the "correct" answer, and the other things are just macros

hiredman22:03:10

but like catch isn't a special form

hiredman22:03:30

(try ...) is a special form and catch is part of its dsl

hiredman22:03:50

user=> (let [catch identity] (catch 1))
1
user=>

borkdude22:03:57

Agreed. It also doesn't behave like a special symbol here:

user=> (let [catch (fn [] :foo)] (catch))
:foo

borkdude22:03:50

@ericdallo In any case, you can use special-symbol? + the clojure.core namespace to determine yourself in clojure-lsp if it's a special form.

ericdallo02:03:49

Should we add this to clj-kondo? that idea of :special true

borkdude22:03:39

user=> (clojure.set/difference cljs-specials clj-specials)
#{defrecord* ns* ns js*}
user=> (clojure.set/difference clj-specials cljs-specials)
#{monitor-exit reify* clojure.core/import* monitor-enter}