This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-06-25
Channels
- # announcements (5)
- # babashka (23)
- # beginners (70)
- # cider (24)
- # clj-kondo (14)
- # cljsrn (2)
- # clojars (6)
- # clojure (195)
- # clojure-australia (1)
- # clojure-dev (2)
- # clojure-europe (27)
- # clojure-france (1)
- # clojure-nl (4)
- # clojure-norway (2)
- # clojure-spec (2)
- # clojure-uk (12)
- # clojurescript (3)
- # clojurewerkz (1)
- # core-async (21)
- # cursive (9)
- # datomic (37)
- # duct (3)
- # emacs (16)
- # events (4)
- # fulcro (34)
- # graalvm (12)
- # javascript (3)
- # jobs (4)
- # malli (1)
- # meander (3)
- # nrepl (1)
- # off-topic (27)
- # pathom (16)
- # re-frame (17)
- # reagent (19)
- # rewrite-clj (18)
- # sci (47)
- # shadow-cljs (179)
- # spacemacs (18)
- # sql (52)
- # tools-deps (80)
- # vim (27)
- # vrac (1)
- # xtdb (9)
@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 🙂
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...
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
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.
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...
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
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.
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.
Do you have tutorial or anything like that for the same?
I find the docs on the redis site good. I use this library https://github.com/lerouxrgd/celtuce. There are others 😄
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 []))]
pairs#))
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 []))]
pairs#))
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))
3
nil
(defmacro something
[& conditions-actions]
(let [clauses (partition-all 2 conditions-actions)]
(cons 'do
(for [[condition action :as clause] clauses]
(if (= 1 (count clause))
condition
`(when ~condition ~action))))))
• edit to handle a trailing single result as cond
doesmaybe 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 ~@(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 ~@
Another detail learned 🙂
(ins)user=> (def do +)
#'user/do
(ins)user=> (do 1 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
Good point!
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 [... ...] `(...)))
hehe. yeah
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!
Is it this one? https://github.com/stuarthalloway/presentations/wiki/REPL-Driven-Development
Running With Scissors? https://youtu.be/Qx0-pViyIDU 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?
Ah, I guess I could reduce and do an early return when the current and last value match the same predicate.
Something like
(reduce
(fn [prev curr]
(if (a-certain-symbol? prev)
(if (= prev curr)
(reduced false)
curr)
curr))
(first coll)
(rest coll))
maybe?(->> "ABBCCCCADTGGH" (partition-by identity) (map first))
;; => (\A \B \C \A \D \T \G \H)
@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)
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: https://github.com/weavejester/medley/issues/47 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
(defn ok? [pred xs]
(->> xs
(partition-by pred)
(some #(and (pred (first %)) (next %)))
not))
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)))
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))
::not-ok)))
(empty?)))
#'user/ok?
(ins)user=> (ok? even? [1 2 3 4])
true
(ins)user=> (ok? even? [1 2 2 3 4])
false
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)
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.
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
https://www.amazon.com/ideas/amzn1.account.AFAABBRGIVOWVKTHP5NOJU5LMROQ/3BSKWCYM12RBZ
I own a copy of ml for the working programmer, and it is freely available online now too
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
it would be if I could pick a parser and stick with it long enough to actually do some compiling
clojure.java.jdbc/insert-multi!
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
alternativly I can move (defonce system (atom nil))
to very separate and alone place without deps. It should work.
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.Keywords do not have to match an actual namespace.
won't having run create-ns
interfere with require elsewhere?
(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)
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.
: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
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))
?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.
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.
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.
1 line sounds simple easy for me to maintenance -- fixed that for you