Fork me on GitHub
#clojure
<
2020-04-21
>
richiardiandrea02:04:33

Hello, what would it be the correct to resolve a symbol that is referred in a namespace this way?

(defn resolve-referred
  [ns sym]
  {:pre [(symbol? ns) (symbol? sym)]}
  (some-> (find-ns ns) (ns-refers) (get sym)))
Is there any other way?

potetm02:04:58

I think you want ns-resolve

richiardiandrea02:04:14

I tried but my input is the ns that :refers and an unqualified symbol and ns-resolve was returning nil

potetm02:04:00

oh, missed that bit

potetm02:04:06

“symbol that is referred”

richiardiandrea02:04:49

will try again though, maybe I am doing something odd, thanks!

potetm02:04:57

uuuh, probably what you have, but that’s a super weird use case

richiardiandrea02:04:46

so actually you are right

richiardiandrea02:04:59

user> (ns orchard.test-ns (:require [orchard.test-macros :as test-macros :refer [my-add]]))
nil
orchard.test-ns> (in-ns 'user)
#namespace[user]
user> (ns-resolve 'orchard.test-ns 'my-add)
#'orchard.test-macros/my-add
user> (ns-resolve 'orchard.test-ns 'orchard.test-ns/my-add)
nil

richiardiandrea02:04:15

the latter case is fully qualified and that is what's not working

richiardiandrea02:04:29

(obviously)

😄 4
potetm02:04:41

ah, yeah, the former is how ns-resolve works–resolves a simple symbol in an ns

richiardiandrea02:04:04

tnx for replying 😉

bananadance 4
noisesmith15:04:42

also ns-resolve is weird, and if you know the full ns you want something from already, you can use (resolve 'orchard.test-ns/my-add)

noisesmith15:04:24

that is, ns-resolve asks for a symbol to be resolved as if you were in some other ns, and resolve is just "get me the binding of this symbol", which seems more direct to me

pfeodrippe15:04:54

Hi, how could I run clojure code in an isolated environment (e.g. in a new ClassLoader) so I can reload the classes every time I run some code? Isolated like made in https://github.com/tlaplus/tlaplus/blob/master/tlatools/org.lamport.tlatools/test/util/IsolatedTestCaseRunner.java

noisesmith15:04:19

I think ztellman's virgil lib is made for this sort of thing https://github.com/ztellman/virgil

pfeodrippe16:04:39

Thank you! But I was thinking more about runtime isolation, I will try https://github.com/RutledgePaulV/clj-embed o/ thanks

rutledgepaulv16:04:18

i always forget I made that. I should probably clean it up and make it work for git deps, etc

pfeodrippe17:04:19

Great work, BTW @U5RCSJ6BB o/

thanks2 4
cjsauer19:04:29

Is there a built-in function to ease implementation of string translations? What I’d like is something like:

(translate {"cat" "dog"
              "dog" "cat"
              "rain" "snow"}
             "It is raining cats and dogs")
  ;; => "It is snowing dogs and cats"

nwjsmith19:04:17

(defn applesauce [s substitutions](->> (clojure.string/split s #" ") (replace substitutions) (clojure.string/join " ")))

cjsauer19:04:14

It’s a bit limiting tho in that the substitutions have to happen on words

cjsauer19:04:31

“concat” would not become “condog” in my example

phronmophobic19:04:04

isn’t that what raining -> snowing is doing?

andy.fingerhut19:04:24

clojure.string/escape is the closest function to what you are asking about that is built into Clojure.

cjsauer19:04:56

> isn’t that what raining -> snowing is doing? yea sorry, I read the output too hastily. It actually returns "It is raining cats and dogs" which is incorrect.

andy.fingerhut19:04:00

For a general set of strings to be replaced, if they can overlap with each other in the input, one also would need to specify (or not care about) deciding what you want as output, e.g. if you ask to replace {"bare" "1", "aren't" "2"} , what should the output be for an input string of "baren't" ?

andy.fingerhut19:04:50

That question is not applicable for clojure.string/escape , because it only allows individual characters in the "to be replaced" set, not arbitrary strings.

cjsauer19:04:21

@andy.fingerhut hm good point. What if I restrict the substitutions to only occur within special syntax, like a template language: "It is <precipitation> <feline> and <canine>"? This is likely a bit easier to solve. I could pull in a template language dep, but it’d be cool to solve it inline.

andy.fingerhut19:04:04

That kind of restriction certainly eliminates most, if not all, cases that could otherwise be ambiguous. As long as the template separators do not occur in any other non-substitution places in the input string, of course.

👍 4
andy.fingerhut19:04:12

I wouldn't be surprised if cl-format can do it, but haven't dug into all of its capabilities. reduce and clojure.string/replace should work probably-more-closely to what you want as long as the replacement strings also never contain the boundary characters < >

cjsauer19:04:56

I believe that should be the case in this scenario

andy.fingerhut19:04:55

The main potential down side to combining reduce and replace is efficiency, if the number of strings to be replaced is large, since it will scan over the to-be-replaced string N times for N strings to be replaced. There are other solutions that guarantee passing over the to-be-replaced strings exactly once, of course.

cjsauer19:04:53

Yea, in this scenario the perf cost might be prohibitive. Is there a name for the single pass algorithm?

andy.fingerhut19:04:24

With delimiters like < and > that are not used for any other purpose, it would be a simple kind of 'lexing' or parsing to find all of those in one pass, and for each one, look up the string in the map for its replacement.

andy.fingerhut19:04:15

At least the first solution there doesn't seem to mention what its behavior would be in the overlapping-to-be-replaced-strings case. It has some behavior in that case, of course -- I just do not know what it is off the top of my head.

cjsauer19:04:06

Hm yea…does clojure have the regex.sub equivalent? That’s pretty cool how you can pass it a lambda in python.

noisesmith20:04:25

user=>(clojure.string/replace "<foo> dog <bar>" #"<[^>]+>" {"<foo>" "cat" "<bar>" "bear"})
"cat dog bear"

noisesmith20:04:38

this works because {} is a lambda in clojure, any other string->string function works there

noisesmith20:04:11

the hard part was making the regex that only matched one <...> at a time :D

cjsauer20:04:12

Wait…`replacement` can be a lambda??? 🤯

andy.fingerhut20:04:26

Try (doc clojure.string/replace) 🙂

noisesmith20:04:29

it says so right in the doc string

noisesmith20:04:33

user=> (clojure.string/replace "<foo> dog <bar>" #"<[^>]+>" clojure.string/reverse)
">oof< dog >rab<"

cjsauer20:04:35

Oh my…I need to get my eyes checked

noisesmith20:04:06

anyway, it's nice that there's such a direct answer to your question

cjsauer20:04:16

This is great. Thank you @U051SS2EU 🙏

cjsauer19:04:31

NB: I’m not actually doing i18n type translation, just literal string replacement

Alex Miller (Clojure team)19:04:20

clojure.string/replace ?

cjsauer19:04:23

Yea that’s what I’m looking at. It should be straightforward to reduce over the translation table kvs and do a str/replace for each one.

cjsauer19:04:30

(defn- translate
  [table s]
  (reduce-kv (fn [s* pattern replacement]
               (cstr/replace s* (re-pattern pattern) replacement))
             s
             table))

cjsauer19:04:49

Close, but it returns It is raining cats and cats.

chrisulloa19:04:50

it’s because you are replacing cat first, so you get “It is snowing dogs and dogs”

chrisulloa19:04:57

then it goes to “It is snowing cats and cats”

cjsauer19:04:21

@andy.fingerhut hm good point. What if I restrict the substitutions to only occur within special syntax, like a template language: "It is <precipitation> <feline> and <canine>"? This is likely a bit easier to solve. I could pull in a template language dep, but it’d be cool to solve it inline.

Alex Miller (Clojure team)19:04:58

you can probably do that with cl-format, it does everything else

cjsauer19:04:41

I’ve always wanted to dive into that crazy function ha

cjsauer19:04:15

I could maybe preprocess the translation table keys to wrap them in the syntax. {"cat" "dog"} would become {"<cat>" "dog"} for example.

cjsauer19:04:26

That might be good enough for my use-case actually…

cjsauer19:04:50

(I should add that I’m solving a very specific problem but asking very general questions 😅 )

colinkahn20:04:41

Sort of a longshot, but is there a way to change equality checks for ##NaN ? When doing comparisons this is a real bummer:

(= {##NaN 0} {##NaN 0}) ; => false

seancorfield20:04:53

That's pretty much "by definition": https://clojure.org/guides/equality -- "'Not a Number' values ##NaN, Float/NaN, and Double/NaN are not = or == to anything, not even themselves. Recommendation: Avoid including ##NaN inside of Clojure data structures where you want to compare them to each other using =, and sometimes get true as the result."

noisesmith20:04:15

a related bit of fun

user=> (assoc {} ##NaN 0 ##NaN 1)
{##NaN 0, ##NaN 1}

🤯 4
andy.fingerhut20:04:36

And this is a long shot answer, but the only way I can think of to change that behavior in Clojure is to change the Java code that implements equality, which would be your own custom version of Clojure that no one else is likely to use.

noisesmith20:04:24

there's prior art for specialized hash-maps that let you use special functions for equality

andy.fingerhut20:04:31

##NaN is not really a true value in Clojure, in the sense of the "value of values" talk by Rich Hickey. It is a thing masquerading as a number 🙂

andy.fingerhut20:04:06

I have seen specialized hash-maps in many other languages, but I do not recall coming across one in Clojure before. Even if you created one, it would not change the behavior of = for ##NaN in all of the other contexts where they appear.

noisesmith20:04:49

right - I'm just saying I could imagine making a hash map that handled NaN differently as a key, without changing clojure's equality globally

andy.fingerhut20:04:49

Apparently it is now 9 months since I spent too many hours creating this: https://github.com/jafingerhut/batman

👀 4
colinkahn20:04:08

Yeah mostly wishful thinking in asking, I’ve run across is a couple times during generative testing, for example (s/gen map?) can produce ##NaN as a key or value. I have enough control over the predicates I’m using though to make a version that doesn’t include NaN, so I’m gonna do that :man-shrugging:

andy.fingerhut20:04:08

I have a half a recollection that test.check might have an issue/ticket to add a non-Nan generator, but I haven't checked.

colinkahn20:04:54

Interesting, there are variants in test.check.generators like any-equatable , I rolled my own since I’m using spec and those aren’t in spec.gen.alpha

colinkahn20:04:29

(defn equatable-any []
  (gen/such-that #(= % %) (gen/any)))

andy.fingerhut20:04:02

That looks like a good filter for most things you would want to generate via test.check

colinkahn20:04:40

Right, I would rather require extra effort to the opposite than avoid that as the default

andy.fingerhut20:04:43

You can ask folks like Gary Fredericks in #test-check channel for back story if you want to dig further. I suspect backwards compatibility of the test.check library may play a role here.

👍 4
gfredericks21:04:08

It's both not wanting to change the meaning of things, but also there's a tension with PBT libraries and edge cases like this

gfredericks21:04:04

On the one hand it's annoying to have to opt out of the edge cases you're confident you don't care about, but on the other hand a big reason to use a PBT library is to discover edge cases you weren't thinking about

gfredericks21:04:25

So making them all opt-in can actually decrease the utility

colinkahn21:04:20

Makes sense. Perhaps this is more of the genericness of the generators spec predicates like map? produce

gfredericks21:04:02

I think you could make a good argument that map? shouldn't generate nans as keys in particular

gfredericks21:04:15

Since you can't even look them up

gfredericks21:04:09

But honestly the whole situation is terrible and there's no good answer and I think languages should never conflate floating point equality semantics with generic object equality

noisesmith21:04:06

even java.util.IdentityHashMap, which is the only one I know of that uses identity instead of equality, still fails to match a nan key because it requires both identity and equality for some reason(???)

gfredericks21:04:04

Regexes are a similar case

seancorfield21:04:10

Yeah, I was shocked/annoyed the first time I discovered that regexes don't compare equal to themselves 🙂

seancorfield21:04:32

But you can always pull the underlying string out of the regex and use that instead.

gfredericks21:04:47

They do to themselves

gfredericks21:04:55

Just not an identical regex

seancorfield21:04:18

Ah, right. Yes, that was the issue I ran into.

seancorfield21:04:53

user=> (= #"one" #"one")
false
user=> (= (.pattern #"one") (.pattern #"one"))
true

Spaceman22:04:32

I have yet another cors issue:

Access to XMLHttpRequest at '' from origin '' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
So it's a cors error, and I follow the instruction and set the credentials to false:
{"Content-Type" "application/edn"
             "Access-Control-Allow-Headers" "Content-Type"
             "Access-Control-Allow-Origin" "*"
             "Access-Control-Request-Method" "GET, OPTIONS"
             "Access-Control-Allow-Credentials" false}
But still getting the same error. Fixes?

rymndhng22:04:56

@pshar10 with CORS, it's not as simple as returning the extra headers -- you need to send those headers in a CORS preflight response, i.e. OPTIONS request. A common way to solve this in a cross-cutting way is to add middleware which sets this configuration up, for example: - https://github.com/r0man/ring-cors - https://github.com/unbounce/encors

👍 4