Fork me on GitHub

(def ^:dynamic test 10000)

(defn test-foo []
  ((fn [] test)))

(defn test-bar []
  (map (fn [_] test) (repeat 3 0)))

(binding [test 1]
  (test-foo)) ;; => 1

(binding [test 1]
  (test-bar)) ;; => '(10000 10000 10000)
Can some explain why in the second case binding doesn't work?


(defn test-bar []
  (doall (map (fn [_] test) (repeat 3 0))))


binding will pop-thread-bindings before return. Since map returns a lazy sequence, it is materialized (by repl) after bindings are cleared.


(macroexpand-1 '(binding [test 1] (test-bar)))
 (clojure.core/push-thread-bindings (clojure.core/hash-map (var test) 1))
 (try (test-bar) (finally (clojure.core/pop-thread-bindings))))

👌 8

lazy evaluation


bonus: there is a slightly different form of test-bar that would do what you’re expecting


the question is: how do you bind the value of test earlier?


What's a good websocket client for clojure (not clojurescript? ?)


That’s the one I’ve been using so far


ok, thanks. I'll give it a try then. I was just worried because it says`Its main purpose is testing WebSockets services without a browser`which doesn't sound like it's ready for constant/stable/high volume production usage


I see, I haven’t used it in high volume usage so I am not sure of the limitations


looks like you can use aleph for this: > WebSocket clients can be created via `(aleph.http/websocket-client url)`, which returns a deferred which yields a duplex stream that can send and receive messages from the server.


ah nice. I've heard of aleph before. Definitely looks more mature but also a way heavier dependency for only a websocket client


gniazdo is only 160~ loc wrapper for the jetty java websocket client, can serve as inspiration to code a custom client if you need something that gniazdo doesn’t support.


Most of the clients seem to be for cljs, not many options for clojure itself, there’s some space for a good wrapper around some java websocket library with more functionality than gniazdo


I'm just getting started with clojure myself. I'll start with gniazdo and go from there. I would think that it's "powered by jetty" makes it pretty stable.

👍 4

That’s what I thought when I started some time ago with a personal project that needs websocket on clojure, and haven’t yet found anything I cannot do with gniazdo, just haven’t really put it into high volume production mode to tell


@daniel415 depending on how comfortable you feel with interop, there’s built in to java>=11

👍 4

Includes websocket functionality


I'm comfortable in Java. using seems like a good idea


Thanks for all the tips and hints 🙂


There's even already with websocket support

👍 4

@daniel415 if you end up giving this a try, please let me know if it works for you or if there is anything you are missing! :thumbsup:


I have a client too but i haven’t open sourced it yet


maybe I even replace my clj-http dependency with that


Yeah clj-http is heavy and old, and stable


Hello, I'm using advent of code to teach me a little bit of clojure. I'm resolving Day 3: Crossed Wires. This is the code so far and I would like a little bit of feedback on two things (and on anything that you think could be improved):

(ns cljbrave.core)

(defrecord Movement [direction length])

(defrecord Coordinate [x y])

(def origin (->Coordinate 0 0))

(defn movements
  "R6 becomes (:R 6)"
  (->Movement (keyword (str (first string-coordinates)))
              (Character/digit (last string-coordinates) 10)))

(defn get-movements
  "R6,U7 becomes [(:R, 6),(:U, 7)]"
  (map movements (.split string-of-coordinates ",")))

(defn moveUp [p] (+ p 1))

(defn moveDown [p] (- p 1))

(def directionsFn {
                   :R {:x moveUp :y identity}
                   :U {:x identity :y moveUp}
                   :L {:x moveDown :y identity}
                   :D {:x identity :y moveDown}

(defn moves1
  [movement length directionFn]
  (if (= length 0)
    (let [next (->Coordinate ((:x directionFn) (:x movement))
                             ((:y directionFn) (:y movement)))]
      (cons next (moves1 next (- length 1) directionFn)))))

(defn moves
  "(0,0) and (:R, 2) will give ((1,0), (2,0))"
  [start movement]
  (moves1 start (:length movement) ((:direction movement) directionsFn)))

(defn generate-coordinates
  "R1,D1 becomes [(1,0),(1,-1)]"
  (let [instructions (get-movements raw-instructions)]
    (loop [start origin
           step (count instructions)
           reminder instructions
           result []]
      (if (= step 0)
        (flatten result)
        (let [path (moves start (first reminder))]
          (recur (last path) (dec step) (rest reminder) (conj result path)))))))
• can generate-coordinates be done better? Ideally what it should do is a (flatten (map (partial moves origin) (get-movements raw-instructions) where moves is not actually partial but it should use the last element of the list generated by the step before. • am I using correctly defrecord or should I use deftype in this case?


I haven't looked closely at the code yet, but clojure's flatten is pretty much always the wrong thing


the usage of records without implementing protocols is a code smell, you probably want maps here


(x,y) is a weird notation - idiomatic for sequential data is [x y] , for maps {:some-key x :other-key y}


> am I using correctly defrecord or should I use deftype in this case? use neither, idiomatic for this kind of thing would be hash-maps with keyword keys


moves1 (and any other function that calls cons as its tail) should be a lazy-seq to better utilize the stack and integrate with clojure's other collection functions


you can literally change (cons x y) to (lazy-seq (cons x y)) to get a lazy result


any loop that steps element by element (using count and rest) can be replaced by map or reduce, which will significantly simplify the code


here I think you need reduce since you have an accumulator


you can avoid using flatten by changing conj - likely you want into rather than conj


perhaps into would also need a cat transducer, but probably not here


Thank you @noisesmith, will work on what you suggested, what do you mean by "implementing a protocol"?


the reason we have defrecord and deftype is in order to have type based method dispatch


if you dont' have methods that dispatch on type, you don't need a defrecord or deftype, and we just use hash-maps, lazy-seqs, sets, vectors etc. directly


protocols define methods that dispatch on the implementation of various types

👍 4

In theory, you could use defrecord as a schema for your maps, but it is recommended to use spec for that instead.


I wanted to give a name to the structure basically


also it's not much of a schema - you can remove keys and it turns into a map, fail to attach them and they are just nil, add them and it quietly adds them regardless of defined fields


@dierre_spam I'm sure this sounds odd but we don't idiomatically name our data structures - it's a lisp thing


Ya, Spec is the recommended way to name your structures.


even then, you contextually assert a datum matches a spec, you don't attach a spec to a structure at rest


that is, at the point where the data are consumed or generated, and not where they are defined


But, getting used to most things not having name is important. Normally, only shared domain entities would be specced and given a name and a clear specification


In effect, I'd suggest your name be the name of the variable/argument.


I actually just wrote a kind of lengthy reply talking a bit about this: As a beginner, it might be hard for you to follow what I'm talking about, but it might still help a little to move the needle in your head.

👍 4

I've read your post. My view on this is that, while I was writing the code, first of all I noticed that you need to be really clear with the naming of the variable because that's basically the type information. What I don't like though is that I need to read a good amount of code to understand what this argument is: I need to see where the function is called and what is actually passed. It's true that I probably care only about this argument is used inside the function (which are probably the first citizen of this language), but practically speaking to me it's faster to understand the code if I can put something like ^Record in front of the variable name so I can click on it and see what is that about.


Thanks for reading. I understand your feeling. I had it too when I started, but it has mostly dissipated. I'm not sure what made it dissipate to he honest. I think it was just knowing Clojure better. I got better at following things along and didn't felt I needed record definitions for everything anymore. When I started, I was definitely making use of records more, and adding unnecessary type hints, and hoping for more type info.


I think my question would be... Why do you need to know what is actually passed to the function?


Ok, in case of the clojure sdk I feel the names are enough. A quick search of the function signatures like map, reduce always solves my problem because there aren't these many concepts in clojure, but I think that when it comes to the domain of the application, it's easier to reason if you have types. I'm not saying I want to do type driven development, but for now I feel it's not wrong to give types to your domain.


For your domain, it helps a lot to use spec


Sometimes I even have a domain.clj namespace where I spec the whole domain model


One thing that is common in Clojure is hard to explain. But its a style where your data is stored in a data-store. This can be a persistent one, like an actual DB, or an in-memory one, which could just be a map inside an atom.


Your entire domain data is stored there


And you can have a full Spec for it all


But your functions don't directly access this data-store


They all just take a partial set of the data from the store as input directly. Those normally end up being clear enough just by having good arg names, doc-strings, destructuring, etc. Though you can also choose to spec them fully or partially.


And they don't write to the data-store either.


They could perform IO, but not to the data-store


And now you have other functions, which act as orchestrators. So if your domain has 10 logical use cases/operations. You have one function for each. And its that orchestrating function which will query the data-store and update it accordingly, and it'll make use of the other functions I just talked about to implement the business logic


Yea exactly


Basically, you need to start thinking in terms of functions that do things, and things that you want done.


Not in term of Nouns and objects and what they can do


I will try to give it a try. Thanks


Be patient, its one of the big mental shifts that Clojure requires, and it takes a lot of time.


So I changed from this

(defn generate-coordinates
  "R1,D1 becomes [(1,0),(1,-1)]"
  (let [instructions (get-movements raw-instructions)]
    (loop [start origin
           step (count instructions)
           reminder instructions
           result []]
      (if (= step 0)
        (flatten result)
        (let [path (moves start (first reminder))]
          (recur (last path) (dec step) (rest reminder) (conj result path)))))))
to this:
(defn path-accumulator
  [result current]
  (let [current-path (moves (:start result) current)]
    {:start (last current-path) :acc (flatten (conj (:acc result) current-path))}))

(defn generate-coordinates-dos
  "R1,D1 becomes [(1,0),(1,-1)]"
  (:acc (reduce path-accumulator {:start origin :acc []} (get-movements raw-instructions))))


I only have a slight problem which is this:

(deftest can-generate-a-list-of-coordinates
  (testing "R2,D1 becomes [(1,0),(2,0),(2,-1)]"
    (is (= (generate-coordinates "R2,D1") [(->Coordinate 1 0), (->Coordinate 2 0), (->Coordinate 2 -1)]))))

(deftest can-generate-a-list-of-coordinates-dos
  (testing "R2,D1 becomes [(1,0),(2,0),(2,-1)]"
    (is (= (generate-coordinates-dos "R2,D1") [(->Coordinate 2 -1), (->Coordinate 1 0), (->Coordinate 2 0)]))))
The order is not the same...but the values are...


flatten returns a list, conj order on lists is different from vectors


(this may not be the issue)


yeah - it is the issue - you are flattening at every step, where before you flattened at the end


(ins)user=> (conj [:a :b :c] :d)
[:a :b :c :d]
(ins)user=> (conj (flatten [:a :b :c]) :d)
(:d :a :b :c)


if you need to flatten, do it outside the reduce, ideally you can rewrite such that you never call flatten


calling into instead of conj will probably give you the right result without any flatten call


(ins)user=> (conj [:a :b :c] [:d :e])
[:a :b :c [:d :e]]
(cmd)user=> (into [:a :b :c] [:d :e])
[:a :b :c :d :e]


Ya, favor into over flatten, it is faster as well

Alper Cugun22:12:32

Does the order in which I define functions in a file matter?


You can only reference things defined before


So you need to order functions and defs based on their dependencies


So if B uses A, you need to have A first and B after in the file


And be careful, at the REPL, it might work even in the wrong order, if you have evaluated the functions in the right order. But it won't work when in production


Only things that have been evaluated before exist and can be used. And Clojure evaluates things in a file from top to bottom when required. But at the REPL, you can force evaluate things in any order, which is why it could work in the REPL, but it won't in prod, where Clojure require will evaluate things from top to bottom.


You have some flipped arrows of causation


Clojure evaluates files form by form top to bottom to match the behavior of typing one form after another into the repl


Hum, I'm not sure I'm seeing where I have flipped arrows ?

Alper Cugun22:12:33

This is a bit different from other languages, right?

Alper Cugun23:12:47

Also now I have this: Syntax error macroexpanding clojure.core/defn at (core.clj:60:1)

Alper Cugun23:12:54

Which is… 🤯

Alper Cugun23:12:52

I changed something small somewhere else in the file.


It is reminiscent of some standard ml implementations and kind of matches the definition of standard ml


It's different than most OO languages, which compile classes together yes


That means you are not using defn correctly


You have a syntax error in it


Review your syntax to defn

Alper Cugun23:12:33

I did, it may be:

"For a vector of steps of the form ["R" 10] etc., generates a vector of points starting from origin."

Alper Cugun23:12:37

That was my docstring.

Alper Cugun23:12:42

Which was fine up until just now.

Alper Cugun23:12:51

If I remove the docstring, it does not complain anymore.


You need to escape the inner quote, or use the single quote instead


The repl is stateful, and you are accumulating state in it as you work(all your definitions and redefinitions), if you end up in some bad unknown state it can be good to start a new repl so you are working from a known state

Alper Cugun23:12:37

Is that why this has been working up until now?

Alper Cugun23:12:44

Also doing fine reloading the file.


It hasn't been


"For a vector of steps of the form ['R' 10] etc., generates a vector of points starting from origin."


"For a vector of steps of the form [\"R\" 10] etc., generates a vector of points starting from origin."


You might be think it was working fine, but it wasn't


Reloading the file doesn't remove the old file from the running repl, so things would still work. Unless you use a library which can first unload things and then reload, such as tools.namespace


You might have had an R name defined in which case a string followed by R followed by another string might not throw an error


Presumably you are here because you are encountering errors, so if that code was later in the file then code producing the error it would never get evaluated


So, I dunno what circumstances would lead you to believe the above was working, but I can tell you it was not

Alper Cugun23:12:10

If I start the repl again, it does not automatically load the core.clj?


that depends on the config of job starting your repl


Generally no

Alper Cugun23:12:56

I'm new, I just created a project.


clojure itself only autoloads user.clj, tools can make it load other things at startup for your convenience


How are you starting the REPL ?


Normally, most REPL start you in a user namespace, which requires clojure.core automatically


In other namespaces, if you have a ns declaration, it will require clojure.core automatically as well


He has a new lein project, core.clj is referring to the file lein generated for him,not clojure.core

Alper Cugun23:12:07

I can't get it to work anymore, so that means whatever I had that was working was in the REPL and not in my source file?


Yes, you had some accumulated state in your repl


Ya, make sense


What IDE do you use?


But your source file is not rebuilding that state do it doesn't work in a fresh repl


If you use Cider, you can run C-c M-n r to sync your REPL to your source files


The most common state to cause that kind of problem is names


With Calva ?

Alper Cugun23:12:11

But I kill the REPL and restart and then it should be clean?

Alper Cugun23:12:26

And all the reload I was doing was for nothing?


For example you define some name but then remove that definition (or name include it) in your source file, which doesn't stop subsequent code from referring to that name


But in a new repl will cause an error


reload is not a feature of clojure, so I am not sure what you were doing

Alper Cugun23:12:30

Now it's complaining: No matching method abs found taking 1 args even though Math/abs exists and works.

Alper Cugun23:12:59

(use 'fwd.core :reload)


Yeah, that just accumulates more state

Alper Cugun23:12:53

But how do I quickly edit/run cycle then?

Alper Cugun23:12:59

Restarting the repl takes too long.


it will depend, but usally you just send a form at a time to be evaluated to your repl


a lot of editors with some kind of clojure mode will have short cuts for that kind of thing, but I find myself just using copy and paste more and more


well, kill and yank


but I am more minimalist about tooling than most


That's a bit peculiar 😛 Why not use editor send to repl, which is effectively the same but more convenient ?


In calva, you want to do: ctrl+alt+c ctrl+alt+n for Load current namespace in the REPL window


because I get a more consistent always working experience with copy and paste


Or you want to do ctrl+alt+c ctrl+alt+e if you want to evaluate only the current form


But in the latter case, you need to understand order


the accumulating state nature of the repl is something you get use to and getting it in to some unknown state where you need to restart it to recover is something you'll need to do less often

Alper Cugun23:12:26

Lein run throws a huge exception: Exception in thread "main" Syntax error compiling at (fwd/core.clj:75:5)

Alper Cugun23:12:36

But if I paste the offending code in, it's fine.


Most editors for use with Clojure have a way to customize them, or someone has already customized it for you, such that you can start a REPL session, or connect to an existing REPL session, within them, and a keystroke is bound to the action "wherever my cursor is, take the surrounding (or perhaps preceding) expression, and send it to the REPL for me"


with no copy/paste on your part


likely it is only fine after the error


e.g. in a fresh repl where you haven't gotten the error it won't work

Alper Cugun23:12:17

I haven't bothered to learn code's support yet.

Alper Cugun23:12:26

I was under the impression that what I was doing was working.


Hum... maybe I'm getting the Calva commands wrong, it might be alt+ctrl+c enter and alt+ctrl+c e not sure, I'm going off the documentation :cop:


That particular keystroke/workflow I would say is a very good one to find in your IDE/editor of choice.


basically the form that causes the error changes some state in the repl while being compiled that causes it not to error again


You have to tell us what you are doing though, if you need further assistance


one way to confirm that is to take a fresh repl, and evaluate each form from the namespace top to bottom, one by one, instead of loading the namespace all at once


you will get to the form that causes an error, get the error, and then try that form again immediately afterwards and get no error

Alper Cugun23:12:01

So I copy pasted all my source code in a fresh repl.


I would expect the error you get to include a little more information

Alper Cugun23:12:16

(Because I want to go to bed but I want this to work first.)


that won't make it work


I am just explaining the process you can go through yourself in the future when you encounter something like this to try and figure it out


your problem is something on the form starting on line 75

Alper Cugun23:12:23

I mean yes, I read the line and pasted that code in the repl and that was fine.


A form is something between parethensis*


or a number, or a keyword, or a standalone reader macro


or a hash map, etc.


Well, ya, that's the beginner summary 😛


it's anything clojure's reader can consume as a unit


but you pasted it after you had already tried to load your code


and that loading changes the state of the repl

Alper Cugun23:12:05

No, fresh repl.

Alper Cugun23:12:09

The error is from lein run

Alper Cugun23:12:19

Then I do lein repl


oh, lein run is a whole other thing

Alper Cugun23:12:33

And paste in:

(defn find-overlaps
  "Takes two lists of points and returns the ones that are in both."
  [points1 points2]
    #(= origin %)
    (clojure.set/intersection (set points1) (set points2))))

Alper Cugun23:12:44

(def origin {:x 0 :y 0})

Alper Cugun23:12:58

It doesn't complain about a syntax error then.

Alper Cugun23:12:04

So I guess there is no syntax error?


is origin defined before that line? do you ever call require on clojure.set?

Alper Cugun23:12:26

Do I need to call require ?


What is the error? Can you paste it here ?


clojure.set is not guaranteed to be present, you should require namespaces if you use them


Yes, you have to require every other namespace you use before using them


if you use something defined in another namespace, you need to require that namespace

Alper Cugun23:12:56

But in the repl it works?


repls can load namespaces

Alper Cugun23:12:10

I mean yes, that would explain it.


that doesn't mean they are always present


Some REPLs auto-require clojure.set as a convenience


the set of loaded namespaces is part of the repls state


require ensures they are present

Alper Cugun23:12:27

How do I require in a file?


That gets complicated 😛 Are you trying to require your own source file? Or one from clojure ?


for the repl, you can one-off require (require '


you can require in a file the same way you do in the repl


but the convention is to do it all at the top as part of the ns declaration


The only error I’ve seen pasted had fwd/something in it. Have you pasted the actual error you’re seeing?

Alper Cugun23:12:52

Is there no way to require java.lang.Math?


that's a class, not a namespace


they are auto-loaded when referenced

Alper Cugun23:12:34

It was working nicely in the repl.


if you want to use abs, use Math/abs, it auto-loads

Alper Cugun23:12:44

Not if I do lein run.


show us the actual error

Alper Cugun23:12:29

Caused by: java.lang.IllegalArgumentException: No matching method abs found taking 1 args


OK, what does your abs call look like? what's the type of the argument?


for example

user=> (Math/abs :a)
Execution error (IllegalArgumentException) at user/eval177 (REPL:1).
No matching method abs found taking 1 args

Alper Cugun23:12:14

(Math/abs (get point :x))


OK, what does (get point :x) return? try printing it, I bet it isn't a number


the distinction here is that it's not saying abs doesn't exist, it's saying that abs isn't implemented for the type provided

Alper Cugun23:12:01

You're right.

Alper Cugun23:12:16

It is for a while and then suddenly it's not.

Alper Cugun23:12:43

But yeah, I'm not going to be able to fix this tonight anymore.

Alper Cugun23:12:59

I'll read up tomorrow whether a decent edit/run/test cycle is possible on my setup.

Alper Cugun23:12:16

One that isn't lying to me.


You just need practice

Alper Cugun23:12:33

And also doesn't take the 5 second delays of lein run.


Eventually you'll learn how to be productive in the REPL

Alper Cugun23:12:55

Stack Overflow said not to use reload.


I would agree

Alper Cugun23:12:39

Isn't it a bug if it doesn't work properly?


It does work properly


You just arn't using it correctly


That's why you need to learn more about it, and practice using it some more


Feel free to ask questions about it here, we'll do our best to make it clear

Alper Cugun23:12:11

I mean in haskell this was working cleanly at least.


I don't know what that means to be working cleanly


Clearly, if you were not using Haskell correctly, it would also not work

Alper Cugun23:12:01

Keep reloading the source file you're working on into the repl.

Alper Cugun23:12:10

Anyway, I'll read up more tomorrow.

Alper Cugun23:12:13

The language is nice.

Alper Cugun23:12:18

The stuff around it…


It doesn't seem you were doing that in Clojure though


Specifically, if I remember correctly, GHCi always clears bindings and reloads the new file.


This has the problem that all state is lost, and its a big downside when working in GHCi