Fork me on GitHub
#beginners
<
2020-10-04
>
orlandomr2700:10:07

Hi, can anyone explain the difference (syntax) between clojure.string$upper_case and clojure.string/upper-case ? 🙂

seancorfield00:10:52

@orlandomr27 clojure.string$upper_case is the name of the actual class in the JVM bytecode; it indicates an inner class upper_case inside the string class in the package clojure; clojure.string/upper-case is a Clojure symbol that identifies the namespace clojure.string and the named Var upper-case.

orlandomr2700:10:34

I understand, thank you Sean 🙂

seancorfield00:10:58

Each Clojure namespace compiles to a JVM class in a package, based on the dotted name. Each Clojure function is compiled to a class (with methods invoke and applyTo).

orlandomr2700:10:36

Which one is more efficient to call or there is no difference?

seancorfield00:10:04

That question doesn't make sense to me.

orlandomr2700:10:56

I was thinking about calling a compiled code or code to be compiled… I guess it doesnt make sense 😄

seancorfield00:10:22

Clojure compiles code "on demand" so the actual call is always compiled, and calls into compiled code.

seancorfield00:10:40

It looks like an interpreted language but it isn't 🙂

orlandomr2700:10:19

Good to know that. Thank you!

seancorfield00:10:03

I had never thought of calling Clojure functions via interop on their compiled classes but your question caused me to try it out:

$ clj
Clojure 1.10.1
user=> (require 'clojure.string)
nil
user=> (clojure.string$upper_case/invokeStatic "Hello!")
"HELLO!"
user=>

seancorfield00:10:32

(this is why I always tell senior developers that they can learn from beginners!)

drewverlee01:10:02

Sean, I appreciate the time you take answering questions on here. Your open (m|k)indness is applaudable.

sova19:10:09

can personally vouch for sean's awesomeness as well 😄 super kind and くわしい (ku-wa-shii)... detailed/ thorough

seancorfield00:10:09

You call Clojure functions. What they're compiled to in the JVM isn't relevant really.

seancorfield00:10:55

(ns some.thing
  (:require [clojure.string :as str]))
(defn shout [s]
  (str/upper-case s))

seancorfield00:10:21

so (shout "Hello!") returns "HELLO!"

hewrin05:10:15

Hi, I’m testing out destructuring and I’m having a bit if an issue, I created a method like so

(defn destructuring-test [coll]
 (let [[x y z :as rest] [coll]]
    (str "x:" x " y:" y " z:" z "total:" (count rest)) 
  ))

(destructuring-test [1 2 3 4 5 6 7 8])
I expected to get
"x:1 y:2 z:3 total:8"
but what I get is
"x:[1 2 3 4 5 6 7 8] y: z:total:1"
Anything I’m doing wrong?

andy.fingerhut05:10:24

Replace [coll] with coll in the let

andy.fingerhut05:10:54

[coll] evaluates to a vector with one element, and the one element is itself a vector with 8 elements.

hewrin05:10:50

oh dear, it was so obvious now that you've said it, thanks so much!

jimka.issy12:10:20

I'm not sure how to correctly ask this question. I suspect my question contains a wrong assumption. But what is the correct way to resolve a symbol which or might not be in a namespace?

vlaaad12:10:52

Not very common function to use during development, what do you want it for?

zdot10112:10:14

Based off his wording, I wonder if he might be trying to resolve a symbol normally if its 'visible' from his current namespace, else try to search through other namespaces in the project to get a var/class bound to that symbol somewhere, although I'm not sure

jimka.issy13:10:26

@, yes I already have puzzled over the documentation in https://clojuredocs.org/clojure.core/resolve but I find it lacking.

jimka.issy13:10:38

I understand what a call such as (resolve 'x) means. it is the same as (ns-resolve *ns* 'x)

jimka.issy13:10:33

but what does it mean to ns-resolve a symbol such as my-ns/x in a namespace other than my-ns ?

jimka.issy13:10:11

There is more motivation of this question which I wrote in a closure-verse post: https://clojureverse.org/t/need-help-to-resolve-symbols-programmatically/6642. I put the posting there as I thought the discussion risked getting more involved.

zdot10114:10:56

resolving my-ns/x is going to get you the var/class for my-ns/x no matter what namespace you're in

jimka.issy14:10:45

@, I think you're right. That what I see by experimentation, However, that's not what the documentation says if you read it carefully. The doc for ns-find says the following:

Returns the var or Class to which a symbol will be resolved in the
namespace (unless found in the environment), else nil. 

zdot10114:10:09

Note that if the symbol is fully qualified, the var/Class to which it resolves need not be present in the namespace.

jimka.issy14:10:02

hmm. yes it says that indeed, which seems like a contradiction, not a clarification, of the previous statement.

jimka.issy14:10:20

So the doc doesn't actually say what happens to a fully qualified symbol.

jimka.issy14:10:31

So what is the correct statement of what happens when a fully qualified symbol is given as 2nd argument to find-ns. I suppose the question is what happens in the 2 arg version and the 3 arg version of find-ns ?

jimka.issy14:10:07

I have added a comment to https://clojuredocs.org/clojure.core/ns-resolve, can someone check it and tell me if what I've written is correct?

zdot10114:10:28

I'm trying to think on the best way to word this to show the subtle difference, but I don't take the first line to be saying the var or class itself has to come from the current namespace, just that its giving you the var/class that a symbol refers (resolves) to from the current namespace. And it so happens that ns/blah, from any namespace, refers to the var in ns with name blah

zdot10114:10:00

Similar to how a phone works, where typing in 123-4567 resolves to (current-area-code)-123-4567 but typing in resolves to the same phone number no matter where you are

jimka.issy14:10:57

and how is the given name space used for a symbol like clojure.lang.Symbol ? That's also not addressed in the documentation.

jimka.issy14:10:43

What does it mean to say "resolves something in a namespace"? Is this expression defined somewhere?

jimka.issy14:10:14

If I understand this correctly, if I'm resolving a symbol to get a class, then it doesn't matter which namespace I use, so I can actually use resolve rather than ns-resolve ... the value of *ns* plays no role, even if the caller intentionally rebinds *ns* before calling my function.

jimka.issy14:10:04

so my code is correct, regardless of what the namespace is bound to when my function is called.

(and (symbol? type-designator)
     (resolve type-designator)
     (class? (resolve type-designator)))

zdot10114:10:20

> What does it mean to say "resolves something in a namespace"? Is this expression defined somewhere? I'd say its the general meaning of the word in programming (here's the first example I could find, albeit not the most formal: https://laracasts.com/discuss/channels/laravel/what-does-the-word-resolve-mean where you'll find, among the definitions, this not yet helpful line: it means when you find out the answer, or find a resolution. In this case, I'd say the resolution we're interested in is name resolution (https://en.wikipedia.org/wiki/Name_resolution_(programming_languages)) ; to take a not-necessarily unique name and find what it actually intends to refer to. If I say 'Jim', there are lots of Jims but we can infer I'm talking about you right now. If I say 'Jim Jones', even though you're the Jim in the room right now we know I'm talking about someone else, as I've also last-namespace qualified this name wut (I realize I'm about to repeat myself, and I'm sure you know how this part works, but to be thorough:) If you're in a namespace and write "a", you intend to refer to a var or class either inside the current namespace, or referred into the current namespace with something like (ns blah (:require [other-ns :refer [a]])) or even (ns blah (:require [other-ns :refer :all])) Likewise, if you explicitly write out the namespace and everything as other-ns/a, we know you're flat out trying to refer to a var/class in that namespace other-ns, no matter what the current namespace is.

jimka.issy14:10:19

Thanks for the well intended explanation Cameron, But when I say "what does it mean", i really mean what does the expression mean in the closure documentation. To resolve a symbol in Common Lisp has different semantics than in clojure, vs emacs-lisp, and I suppose in many different lisps. Plus, it seems that symbol resolution in clojure is very subtle, and it very often trips me up in practice. I can imagine it may be less confusing to people who have only ever used one dialect of lisp.

jimka.issy14:10:20

Where is the documentation for &env ? what it contains etc...

ddouglass14:10:54

A blurb about &env can be found here : https://clojure.org/reference/macros

jimka.issy14:10:59

clojure-rte.core> (let [a 100 b 200]
     (defmacro xyzzy [& others]
                    (println &env)
                    nil))
#'clojure-rte.core/xyzzy
clojure-rte.core> (xyzzy )
nil
nil
clojure-rte.core> (let [x 100 y 200] (xyzzy))
{x #object[clojure.lang.Compiler$LocalBinding 0x2f6640f4 [email protected]], 
 y #object[clojure.lang.Compiler$LocalBinding 0x877bb0e [email protected]]}
nil
clojure-rte.core> (xyzzy)
nil
nil
clojure-rte.core> 

jimka.issy14:10:19

It seems to contain info on local variables at the macro call-site, not the macro definition site.

jimka.issy14:10:18

Does this mean that multiple uses of a macro with the exact same arguments must be expanded multiple times, as the &env might be different each time?

andy.fingerhut14:10:16

I Believe that every macro call is expanded independently from all others. There is no caching of results based on parameter values, as your question seems to be hinting at

jimka.issy14:10:24

I have added a comment to https://clojuredocs.org/clojure.core/ns-resolve,  can someone check it and tell me if what I've written is correct?

jimka.issy14:10:50

More questions about resolve. If I define a function foo as follows

(defn foo [s] (> s 10))
then (foo 12) evaluates to true, and so does ((resolve 'foo) 12) However, (fn? (resolve 'foo)) evaluates to nil, as (type (resolve 'foo)) evaluates to clojure.lang.Var. What is the correct way to figure out whether a given symbol results to something callable? I see also that (fn? #{:a 100}) evaluates to false.

jimka.issy14:10:10

Ahh I see, (ifn? (resolve 'foo)) evaluates to true. This is explained in the documentation of fn. https://clojuredocs.org/clojure.core/fn_q

zdot10114:10:44

ifn? should be misleading here, because all Var s are ifn , even if the value they wrap isn't.

core> (def a 1)
#'core/a
core> (ifn? (resolve 'a))
true

jimka.issy15:10:04

again, (resolve 'a) tries to resolve a in the current namespace. This might be dangerous, because the person calling my function might be rebinding *ns* to something different. I guess I'd like to resove in the namespace of the calling function. But that's asking for the impossible.

zdot10114:10:14

so instead it'd just be (ifn? a)

jimka.issy14:10:33

yes. What if I write a function such as

(def is-callable [x]
  (or (fn x)
      (and (symbol x) (resolve x) (ifn? (resolve x)) ... )))
what should I fill in at the ... to distinguish between just a Var and a var designating a callable something?

jimka.issy15:10:07

seems like (deref (resolve x)) gives me the function object.

jimka.issy15:10:50

(def is-callable [x]
  (or (ifn? x)
      (and (symbol x) (resolve x) (ifn? (resolve x)) (ifn? (deref (resolve x))))))
???

andy.fingerhut15:10:41

In your is-callable, any Var given to it will return true because of the first call to ifn? But maybe that is what you want for behavior here. Not clear to me what cases you are trying to distinguish

jimka.issy15:10:26

I'm trying to determine if it is a callable or a symbol designating a callable. perhaps it should be

(def is-callable [x]
  (or (fn? x)
      (and (symbol x) (resolve x) (ifn? (resolve x)) (ifn? (deref (resolve x))))))

jimka.issy15:10:22

Perhaps if I limit the logic of my program to demand a symbol, it will work better?

(def is-callable [x]
   (and (symbol x) (resolve x) (ifn? (resolve x)) (ifn? (deref (resolve x)))))

andy.fingerhut16:10:53

Note that it is technically possible for a Var to change value over time from callable to not-callable, or vice versa. Clojure programmers tend not to do so in most programs, though. I realize I may be pointing out the obvious here.

andy.fingerhut16:10:58

The JVM object representing the Var returns true for ifn? Independent of its current value

andy.fingerhut18:10:52

Example of things that can be done, but most Clojure programs do not do:

user=> (defn f1 [x] (inc x))
#'user/f1
user=> (defn f2 [x] (* 2 (f1 x)))
#'user/f2
user=> (f2 5)
12
user=> (def f1 5)
#'user/f1
user=> (f2 5)
Execution error (ClassCastException) at user/f2 (REPL:1).
class java.lang.Long cannot be cast to class clojure.lang.IFn (java.lang.Long is in module java.base of loader 'bootstrap'; clojure.lang.IFn is in unnamed module of loader 'app')
user=> (alter-var-root #'f1 (constantly (fn [x] (println "got here") 75)))
#object[user$eval146$fn__147 0x25d958c6 "[email protected]"]
user=> (f2 5)
got here
150

jimka.issy06:10:55

Thanks @ for the useful commentary. I'm taking a look at all the uses of resolve in my program. There seem to be two cases. 1) where I'm trying to determine whether a given symbol names a class, and 2) where the given symbol names a type predicate such as list? . I've refactored these two cases into their own functions. find-class and callable-designator? ... this means if I discover later that I have a subtle bug in the either semantic, I can refactor it in one single place.

hiredman21:10:06

Callable or not is not the same thing as throws an error when called or not