Fork me on GitHub
#beginners
<
2021-03-07
>
sova-soars-the-sora00:03:40

Hmmm. Seems like chrome doesn't care xD

sova-soars-the-sora00:03:10

the CSRF token is changing, but it's apparently not getting the right one via this embedded electron app

sova-soars-the-sora00:03:50

not mission-critical, but still a perplexing issue.

sova-soars-the-sora00:03:05

I thought maybe I was passing in an outdated token or something... the web version works fine, the nativefier version does not

Kifah11:03:58

Hi, can anyone help me with setting up Intellij?

Kifah11:03:05

I've set up a barebones cljs project as per https://andrearichiardi.com/blog/posts/clojurescript-cursive-shadow-setup.html, but IDEA won't stop complaining, and fetching maven repositories fails, like so-

Kifah11:03:21

This is what my deps.edn looks like

simongray12:03:10

@kifah FYI there's a #cursive channel too

Kifah12:03:59

Thank you Simon, crossposted there 🙏:skin-tone-3:

Jim Newton14:03:22

Every time I restart doing something in clojure, it get confused again. Can someone help me understand why this doesn't equal 0 when I is an integer?

(reduce + 0 (concat (range (- i) -1)
                                     (range 1 i))))

Jim Newton14:03:39

I see, it is because range DOES NOT include its upper bound ....

Jim Newton14:03:51

if I add the numbers (range -99 -1) to the numbers in (range 1 99), I don't get 0.

Jim Newton14:03:08

I have a question about loop/`recur`. If I have a function which DOES work recursively, is it still better to write it as loop if possible? i.e., is there some speed advantage?

andy.fingerhut15:03:24

loop/recur will not grow and shrink the stack, whereas a recursive function will. The default stack depth is small enough that you will often cause an exception to be thrown due to stack exhaustion in a few thousand depth recursive calls. You can configure it higher, but loop/recur will avoid that completely

Jim Strieter14:03:48

Singletons are 1 instance per JVM instance, right? (As opposed to 1/cpu, 1/motherboard, etc.) I wouldn't think multiple JVM's on the same machine would share runtime, but I wanted to check.

Alex Miller (Clojure team)15:03:53

depends what you mean by "singleton" and "runtime", but generally yes

Jim Strieter14:03:11

Is there a way that I can write a clojure fn to operate on a Java instance without having to pass the Java instance into every method call?

Alex Miller (Clojure team)15:03:21

there are several ways to work with "ambient" objects: • Clojure dynamic vars (available and overridable on a per thread basis) • Java threadlocals (threads only see "their" version) • global state (all Clojure vars are global state)

3
Alex Miller (Clojure team)15:03:52

that said, all of these mechanisms result in functions that depend on external "stuff" not visible to the caller. in general those kinds of functions are harder to test, harder to understand, and harder to maintain, and should be considered an exceptional case to use only when you've thought about all the tradeoffs

3
Jim Strieter15:03:15

@U064X3EF3 thank you! Yeah, after I wrote that I realized that that's kind of like global variables. Good to know it's possible but not preferable

Jim Newton14:03:33

I was trying to rewrite a small but precise Scala function in Clojure, and I am unable to make it as concise. in Clojure it is roughly twice the size. The function is purely functional, doesn't depend on OO paradigms, so my instinct is it ought to be very concise in Clojure. Here is the Scala code.

def treeFold[A](seq: Seq[A])(z: A)(f: (A, A) => A): A = {
  def dwindleTree(stack: List[(Int, A)]): List[(Int, A)] = {
    stack match {
     case (i, b1) :: (j, b2) :: tail if i == j =>
dwindleTree((i + 1, f(b2, b1)) :: tail) case _ => stack
}}
val stack = seq.foldLeft((1, z) :: Nil) {
    (stack, ob) => dwindleTree((1, ob) :: stack)
  }
stack.map(_._2).reduce { (a1, a2) => f(a2, a1) } }
One thing that makes this concise is that the first case in the pattern match case (i,b1)::(j,b2)::tail if i== j simultaneously checks two different conditions. In the Clojure code I need two concentric if forms.
(defn tree-fold
  "omitting doc string...."
  [f z coll]
  (letfn [(dwindle-tree [stack]
            (loop [stack stack]
              (if (empty? (rest (rest stack)))
                stack
                (let [[[i b1] [j b2] & tail] stack]
                  (if (= i j)
                    (recur (cons [(inc i) (f b2 b1)] tail))
                    stack)))))]
    (let [stack (reduce (fn [stack ob]
                          (dwindle-tree (cons [1 ob] stack)))
                        (list [1 z])
                        coll)]
      ;; stack is guaranteed to have at least one element
      (reduce (fn [a1 a2] (f a2 a1))
              (map second stack)))))
Isn't there a better way?

borkdude15:03:15

@jimka.issy Perhaps this can be made concise with core.match. I will give it a try

borkdude15:03:33

I came up with this, but it's pretty ugly, so not really an improvement:

(require '[clojure.core.match :refer [match]])

(defn tree-fold
  "omitting doc string...."
  [f z coll]
  (letfn [(dwindle-tree [stack]
            (loop [stack stack]
              (match (nnext stack)
                     ([[i b1] [_ b2] & tail] :guard (fn [[[i _] [j _]]] (= i j)))
                     (recur (cons [(inc i) (f b2 b1)] tail))
                     :else stack)))]
    (let [stack (reduce (fn [stack ob]
                          (dwindle-tree (cons [1 ob] stack)))
                        (list [1 z])
                        coll)]
      ;; stack is guaranteed to have at least one element
      (reduce (fn [a1 a2] (f a2 a1))
              (map second stack)))))

(tree-fold + 0 [1 2 3])

Jim Newton15:03:04

Thanks. BTW what is nnext ?

borkdude15:03:47

same as (next (next ..))

Jim Newton15:03:53

is it necessary, the Scala code matches stack rather than matching (next (next ...))

borkdude15:03:10

(empty? (rest (rest stack))) will return the same truthyness as (nnext stack)

borkdude15:03:53

I couldn't find out how you can re-use the bindings in the :guard

Jim Newton15:03:15

reusing the bindings with :guard is the important part of guard. right?

borkdude15:03:17

True. I guess you could do it like this. But this isn't buying you much over let. Maybe just accept that Clojure is longer than Scala here ;)

(dwindle-tree
 [stack]
 (loop [stack stack]
   (match (nnext stack)
          ([[i b1] [j b2] & tail])
          (if (= i j)
            (recur (cons [(inc i) (f b2 b1)] tail))
            stack)
          :else stack)))

Jim Newton15:03:39

Or I could take it as a challenge to implement a pattern matching which sane guards ? Grins. Don't complain unless you're willing to submit a fix.

borkdude15:03:25

Yes. core.match is extensible btw, but I'm not sure if it is possible to "forward" bindings into a predicate... I think it should be, but not sure.

3
Jim Newton15:03:15

of course I can reduce one line by using tail recursion rather than loop/reduce

solf02:03:24

You mean removing

(loop [stack stack]
? I don't think it's needed, actually. recur target both loop and functions, so the loop is redundant here.

Jim Newton15:03:41

I know (for my algorithm) that the maximum recursion is log(length(coll))

Jim Newton15:03:25

I can put the operands of reduce on a single line if I make reduce-1 a local function. that trades two lines for two lines.

(defn tree-fold
  "Like the 3-arg version of reduce except that does not compute from left-to-right
  but rather effectingly by grouping in concentric groups of two. e.g.
   (+ (+ (+ 1 2) (+ 3 4)) (+ (+ 5 6) (+ 7 8))) ...
  The remaining (right-most) elements are partitioned into 2's as much as possible.
  Intermediate values become unreferenced as quickly as possible, allowing them to 
  be GCed"
  [f z coll]
  (letfn [(dwindle-tree [stack]
            (if (empty? (rest (rest stack)))
              stack
              (let [[[i b1] [j b2] & tail] stack]
                (if (= i j)
                  (dwindle-tree (cons [(inc i) (f b2 b1)] tail))
                  stack))))
          (reduce-1 [stack ob]
            (dwindle-tree (cons [1 ob] stack)))]
    (let [stack (reduce reduce-1 (list [1 z]) coll)]
      ;; stack is guaranteed to have at least one element
      ;; and have length <= log_2(coll)+1
      (reduce (fn [a1 a2] (f a2 a1))
              (map second stack)))))

andy.fingerhut16:03:17

I haven't looked at the specifics of this code, but people have often made their custom variants of cond macros that do combinations of conditional tests and bindings that are specific to each branch. Not sure if that helps with code size / expressivity here, but it is a reasonably common desire that isn't part of Clojure core library (except if-let and when-let , and also somewhat cond-> and cond->>)

3
Jim Newton16:03:52

yes. in my case small-code-size relates to putting a code snippet on a beamer (powerpoint) slide to display to non-closure experts.

Jim Newton16:03:27

I'm giving a talk in a few weeks to a group of what I believe to be data scientists, about use cases for non-standard fold/reduce patterns.

Jim Newton16:03:01

i.e., why a person might be dissatisfied with the default fold implementation in your language of choice, and what you can do about it.

Jim Newton16:03:44

the original paper is written for a scala crowd, but I'd like to give the same talk to a non-scale crowd.

Jim Newton16:03:59

super-optimized-scala can look like line-noise.

Jim Strieter17:03:13

I'm trying to import a local Clj namespace from the same project, different file.

Jim Strieter17:03:59

I type: (:use 'my-package-name.my-namespace) and get nil. Then I try to call something from that namespace: (some-func 0.) and get: Unable to resolve symbol in this context What am I doing wrong?

borkdude17:03:44

@jimka.issy Can you give more context? Where are you typing this expression? In a REPL outside of an ns form?

borkdude17:03:02

Then you should use (use ....), not the keyword

borkdude17:03:31

(:use 'my-package-name.my-namespace) returns nil because it tries to look up the keyword in the symbol

Jim Strieter17:03:39

In a repl outside of a ns form - yes

borkdude17:03:56

(use 'my-package-name.my-namespace)

Jim Strieter17:03:18

@borkdude when you say it returns nil because it tries to look up the keyword, does that mean it succeeded or failed

borkdude17:03:35

it doesn't do anything namespace related. the expression evaluates like (get 'my-namespace :foo)

borkdude17:03:21

e.g. (:foo {:foo 1}) returns 1, because the keyword looks itself up in its argument

borkdude17:03:55

but (:foo "dude") returns nil because there is no :foo key in a string

borkdude17:03:05

you are just hitting a confusing case of this while trying to use use

Jim Strieter17:03:11

My problem is that when I type my-package-name, the repl fills in .core. I can type in .my-namespace manually, but then the repl doesn't let me call anything from the namespace I just imported. Even after I type my-namespace manually, the repl won't let me call anything from that namespace

hiredman18:03:22

You have to load code in order to use it, if you haven't loaded the file that defines a namespace, then the namespace doesn't exist and you can't use it

hiredman18:03:05

It isn't like java where you can use a long fully qualified named anywhere regardless of if you import a class or not

hiredman18:03:46

require and use both also load the code

Jim Strieter18:03:38

@hiredman is there a way to load a file that implies local project? for instance, tells repl to look in src/my-project/.../someFile.clj without typing src/my-project

hiredman18:03:54

Namespace names implicitly map to resource names relative to classpath roots

hiredman18:03:27

So when you require the namespace foo.bar, clojure looks for a foo/bar.clj resource

hiredman18:03:25

Where is you aren't familiar with java resources, you can for now just think of resources as a file

hiredman18:03:05

So generally you will use some tool to launch your repl, which puts your src directory on the classpath, so when you require foo.bar, src/foo/bar.clj will be loaded

David Reno22:03:46

On the topic of namespaces. I just worked through a problem and I’d like to confirm my understanding. 1. I’m connecting to a remote REPL 2. my main (ns …) declaration requires another namespace in a deeper directory 3. that file doesn’t exist on the remote REPL because I’m developing and organizing on my local machine 4. I get a FileNotFoundException because my other namespace files aren’t on the remote host (they’re local) 5. as long as I go to the buffers of the namespaces I need and load the files, everything works My understanding is that if the namespace is already loaded into the remote REPL, it won’t look for the file on the remote host. Am I understanding this correctly?

David Reno22:03:39

sorry if I’m relying on information in my own head for this to make sense. Here’s an example: I have the following files on my local host where I’m editing the code: • myapp/src/core.cljmyapp/src/otherstuff/thing.clj Those files aren’t on the remote host where the REPL is running. In myapp/src/core.clj, I have the following line: (ns core (:require [otherstuff.thing] :as thing]) When I eval that last line I get the exception as I would expect since the file isn’t there on the remote host. If open the local file myapp/src/otherstuff/thing.clj and load the buffer into the remote REPL, the problem goes away.

andy.fingerhut22:03:48

Whenever you do a require or use on a namespace in Clojure, it remembers this in a *loaded-libs* internal variable (which you can examine if you are curious to see what it contains). Future require/`use` (or their equivalents inside of an ns form) will avoid reloading a namespace that *loaded-libs* says has been loaded already.

andy.fingerhut22:03:22

Even if you are not trying to do any dynamic development at the REPL, this is a critical time-saver, because you don't want a namespace required from 20 other namespaces to be loaded 20 times.

David Reno22:03:29

ok, just confirming that I got it. If it’s loaded, it goes in *loaded-libs* and won’t be looked for to load anymore when you require it in a (ns …) command. So it doesn’t have to exist remotely if you loaded it locally.