Fork me on GitHub
#beginners
<
2018-09-14
>
forrest00:09:30

quick question! I’m new to clojure and was taking a guess at how iterate might be implemented with mutable internal state, but this does not seem to work

(defn iterate [f x]
  (def next x)
  (fn [] 
    (let [current next]
      (def next (f current))
      current)))
what’s the right way to do something like this (higher-order function with mutable internal state)?

Bobbi Towers00:09:28

I think you'd wanna use an atom for that

Bobbi Towers00:09:02

a def inside a fn is a no-no, even though it technically works

forrest00:09:33

ah, I see - reading about atoms now and does look like what I need; thanks!

forrest00:09:08

also realize now that iterate returns a lazy-sequence, so isn’t exactly what I was thinking but a learning experience nonetheless

mfikes00:09:50

@forrest.akin Yeah, using an atom it is a good learning exercise to produce something like

cljs.user=> (def iterate-f (iterate inc 1))
#'cljs.user/iterate-f
cljs.user=> (iterate-f)
2
cljs.user=> (iterate-f)
3

💯 4
forrest01:09:25

that’s exactly what I was testing with, haha

mfikes00:09:16

Ahh, I see your original definition pretty much behaved that way, apart from a difference in how things start up 🙂

seancorfield00:09:12

Because def always declares a top-level (global) var @forrest.akin -- you want let for local bindings.

4
forrest00:09:48

oh wow, good to know!

forrest01:09:40

thanks all, this is what finally worked

(defn iterate [f x]
    (let [next (atom x)]
        (fn [] 
            (let [current next]
                (swap! next (fn [current] (f current)))
                (deref current)))))

(def inc-one (iterate (fn [x] (+ x 1)) 0))

forrest01:09:41

was confused for awhile by all the memory references in the console; my first lisp so keep on forgetting I need those parens to evaluate my expression 😂

forrest01:09:18

and the last piece was figuring out that I have to use deref to actually get the value out of the atom

Bobbi Towers01:09:34

cool! you can also use the @current shorthand for (deref current)

4
seancorfield01:09:40

@forrest.akin You can simplify that swap!

(swap! next f)

💯 4
mfikes01:09:20

Yeah, the whole thing can be simplified down to use one let as well

(defn iterate [f x]
  (let [state (atom x)]
   (fn []
    (swap! state f))))

seancorfield01:09:20

That's not quite the same @mfikes -- @forrest.akin wants the old value back, not the new one.

forrest01:09:22

ah, the reason I have the 2nd let is so the first return is x instead of (f x)

mfikes01:09:53

Right, I was imitating his iterate above...

seancorfield01:09:12

(defn iterate [f x]
  (let [state (atom x)]
    (fn []
      (first (swap-vals! state f)))))

seancorfield01:09:38

swap-vals! returns a pair of old value, new value.

seancorfield01:09:42

@forrest.akin Like this?

(! 539)-> clj
Clojure 1.9.0
(defn iterate [f x]
  (let [state (atom x)]
    (fn []
      (first (swap-vals! state f)))))
WARNING: iterate already refers to: #'clojure.core/iterate in namespace: user, being replaced by: #'user/iterate
#'user/iterate
user=> (def inc-one (iterate inc 0))
#'user/inc-one
user=> (inc-one)
0
user=> (inc-one)
1
user=> (inc-one)
2
user=> 

mario-star 4
mfikes01:09:57

To fix up Forrest's earlier version you'd need to deref earlier:

(defn iterate [f x]
    (let [next (atom x)]
        (fn [] 
            (let [current @next]
                (swap! next (fn [current] (f current)))
                current))))

😎 4
seancorfield01:09:25

Ah, good catch!

forrest01:09:15

oh, right because I was assigning current to the atom instead of the value, so my swap effected both

👍 4
mfikes01:09:20

Another side point worth mentioning is that for some stateful functions, you can use volatiles, but for now it is probably better to just stick with atoms

mfikes01:09:03

(A volatile is much like an atom, but with slightly weaker semantics but better performance in some use cases)

forrest01:09:59

is swap-vals! new? compiler no like

forrest01:09:27

ah, yup >available since 1.9 I’m on 1.8

seancorfield01:09:32

You might as well switch to 1.9 -- it's been out for ages now and Alpha releases of 1.10 are already available.

seancorfield01:09:09

Clojure is very stable, so it's usually pretty easy to upgrade and stay on the very latest version. We've been deploying Alpha builds to production for over seven years now 🙂

forrest01:09:25

done! I was using the lein repl, which I guess is still coming with 1.8

forrest01:09:53

as does http://repl.it :thinking_face:

seancorfield01:09:33

Wow, I'm a bit surprised the latest Leiningen still fires up a 1.8 REPL :shocked_face_with_exploding_head:

seancorfield01:09:36

(I was on Leiningen 2.7.1 so I wouldn't have been surprised there -- but I just ran lein upgrade to get 2.8.1 and it's still a Clojure 1.8 REPL...)

schmee10:09:18

no, that’s just a misleading example

schmee10:09:17

you could write the Javascript example in a functional way that would be pretty much exactly like Clojure

schmee10:09:40

and you could write the Clojure one in an imperative way (using atoms and loop) and it would look like the JS one

deliciousowl10:09:45

looks really ugly though 😄

schmee10:09:45

ie misleading

deliciousowl10:09:54

but yeah makes sense

grierson10:09:28

How do I log incoming requests coming through a compojure Ring handler so that I can debug why it's returning a nil response.

grierson10:09:06

Im learning Ring, and when I call the second app expression in the comment block I get a NPE error. But I can't see the shape of the request so I can't see the cause.

grierson11:09:03

@borkdude Hey, I tried your inline def but it's saying it's unable to resolve symbol.

borkdude11:09:49

you have to re-evaluate your code first

borkdude11:09:10

be sure to read this first if you’re not familiar with working in a REPL: https://clojure.org/guides/repl/introduction

borkdude11:09:21

an ordinary prn/println should work just as well, but then you only have the printed value, which should suffice if it’s just a small map

borkdude11:09:47

since you’re getting a null pointer, it’s likely that x or y are nil, which you should see in the prn you already put there

borkdude11:09:23

@grierson I took a look at the ring spec: https://github.com/ring-clojure/ring/blob/master/SPEC maybe try query-string and then x=1&y=2 ?

grierson12:09:03

@borkdude I worked it out, compojure destructing was looking for a key :request because I added GET '/math' [request] (handler request) just needed to call the handler. Thank you for your help

👍 4
quadron18:09:17

How do I return a default value from a channel instead of blocking?

noisesmith18:09:37

what kind of channel?

quadron18:09:03

core async channel

noisesmith18:09:51

you can use poll!

noisesmith18:09:21

that returns nil if nothing is immediately consumed, then you can do what you like with that

✔️ 4
sb18:09:41

Do I need create the macros in clojure part and call with require-macros from clojurescript? as in this post http://blog.fikesfarm.com/posts/2016-01-05-clojurescript-macros-calling-functions.html

borkdude19:09:45

@sb In normal cljs projects, as far as I know, yes. Bootstrapped Cljs may have other rules. For these intricacies #clojurescript’s your channel. In .cljc even other rules.