Fork me on GitHub

@drewverlee I'm very curious as to what costs you feel are associated with a REPL workflow?


I have the exact same workflow whether I'm working in a local REPL, a local process that is running a (socket) REPL, or a remote process (again, running a socket REPL). There are no dependencies, and no special code (other than a JVM option to start a socket REPL)


I suppose I'm referring to the combined nature of several elements. Reloaded workflow, lisp being highly flexable, clojure being hosted. I didn't spend nearly as much time using python, but by comparison, the overall effect was that many options I have in clojure never even occurred to me there. I'm curious if people who have gone deep into other languages/communities feel the experience compares.


I used emacs' inferior python repl with python, but it definitely wasn't idiomatic. I ended up writing things in an odd style compared to others just so I could have a REPL based workflow. there's also some other things about python that don't really make a REPL based workflow easy.


I think it's definitely fair to say that the Clojure/Lisp REPL is unlike any other language's REPL.


Probably the closest equivalent is the JS console in a browser when running a single page app?


It's definitely the best live ecosystem that I've used a lot


I know people don't consider excel "real" programming, but it's got immutable values, a live environment, and reactive/flow based paradigm


True, true. My wife would probably agree with you as she's quite the Excel hound 🙂

metal 3

I'm generally interested in the future of programming and I think there's a lot of room for something more flexible than excel, but with a similar approachability and learning curve


I thought Aurora looked very interesting, from Chris... <blank> the guy who created LightTable...


Chris Granger


old guy's brain forgets names

😁 6

I don't use any sort of refresh/reloaded workflow -- don't need it.


As for other languages, that's probably a discussion for #other-languages or #off-topic


I find my Clojure REPL workflow to be very, very simple and lightweight. A socket REPL on any process. No dependencies. Half a dozen key bindings to simple commands.


(OK, so I also use REBL when developing against a local REPL and I do have a bunch of extensions written in ClojureScript for Chlorine, the Clojure/Script package for Atom -- but even if I use rlwrap around telnet I can still interact with any of those processes pretty easily)


Structured editing alone is a hurtle for many. Again, this isn't a negative thing, it just raises the bar on what you can do. I was showing a friend how I developed and he was more then a little overwhelmed. It just made me wonder if other languages had a similar skill cap. Anyway, I'll take my evening museing to off topic. . Thanks @seancorfield


Hmm, structural editing is a spectrum. I think you need paren-matching and paren-aware selection but that's at the "minimum" end. Auto-nesting based on indentation (parinfer) is very helpful -- but some people really don't like. Slurp/barf are useful but also nice-to-have.


I am semi-luddite here, I am sure, but highlighted paren-matching and auto-indent via pressing the tab key are all I ever use that are "Lispy", even in Emacs. slurp/barf/etc. I consider nice-to-have, but beyond my inertia level to bother with 🙂


Yup. I use wrap-with-( (or [ or {) a lot more than slurp/barf but I do use those occasionally. And I use expand-selection a fair bit (for copy/cut/auto-indent).


I'd be very interested to see what editor features Rich and Stu use. I know they both favor a basic setup. I think both use inferior-mode and a plain REPL.


I think that being really effective in Clojure necessitates investing in certain skills and a certain mental model of programming that is not the status quo in other languages. This can feel like a large up front cost


However, I have found that that mental model does extend to other languages


I do get frustrated when the REPL tooling for those languages doesn’t meet my needs tho 😄


Having spent 35 years professionally developing software in over a dozen languages, spanning several paradigms, I actually think that Clojure/FP has the simplest mental model and is by far the easiest to work with from an objective p.o.v. -- But extensive exposure to OOP (and especially exclusive exposure to it) makes the FP model harder to learn/internalize. So I think folks with, say, only a Java background will have a much harder time really getting up to speed with Clojure.

☝️ 6

I’ve been playing around with designing my own code editor focused on Clojure. It’s interesting how a very simple, elegant design can fall out of the idea that programming in Clojure is simply evaluating forms live in the process. Trying to map that to other languages gets difficult, which is a shame


there's a bunch of stuff that I was trying and failing to do in other languages/ecosystems and when I stumbled on to clojure, it was great to 1) have first class support for those workflows 2) get introduced to better versions of the the janky setups I was using


Something we take completely for granted in Clojure is that a unit of evaluation is precisely the (..`)` enclosed form that you select -- no more, no less. That is really, really hard to delineate in most other languages, particularly in statement-based languages (which is most languages). I don't think most folks really appreciate how valuable that fundamental concept is...

☝️ 15

fine-grained iterative compilation


Yeah... and perhaps most importantly, totally under user control, rather than at the whim of the compiler's algorithm 🙂


This, precisely, is why I prefer per-file reloading in Python rather than "sending a function definition to a Python process", which the Python package for Emacs supports. Python doesn't delineate the unit of computation as clearly as Clojure


I guess Jupyter creates a similar workflow to Clojure’s


for arbitrary languages

David Pham04:06:36

But you fight a constant global environment in Jupyter. How many things can we keep in our head. This is issue in jupyter. They become monsters.

Romit Gandhi06:06:24

Had anyone used Redis with clojure? I'm new to redis so if anyone provides tutorial or any kind of help then it will be very helpful for me.


I would recommend using raw Lettuce or Jedis. No need to use a wrapper. You can follow the Java tutorials directly.


yes. I use it quite a bit

Romit Gandhi06:06:04

Do you have tutorial or anything like that for the same?


I find the docs on the redis site good. I use this library There are others 😄

Romit Gandhi06:06:08

Okay, thank you so much.😊


I’m dabbling with macros to learn more about them. Does anyone know why I’m getting this error?

(defmacro something
  [& forms]
  `(let [pairs# (->> '~forms
                     (partition 2)
                     (keep (fn [pair#]
                             (when ~(first pair#)
                               (second pair#))))
                     (into []))]
Syntax error compiling at (/private/var/folders/_d/h17164js38j8rq2hg4bjfxbm0000gn/T/form-init14908207977537678733.clj:6:37).
Unable to resolve symbol: pair# in this context
I think I’ve seen example of using the gensym reader macro to create gensymed symbols in anonymous function parameter vectors before…. And in destructurings, too. So I wonder why it isn’t working here.


you are using symbol pair# generated in (fn [pair#] … form in the context of ~(first pair#) where it is unknown


simply replace ~(first pair#) with (first pair#)


Thanks @U04V4KLKC. I suppose this will give me the first element of the pair unevaluated, though, and I would like to evaluate it, so that I can filter (hence the keep) by truthy vals. How would I achieve that?


a pair could be ((and x y) (do-sideeffect)), like in conds


(in this case, wrapped in a list to keep them together)


coulde you give me an example of input for that macro?


(something (and false true) (prn "first") true (prn "second") nil (prn "third"))


In this case, I’d like to keep only (prn "second")


But since '(and false true) is a truthy val (a collection), I also keep that with the above code.


I was experimenting with something like a cond that ensure that exactly one predicate is truthy, which is why I’m trying to find all of the truthy predicates. I want to count them.


(defmacro something
  [& forms]
  `(let [pairs# (->> '~forms
                     (partition 2)
                     (keep (fn [pair#]
                             (when (eval (first pair#))
                               (second pair#))))
                     (into []))]
Seems to do what I want:
(something (and false true) (prn "first") true (prn "second") nil (prn "third"))
=> [(prn "second")]
I’ve no idea whether this is bad form, though.


you can do this without eval - the key is to remove the side effecting part at expansion time

(defmacro something
  [& forms]
  `(count (filter identity
                  ~(mapv first (partition 2 forms)))))
you need more complexity if you need to return the rhs of the thing that was truthy though


@U051SS2EU cheers 🙂 Let’s say that something is like a cond that evaluates those right hand sides whose left hand sides are truthy - that is, all of the right hand sides, not just the first. What I wanted to do with the code above was to collect all of the right hand sides and then evaluate them sequentially. Being accustomed to fns and not macros, I just went ahead and used gensymed symbols as I would normal symbols, but I understand that it gets more involved in macros. How would you accomplish what I describe?


(ins)user=> (something (and 1 false) (println 1) false (pri
ntln 2) true (println 3))
(defmacro something
  [& conditions-actions]
  (let [clauses (partition-all 2 conditions-actions)]
    (cons 'do
          (for [[condition action :as clause] clauses]
            (if (= 1 (count clause))
            `(when ~condition ~action))))))
• edit to handle a trailing single result as cond does


maybe I still misunderstand - that will run all actions where condition is true


with some elaboration, it could error if two conditions were true, and run none of the clauses or only the first or only the last


the hard thing with macros (to me) is to internalize the fact they are just a data transform before compiling, and segregating what needs to be executed while compiling from what needs to be emitted - note how in this example I try to clarify the distinction by embedding pieces of quoted forms inside normal data ops


or perhaps doing that makes it worse LOL


I think what you are doing seems a sensible way to approach it, to isolate the part that is different from the rest.


I think I struggle with exactly what you are describing, separating the code transformation from the emitted code - and also, since I’m just starting out with macros, understanding when to stay on either side of the boundary when using specific functions/macros/reader macros.


The rules are quite simple, but just counterintuitive to humans in my experience. Something you quote becomes part of the compiled, something you don't quote instead generates the thing compiled.


alternatively, one could say that your macro generates the thing compiled, and the simplest version of that is to quote a list and return it to be compiled


simple, but difficult to grasp once you leave the simplest cases


Yeah, the simple cases clicked almost immediately, and it felt enticing. And then, once I started mixing syntax quote, unquoting and gensym, boom! Things stopped making sense 🙂


I mean, I believe I understand each of those things separately, but not entirely how they mix (and don’t mix) yet.


Thanks a lot @U051SS2EU, this helped me greatly! 🍻


I like this version, too, but I don’t understand why I don’t have to add # to the end of condition and action. They are, after all, nested inside a syntax-quoted form - but, of course, an unqote-spliced form, too, which I guess negates the outer syntax-quoting when it comes to requiring gensyming symbols?

(defmacro something
  [& conditions-actions]
  (let [clauses (partition-all 2 conditions-actions)]
    `(do [email protected](for [[condition action] clauses]
             `(when ~condition ~action)))))


the for is not inside `, so needs no gensym


the @ plus ` do the same thing as the original cons and ', with more~ different verbosity


Yeah, it’s same same. Except that I don’t understand that detail. The for is inside ` from my perspective, just not directly inside.


the for uses ` repeatedly, but is not inside it


Maybe we’re talking past each other. There’s a (do which surrounds the for`. That looks like “inside” according to normal Clojure code conventions to me.


in my version there is no outside the for, and 'do` only quotes that single symbol


Yes, and that version I understand (well, actually I’m wondering why the quoted do does not shadow a (defn do that I create in the same namespace, but that’s a different matter)


But in my version, I do (heh) have a syntax quote surrounding the for.


Yet it does not require the destructuring symbols to be gensymed.


do does not exist, it's a compiler feature, if you had a var called do it would not get used in call position (don't make a var called do)

(ins)user=> do
Syntax error compiling at (REPL:0:0).
Unable to resolve symbol: do in this context


it is outside the ` again thanks to [email protected]


Another detail learned 🙂


(ins)user=> (def do +)
(ins)user=> (do 1 1)


that's why you should never call anything do


but yeah, my code does assume the reader knows do is special


Ah, so it was as I suspected - the unquoting negates the quoting. Sounds obvious when I write it out like that… But not when fiddling with gensymed symbols.


it's not just negation - it's like each ` pushes to a stack, and each ~ pops, and when you reach empty stack there's no quoting


I do not recommend writing code that requires keeping track of that stack :D


I guess that’s why I liked your version as well, because it squished the stack manipulation close together for an easy overview, and isolated them to a leaf node, as well.


a compromise:

(cons `do (for [... ...] `(...)))


or even

(for [... (cons `[do] conditions-actions)] `(...))


(that requires my modification which allows either one or two element inputs in the iteration)


I liked that one, it was nice.


BTW, I avoid shadowing/replacing symbols used in Clojure core functions/macros/special whatevers myself, as well. I really dislike what I see some people do sometimes, like

(:refer-clojure :exclude [+ * and assert or cat def keys merge])


(That’s a cheeky stab at clojure.spec.alpha, by the way…)


… But I’ve seen other people do stuff like that, as well, and I don’t understand it. I know naming is one of the hard problems of programming, but is it hard enough to justify recycling names? 😄


mornin folks, 👋 I'm searching for a video of a talk. I am pretty sure it was on Youtube and given by Stuart Halloway. I could be wrong on both counts. The video showcased a development style using the repl where everything was def'ed and evaled inline, and IIRC it sort of eschewed separate tests for a pile of (comment)ed code at the bottom of your file. It was very interesting, and I'd like to revisit it, but I can't find it!


Running With Scissors? There's stuff on 'Rich Comments' and tests at 20 mins in


"Running With Scissors?" is along the same lines, but the video I'm thinking of had a live coding portion (where the presenter was demonstrating live in a repl)


@UHK8B8STX hmm no it wasn't that one, like I said , I remember the video having a live demo of a buffer + repl as the presenter demonstrated their workflow.


Anyone have a quick way of determining that certain values are not adjacent in a coll? E.g. I need to make sure that symbols do not appear right next to each other in a sequence, but they are allowed to appear 1+ spaces apart.


That should be doable in one pass over the coll. Are you looking for quick as in a nice function in core, or quick as in execution time?


As for nice function in core, I don't have one off the top of my head, sorry.


just a proper functional way do it that won’t tear my hair out.


Ah, I guess I could reduce and do an early return when the current and last value match the same predicate.


Something like

  (fn [prev curr]
    (if (a-certain-symbol? prev)
      (if (= prev curr)
        (reduced false)
  (first coll)
  (rest coll))


Yes, exactly. 😄


(->> "ABBCCCCADTGGH" (partition-by identity) (map first)) ;; => (\A \B \C \A \D \T \G \H)


And I always forget about dedupe


So just (= (dedupe coll) coll) will tell you if there are consecutive dupes


@U093SNDV5 wouldn’t that only work when comparing values by equality, not any pred, e.g. symbol?


(= (dedupe '[a b c]) '[a b c]) if adjacent symbols are not allowed, then this will not register that


Ok, this seems to work and I think it will be close to linear time, right?

(defn- non-adjacent
  "Return `coll` if no adjacent values satisfy `pred`."
  [pred coll]
  (reduce (fn [coll val]
            (if (and (pred val)
                     (pred (peek coll)))
              (reduced nil)
              (conj coll val)))
          (empty coll)

(non-adjacent symbol? [1 'a 2 'b]) ;=> [1 a 2 b]
(non-adjacent symbol? [1 'a 'b 2]) ;=> nil


OK. I understood "Anyone have a quick way of determining that certain values are not adjacent in a coll" to mean that no two adjacent values in a sequence can be equal...


Slightly related: It's more useful for parsing than validation


@U093SNDV5 yeah, re-reading my original question, it is a bit unclear what I meant. Sorry about that.


@U04V15CAJ so it’s like split-with, but can split into more than two collections? Seems useful.


@U4P4NREBY The split-when function is lazy btw, so you could short-cirtcuit when you find an error

metal 3

(defn ok? [pred xs]
  (->> xs
       (partition-by pred)
       (some #(and (pred (first %)) (next %)))
I'm an idiot. The problem is to find any adjacent elements where a pred is true. So partition by the pred. And if any of those sub-sequences match the pred on the first, and contain more than one element then there are adjacent elements. That'll terminate early and partition-by is lazy.


how about this?

(defn ok?
  [pred xs]
  (empty? (filter #(apply pred %) (map vector xs (rest xs)))))



(ok? symbol? ['a 'b])

Execution error (ArityException) at cuphic.core/ok?$fn (form-init9008355435483086642.clj:3).
Wrong number of args (2) passed to: clojure.core/symbol?


@U093SNDV5 that does seem to work.


yeah, it’d have to be (ok? #(and (symbol? %1) (symbol? %2)) ['a 'b])


if you only want that sort of thing you can do

(defn ok?
  [pred xs]
  (empty? (filter true? (map #(and (pred %1) (pred %2)) xs (rest xs)))))


hmm, what about something like (defn ok? [pred xs] (empty? (keep (partial apply pred) (partition 2 1 xs)))

👍 3

oh, with the and construction above...


final answer

(defn ok?
  [pred xs]
  (->> xs
       (partition 2 1)
       (keep (fn [[x y]]
               (when (and (pred x)
                          (pred y))
(ins)user=> (ok? even? [1 2 3 4])
(ins)user=> (ok? even? [1 2 2 3 4])

Stephen Lester16:06:17

I'm sure this gets asked a bunch but search doesn't really work super well for me here: what books were the most impactful for you? I am thinking mostly of functional programming/FP software design/FP software architecture in general, it doesn't have to be Clojure specifically (but ideally yes)


SICP hands down


The Little Schemer (goes with SICP)

👍 6

The Little Schemer is my favourite book on just programming and thinking like a programmer. There’s also more in the series. A Philosophy of Software Design by John Ousterhout is a bit OO-focussed but is excellent (but also controversial - I’ve read a few articles attacking it - that’s maybe what makes it interesting!) Clojure-wise: Joy of Clojure. I received my copy after working professionally in Clojure for a year. But I enjoyed reading it nonetheless (selfish reason being that it confirmed that a few practices I was following independently were probably decent) I’d also recommend Elements of Clojure as a fun and short read. My main reason to recommend these books is that they all offer something a bit different from the standard tomes on programming languages and best practices.


Elements of Clojure


Pragmatic Programmer when I was a junior


As a side question, has anyone read "ML for the Working Programmer"? It's a Standard ML book that I've got a copy sitting on my shelf un-read. It's supposed to be really valuable (edit: in terms on content...) but it seems to be overlooked for Haskell recommendations


it is part of the clojure bookshelf


There's a clojure bookshelf? 😄


rich made a list


I own a copy of ml for the working programmer, and it is freely available online now too


Have you read it?


I have referred to it


but I also happen to have a side project (for like the last four or five years?) of writing a standard ml compiler, I haven't referred to it for clojure work


That sounds really interesting!


it would be if I could pick a parser and stick with it long enough to actually do some compiling


What are you writing it in?


(If you don't mind me asking 🙂 )


the end goal is to compile to lua as well


Lua gets everywhere, I can see the attraction


Maybe a thread for this non-Clojure discussion, or take it to #off-topic ?

👍 12
Dan19:06:33! is painfully slow when inserting many rows into a remote postgres database. Tried both inserting as a sequence of maps and sequence of lists of column values. Anybody have any thoughts?


@daniel.stephen.lee happy to follow up in #sql to avoid cluttering this channel with DB-specific stuff...


(ns api.config
  (:require [server.core :as server]))

(def integrant
  {::server/host (System/getenv "HOST")
   ::server/server {:port (some-> (System/getenv "PORT") (Integer/parseInt))}})
Is a way to refer to ns by keywords, but not really make them as dependency in :require? So I want to refer to ::server/server for configuration, but not really need to use code from this namespace. It can make a potential issues about loop dependency when want to use configuration somewhere.


::server/host is just a shorthand for :server.core/host, if you use the expanded form the namespace doesn't need to exist


I wanted refer by alias, because during prototyping I change names a lot


alternativly I can move (defonce system (atom nil)) to very separate and alone place without deps. It should work.


so this is an atom where integrant keep the init


yeah, probably make sense then


If you want an alias without a source file corresponding to the resolved name:

(alias 'server (create-ns 'server.core))
That works even if server.core doesn't exist as a namespace but you want ::server/host to refer to :server.core/host so you can rename as much as you like.

👍 3

Keywords do not have to match an actual namespace.


won't having run create-ns interfere with require elsewhere?


but probably the right solution is to move system to alone ns


(that is, the ns already exists (and is empty and useless) so require becomes a no-op)


there really, really needs to be a way to alias w/o creating an ns in Clojure(Script)


ech I shouldn’t code after midnight 😉


it’s like the biggest QoL thing I would use every day, working in a system built with Clojure and ClojureScript and using clojure.spec on both ends


There's a ticket for it. A very long-standing ticket.


I wonder if I could write a version for CLJS that “interns” a namespace in the analyzer but that would probably run into the same issue that noisesmith is talking about, where if the ns ends up being real later that a require of the ns would be a no-op


that's an intrinsic problem to using create-ns just to make an alias, but isn't any more wrong in cljs than clj - I just wanted to bring it up in this convo since at least for the moment the namespace existed


also, if an alias that doesn't point to a real ns is OK, why not :server/host , it's not like we usually have deep structures of nested system components


Yup. That's mostly the approach we've taken at work.


oh you don’t use namespaced keywrods with integrant?


:server/host is namespaced


also, integrant is perfectly happy to use eg. [:server :host] - keywords aren't baked in, the dispatch just has to match the extension


ok I asked wrong. I mean you don’t use alias for integrants.


oh I didn’t try to use something else, than keywords. Interesting.


and you keep init result in separate ns to make it readable from all places? So having something like this:

(ns api.system.state)

(defonce state (atom nil))


to avoid loop dependencies


ideally you don't need to access the system from a global binding, if I need it for dev reasons I just use def, and I break dependency loops by using functions that take arguments


I don’t want to make everything more complex to pass the HOST variable etc. I wanted keep it simple.


IMHO global mutable state is the opposite of simple, but OK


1 line sounds simple for me to maintenance


1 line sounds simple easy for me to maintenance -- fixed that for you


How do you avoid using def not starting ig/init when load REPL and overwrite def value when reload ns? If this will be overwritten without halt before, then it wouldn’t work, because port will be blocked. Unless I don’t see obviously solution here it is still simpler for me, than playing with this issues.


It can be done alter-var-root but I would say this is simpler


If I want to manipulate the system in the REPL, I'll def it in a comment, just for use in the REPL. Reloading that namespace wouldn't overwrite it, because it's not in the code, it's in a comment.


For normal use, my -main sets up the system and passes it into the call chain -- so there are no globals.


ok I am going sleep, goodnight! 🙂


but you are right, maybe def will be enough.


1 line sounds simple easy for me to maintenance -- fixed that for you