Fork me on GitHub
#beginners
<
2023-07-03
>
Rajeev Ranjan Jha05:07:44

I have a few basic questions again. I have a vector of string integers which I want to convert into integers.

(map Integer. ("4" "8" "9"))   ; This doesn't work
(map (fn [x] (Integer. x)) ("4" "8" "9")) ; This works
I am curious about what is "Integer." doing semantically and how does it work. Also, what is the best way to convert string to integer in clojure?

Rajeev Ranjan Jha05:07:12

https://clojuredocs.org/clojure.core/int

;; Use Java interop instead
(Integer/parseInt "1")
;;=> 1
This didn't work for me btw. Also curious why

Ben Sless05:07:46

You can't pass static methods as arguments to clojure functions (for now) But parse-long does what you need

didibus05:07:26

Integer. is a call to a Java method. Java methods are not first class, they cannot be passed as arguments or returned. Map takes a function as argument, and a Java method won't work, so you need to provide a function. You can create a function that calls a Java method internally, this is your second approach, and that will work. But the Java method directly won't. With Clojure interop, you can call Java methods, but that's all you can do with them, like I said, you cannot pass them as arguments or return them. This is possibly going to change soon, as Clojure 1.12 is looking into a feature that will allow Java methods to become first class, and that would make your first example work as well, but until that feature makes it, for now, you cannot.

didibus05:07:26

To parse integers in Clojure, in 1.11, some standard parsing methods were added: https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/parse-long and https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/parse-double You should also know, in Clojure, all numbers are represented as longs by default, not as ints, similarly, all decimals are represented as double, not as floats. That's why it is parse-long, and there is no parse-int.

didibus05:07:27

> I am curious about what is "Integer." doing semantically and how does it work. Specifically, to that question. In Java, you have Objects, and you create an instance of an Object using a Constructor method. The syntax to call a Java object constructor method in Clojure is: (ConstructorMethod. <params>) , basically the name of the constructor method followed by a dot . This is a shorthand syntax, the long form is: (new ConstructorMethod <params>) You can learn more about interop with Java here: https://clojure.org/reference/java_interop

Rajeev Ranjan Jha13:07:23

@U0K064KQV Thank you. This was very informative. However, I could not use parse-long.

(parse-long "5")                                                                                                 
   Syntax error compiling at (foreverclojure:localhost:36483(clj)*:816:22).                                                              
   Unable to resolve symbol: parse-long in this context
Does on need to import something or am I doing something wrong?

Ben Sless13:07:25

You need to use a more recent Clojure version

Eduardo Lopes15:07:36

How can I bind an exception to *e? I have a try catch and I want to do it on catch block

Bob B16:07:08

You could use binding to scope it, or set! . I'm not sure if I'd recommend doing it, but it works in my limited experiments.

hiredman16:07:40

The binding for *e is setup in the outer repl code, and then inside the repl any exceptions on the repl thread that bubble up to the repl loop are handled by set! *e to the exception

hiredman16:07:15

The question is why would you want to set *e? It is just the mechanism by which the repl tracks the last exception it saw

hiredman16:07:20

Do if you have code that sets *e that code will throw an error when run without a binding setup for *e (which is the case outside of the repl) unless you explicitly setup your own binding for *e, which will get popped before control is returned to the repl loop if you are using it in the repl

Eduardo Lopes16:07:53

I want to set *e so I can debug from repl an exception that was caught, I am currently creating an _e symbol to debug from repl every time I need it

Eduardo Lopes16:07:35

Thought that using *e would make more sense since I want to debug from repl, that is where I run my whole application

hiredman16:07:01

if that is the case, and the exception is happens on a thread where *e is bound you can just set! it

hiredman16:07:43

if you find yourself constantly looking at some exception in the repl, maybe whatever that is shouldn't be an exception, it isn't exceptional

daniel.flexiana16:07:46

Maybe it's kinda out of scope but you can also rely on calling :ex using ClojureStorm.

Fay Carsons16:07:25

Whenever I define records in cljs, the constructor functions(map->Name, Name.) stop being recognized on hot reload, other files can't seem to use their methods consistently, and I have to add type hints when I can. This is using vscode + shadow-cljs + calva. Any ideas as to what's causing this and how I can fix it?

delaguardo16:07:53

most likely you are calling those functions from another namespace. consider adding a fibrin like new-name or create-name into the same namespace where you define a record. and call that function instead of generated. This happens because during reload new objects represent a record got defined but other namespaces still using methods of the old one.

Fay Carsons16:07:37

I'm not sure I follow, I need to create a function for each method of a record so they can be accessed in other namespaces? not sure if "fibrin" is a misspelling or something I'm not familiar with.

Fay Carsons16:07:20

Also, sometimes before hot reload, after clear cache refresh, other namespaces won't recognize record methods even though I've required the record. So I feel like there may be something, or multiple things wrong here.

Fay Carsons16:07:27

(ns example.primitives (:require [example.tools :refer [distance]])) (defrecord Sphere [position radius] (get-distance [this point] (- (distance this.position point) this.radius))) ;these all work in the namespace the record is declared ;and sometimes(before hot reload) in other files (def test-sphere (Sphere. [0 0 0] 1)) (def test-sphere-two (->Sphere [0 0 0] 1)) (def test-sphere-three (map->Sphere {:position [0 0 0] :radius 1})) (ns example.core (:require [example.shapes :refer [Sphere]])) (def spheres (mapv (fn [_] ;this works before hot reload, but doesn't after (Sphere. (vec (repeatedly 3 rand)) (rand))) (range 8))) (def sphere-distances (apply min (map (fn [sphere] ;but this often doesn't work before or after hot reload ;unless it's in the same file the record is declared ;".get-distance is not a function" (.get-distance ^Sphere sphere)) spheres)))

delaguardo16:07:44

yeah, sorry, was typing from my phone. "fibrin" is misspelled "function" 🙂

(ns foo
  (:require [bar :as b]))

(def common-name (b/map->Name {:val "John"}))
and another namespace where Name is defined
(ns bar)

(defrecord Name [value])
in that case when you change namespace bar you will get a new Object representing Name with different map->Name and ->Name functions pointing to this new object. Because namespace foo isn't reloaded it is still uses old definition of the record Name

delaguardo16:07:09

to fix that you can define common function new-name in namespace bar:

(ns bar)

(defrecord Name [value])

(defn new-name [value]
  (->Name value))
and use this function everywhere you need to define an instance of Name

delaguardo16:07:37

from your snippet: I think most of the problems will go away if you will define functions with defn instead of making global objects with def

rolt08:07:40

the code doesn't seem right, you're missing an interface name before (get-distance ... in your defrecord declaration. And you're applying get-distance to a sphere, when it takes a sphere and a point. Do you have compilation errors ? I think this should work fine if you fix the bugs, but you can have problem with stale records/protocols if you're storing them in a state (defonce myatom (->Sphere [0 0 0] 1))

Nathan Nolk18:07:02

Hi all, I am using Java interop for the first time and I'm not fully sure what is going wrong in this test I am writing. I am using a library called Sudachi (see here: https://javadoc.io/doc/com.worksap.nlp/sudachi/latest/index.html). In it, there is an interface called Morpheme (https://javadoc.io/doc/com.worksap.nlp/sudachi/latest/com/worksap/nlp/sudachi/Morpheme.html) which has a method called surface() (here: https://javadoc.io/doc/com.worksap.nlp/sudachi/latest/com/worksap/nlp/sudachi/Morpheme.html). I am writing a test for a function we wrote that takes in a string foo and returns a lazy sequence of Morphemes with only the words in the string. I tried calling Morpheme/surface (.surface didn't work) on the list but I get the following error: "No matching method surface found taking 1 args for interface com.worksap.nlp.sudachi.Morpheme." I am also not sure the (import ...) here is necessary but I'm a bit confused. Any help appreciated! Here is the part of the code that's relevant:

hiredman18:07:33

(Morpheme/surface %)
is calling a static method named surface from the class Morpheme

Nathan Nolk18:07:54

Hi thanks, I tried using .surface but I also get an error.

Nathan Nolk18:07:23

This time I get: Reference to field surface can't be resolved.

hiredman18:07:40

well the javadoc above definitely shows surface as not a static method

hiredman18:07:00

and how does that code look?

Nathan Nolk18:07:00

Yes I thought so too and I also tried using .surface, maybe I messed up syntax?

Nathan Nolk18:07:14

(is (= '["漢字" "いい"] (vector (map #(.surface %) (get-words words)))))) this is what I had for that part of the code

hiredman18:07:17

are you sure the version of the code you are using matches the version of the docs you are looking at

Nathan Nolk18:07:32

Or rather: how can I check?

hiredman18:07:45

well, what version are you using?

2
Nathan Nolk18:07:50

0.7.3 I believe?

hiredman18:07:01

ok that is what the docs say

Nathan Nolk18:07:07

I mean I pulled this from a git repo but I also installed 0.7.3 on my own system locally

Nathan Nolk18:07:14

So I assume it is correct, we just wrote this code

hiredman18:07:01

what is the actual error you are getting?

Nathan Nolk18:07:03

Just to check, I ran (type (get-words words)) and I get LazySeq

Nathan Nolk18:07:18

Reflection warning, /home/mayumin/Documents/Programming/Bunseki/test/bunseki/analyzer/word_test.clj:11:42 - reference to field surface can't be resolved.

hiredman18:07:25

and what are the types of the parts of the lazy seq

hiredman18:07:33

that isn't an error that is a reflection warning

nikolavojicic18:07:38

Try (Morpheme/surface) In Javadoc it doesn't take an argument

hiredman18:07:48

it isn't static

👍 2
Nathan Nolk18:07:53

...sudachi.MorphemeImpl

hiredman18:07:00

do you actually get an error when you run the code?

Nathan Nolk18:07:02

I just ran (type (first (get-words words)))

Nathan Nolk18:07:14

So if I do like lein test you mean?

hiredman18:07:18

the above is not an error, it is a reflection warning, an optional thing you have to turn on to get reported to you, it is the compiler warning you that it doesn't know the types involved in the method call, so it is emitting reflective code which isn't the most efficient

Nathan Nolk18:07:36

AH, okay I think it is working properly then, thank you

Nathan Nolk18:07:42

I get a test failure but I think this may be due to me

hiredman18:07:01

you can get rid of reflection warnings usually by adding type hints

Nathan Nolk18:07:32

Thank you so much, I was going crazy

Nim Sadeh23:07:01

Hey one more zip question here: how do you add a node at the end of a tree? For example

(-> (zip/vector-zip '[1 [2]])
    (zip/next)
    (zip/next)
    (zip/next)
    (zip/next)
    (zip/prev)
    (zip/insert-child 3) ;;#object[TypeError TypeError: Cannot read properties of null (reading 'cljs$core$IFn$_invoke$arity$1')]
    )
    
I can't walk back either, because running zip/prev returns nil

Bob B00:07:52

iirc, what I've done in the past is if next is end? then do the insertion, otherwise go next

Nim Sadeh00:07:53

I managed to brute force the issue, although I don't think I should have had to:

(defn replace-node
  "Catches the error you get when trying to replace the last node"
  [loc node]
  (-> (if (zip/end? loc)
        (-> (zip/root loc)
            (conj node)
            (zip))
        (zip/replace loc node))
      (zip/root)))

Nim Sadeh00:07:31

@U013JFLRFS8 my codebase separates out the navigation to and operations on locs in different functions, I'd hate to bundle them together. But that makes sense. I just like the api of

( -> (go-to tree n)
     (operate args)
     (zip/root)
)

Nim Sadeh00:07:44

ofc the issue here is that it doesn't do what I'd expect for conj to do on seq, but that's how you learn I guess

Bob B00:07:00

you can break it up - the initial ask had them compounded, but going to the last node could be a separate thing - I think a discrepancy here is that if end? is true, if I'm remembering things correctly, then the walk has proceeded past the last place to insert a node, and an insert will fail at that point, so for insert to succeed, I think zip/end? has to be false

Nim Sadeh00:07:20

yea that makes sense and what I have above doesn't at all - it adds a child to the root instead of the last node. Here it is:

(defn crossover [t1 t2 n1 n2]
  (let [p1a (go-to t1 n1)
        p2a (go-to t2 n2)
        p1 (if (zip/end? p1a) (go-to t1 (dec n1)))
        p2 (if (zip/end? p2a) (go-to t2 (dec n2)))
        replace #(-> (zip/replace %1 (zip/node %2)) (zip/root))]
    [(replace p1 p2) (replace p2 p1)]))

Nim Sadeh00:07:38

go-to cycles - if it hits the end node it starts at the root again

didibus04:07:24

> Nil signifies an empty location, and there is no reference to the previous step in it. The zip/up, zip/right and other functions also return nil for an empty location. If you iterate in a cycle and do not take this into account, you’ll just end up spinning your wheels. >

(-> [1 2 3]
> zip/vector-zip
> zip/down
> zip/left
> zip/left
> zip/left
> zip/left)