Fork me on GitHub

Hi! I’d like to build a small command line tool with clojure, that takes some user input and generates a webpage from it. I made this initially with clojure using (read-line) for the input. I am now trying it as a clojurescript project, but am getting errors that read-line is not defined. Is read-line not a function with clojurescript, or is there an issue with my project setup?


how are you running the js? node?


yah, node!


and what made you switch from clojure to clojurescript?


if the startup time is an issue, you can try babaska,


Mostly to keep the startup time low. I’m new to clojure, and wanted to try to keep the amount of time i spent on build tooling to a minimum, as it was hurting my momentum.


so i started with a ‘lein new app’ app, and it worked welll enough, but calling it (even as an uberjar) took about 20 seconds.


the app is intended to be a note taker of sorts, where I can call it, it’ll ask a couple of inputs that I type in quickly, and then keep on with my day.


I would definitely check out in that case

☝️ 1

I love what i’ve used of babashka so far, but correct me if i’m wrong…you can’t really use dependencies with it?


I'm actually not sure


clojurescript is also pretty reasonable


My app is using cheshire to fetch from a json api and hiccup to build up the site.


and I know that babashka has its own curl, but wasn’t sure about hiccup.


Just for full context: it’d be a front page for a website that had that day’s sunrise/sunset and moonphase along with observations i’d made that day and my own comment on the weather.


So I’d want a way to write and save these observations, plus something that took these observations, plus a weather api, created a page, and replaced it as the new index.html in my website folder


thanks! I’m excited for it, and it seems scoped well enough as a learner project…while providing good use.

metal 1

it seems like cljs doesn't have read-line since read-line would depend on the platform, but it would definitely be possible to do with js interop to the node js utilities


ah good point, and thank you for the link! Do you know if callbacks are handled in a special way with clojurescript?


it seems like most of the node ways of getting input return a callback, but wasn’t quite sure how to transpose that structure to clojure just yet.


there's clojure.core.async, but there's a bit of a learning curve on that


Ah, hmmm. Perhaps it might be better to check out the build tooling a bit more--so I can keep the working code, but try to just reduce the start up time.

👍 1

I’ve heard GraalVM made things faster, but wasn’t sure if it was a rabbit hole to get it set up


Or….just have two projects…one that builds the site (it’d be done as a cron job, and i wouldn’t notice the startup time)…and a babashka script that takes some inputs and writes it to the right file.


Thank you for the help!


I've tried GraalVM and it really is some cool tech. I've found that it can be pretty hit or miss depending on what libraries you're using


depending on the library, it can definitely be a rabbit hole


Babashka includes Cheshire by default, and I'm almost certain you can do hiccup in some way. I would recommend it over clojure script if only because it's so much simpler to use.


oh, awesome. Thank you, @U7RJTCH6J and @U7S5E44DB!


Hi, can someone image a reason to write a function like this #(= % %) instead of something like (fn [x] true) ?

Tamizhvendan S10:07:52

You can make use as well. One potential use case is using it as a placeholder argument to call a function that expects a “single arity function” as parameter


Thank you! I figured that just wanted to make sure I didn't miss something important 😋

👍 1

there's at least one argument that is false for #(= % %) but true for the other alternatives offered: NaN


this could be a badly constructed NaN check


I am at a loss of what is happening once again. I am trying try Datomic, spent the whole week, still can't do anything. The aws setup part works, if I set the credentials explicitly even stuff like datomic client access <system> works, I have it running right now, it's connected. What I can't do is anything in the client. For any call I get back Error building classpath. Could not find artifact com.datomic:ion:jar:0.9.43 in central () I tried to to set that datomic-cloud repository at least 15 different ways, I have no idea why it tries to look it up in the central : (


There is a datomic channel, maybe you can get help there


If you want to ask in #datomic with a little more detail on your deps.edn and aws cred setup, we can help figure it out


Hey team, noob editor question. I have been a long-time user of cursive, and love it. However, I decided recently to learn one of the l33t editors -- vim or emacs. (coding for ~8 yrs professionally...and never really gave em a true shot) I am leaning towards vim, but I do know that clj is best on emacs. Would love to hear thoughts: • For people who have tried vim and cursive, is the vim support for clj just as good as cursive? (paredit, send form to repl, refactor, search, etc) • For people who have tried vim and emacs, how do they compare for clj?


i use vim with conjure, it works great.


It has mostly everything you want out of the box.

Pavel Klavík11:07:41

if you want to learn vim, there is also plugin for IntelliJ


as in IdeaVim, or a plugin that teaches you vim?

Pavel Klavík11:07:51

IdeaVim, I have it because I was using vim for years

👍 1
Pavel Klavík11:07:17

but most of it I replaced with structural editing anyway

👍 1

Gotcha! Indeed I may end up moving back to cursive, but am thinking I should try pure vim, to get used to the key bindings


Thank you!


And #conjure if you have questions or need help 😄

❤️ 1
Pavel Klavík11:07:32

but vim is mostly made for working with lines not forms, so the vim bindings are not great for editing clojure


this is false, vi is made for text objects, and parenthesized forms are text objects, even without any vim or plugin features


ya( - copies the innermost list, yaF copies the outermost form, d% deletes from the point to the paren match, etc. etc. etc.

Pavel Klavík18:07:04

I know that these exists but overall specialized paredit will be better. At least I never learned vim enough to match the speed I have in cursive. And I suspect most vim users work with lines or words mainly. Similarly as most Unix utilities works with lines a for instance diff (and so git) is terrible for diffing changes in Clojure code.


having used vim text objects and paredit I disagree. I used emacs for over a decade, and for the first five years of my clojure career.


use the editor that works best for you, but please don't make false claims about others

Pavel Klavík20:07:33

I am disagreeing with the sentiment that you need to use vim or emacs to be good programmer or to code Clojure. It doesn't offer as much as people think it does, aside cryptic controls.

Pavel Klavík20:07:34

btw. if you know how to setup git to make good diffing of Clojure code (form-based, not line based), I would be intersted


I don't find that. I find the bindings work really well.


for example, in Conjure, ,ee -> evaluate form under cursor

Pavel Klavík11:07:35

you can get custom bindings for Clojure, but then it is no longer vim in my opinion 🙂 but sure, it might work


It's true lots of vims operations work on lines, but text objects are very well suited for lisp editing.

Phil Hunt11:07:58

Vim (or maybe vi) has some other uses which may be compelling if you do much with remote servers (I use Emacs, but sometimes wish I didn't have to look up vi commands when I do need it)

👍 1

Conjure's form selection is actually written in Lua (Fennel - a lisp - compiled to Lua) which relies on Neovim's findpairpos, it'll eventually be replaced by digging through the AST in Neovim's memory as and when Neovim has pervasive tree-sitter support. So it's not even text objects 😅 it just behaves kind of like it. (sorry, this is pretty off topic for questions about getting into Clojure!)

👍 1

Can someone explain why #(= % sym)` works like expected inside a macro but this (fn pred-const [x] (= x sym))` doesn't? (Call to clojure.core/fn did not conform to spec.)


And is there a resource that can help me to understand the error messages better:innocent:? So that I can better help myself 🙈


(fn pred-const [x] (= x ~sym))` works for me in the repl (after defining sym of course)


fn takes an unqualified symbol as function name


The back tick will cause pred-const to be qualified

👍 1
❤️ 1

So use ~’pred-const


Thanks a lot


This is a classic macro issue


Hey team, what is the clojure view on the idea of "thinking locally" From exp in big cos, one of the big benefits of types (apart from the many drawbacks) was this: • If you have a function in the middle of a callstack, and need to change it, you can be rather certain that all else will be fine, as long as you don't change the input and output type. If you do change it, then the compiler will guide you to all the places that depended on it • In practice, since most code lived somewhere in the middle of the stack, this was very useful How do we deal with this kind of thing in clojure? • For example, we could use spec at the boundaries, but if a new engineer came into a function in the middle of the stack, it would be hard for them to have guarantees or safely change. We could add safety checks everywhere, but that would make most code needlessly complex From Rich Hickey talks, I have a feeling the clojure view of design is quite different -- from how we define functions & contracts. Am not quite sure as I try to make it concrete -- would love a clojurian's take


A reasonable counter argument could be that just because a function still returns the same type that it returned before it was changed doesn't necessarily mean that all else will be fine.

Pavel Klavík12:07:54

It doesn't really matter. Maybe static typing can catch a few of these easy mistakes. But it does not catch everything anyway. And people know that, so they also push unit testing to catch more. If you are doing random changes in your code without fully understanding them and hoping that the machine will do the checking for you, sooner or later you will shoot your own leg. Clojure way is to simplify the code so that you have a better chance to actually understand what is going on. I can easily do refactoring but searching for usages of a function and seeing what precisely is impacted.

👍 1
Binny Zupnick12:07:52

Hey! I was initially trying to figure out how to use the output of str/split as respective params for a function, but then I discovered apply and it worked perfectly. Now where I'm stuck, though, is I need to add another param AFTER the params added from the str/split. So what was (apply my-func (str/split "example for slack" #" ")) I now need something like (apply my-func (str/split "example for slack" #" ") "another param") . I hope that made sense, and thanks!

Pavel Klavík12:07:35

one option is to add the parameter to the front

Pavel Klavík12:07:02

if you need to add it to the end, you have to add it to the end of the sequence produced by str/split, for instance using concat

Pavel Klavík12:07:52

(apply my-func (concat (str/split "example for slack" #" ") "another param"))

Ian Fernandez12:07:02

(let [a [1 1]
      b [0 1]]
  (= [1 2] (fn-add a b))
how I make this fn-dd that is sum of (a0+b0, a1+b1)

Pavel Klavík12:07:13

(mapv + a b)

👍 1
Ian Fernandez12:07:40

thanks I was doing this in my code, maybe it's another error :thinking_face:

Ian Fernandez12:07:48

in other part, hah

Ian Fernandez12:07:24

it's in another part of my code

Binny Zupnick12:07:39

@pavel.klavik thanks! I think I'm going to go with the "changing parameter order" solution. Thanks! I didn't know that apply took params before the vector. Thanks 😃


@d.ian.b Are you doing matrix math?


There are libs specifically for matrix math


That's why I was wondering


Any built-in way to make Clojure throw an exception when a value does not exist in a set instead of returning nil?


Something like this (not sure why you want this):

(defn with-exception-instead-of-nil [val]
  (if (nil? val)
    (throw (Exception. "Returned nil"))       
Use like:
(with-exception-instead-of-nil (your-fn-that-returns-nil args))


But a value not existing in a set isn't usually something that should throw an exception, again, depends on your use case.


Thanks, I figured I could do something like that. Was wondering if there was a built-in way of doing it. It is useful when using enums to query the database for example. It leave no room for typos etc.


I like failjure rather than exceptions.


Will check that out. Thanks.

Sergiusz Bleja15:07:42

I'm trying to retrieve the equal elements in the beginning of the two lists, I've come up with a rather convoluted way. I'm sure there is a better way of doing this? (The result should be (1 2). )

Pavel Klavík15:07:29

The natural way to do this is reduce.

Pavel Klavík15:07:06

(reduce (fn [[x y] same]
           (if (= x y)
             (conj same x)
             (reduced same)))
        [] paired)

Sergiusz Bleja15:07:33

Great, thanks, I learned about reduced ! I didn't actually get the same correct result though -- is paired the same structure as above?

Pavel Klavík15:07:10

ya, it should be, maybe I am doing something wrong, let me test it

Pavel Klavík15:07:01

sorry, I swapped arguments in reduce fn

Pavel Klavík15:07:16

(let [xs [1 2 3 5]
      ys [1 2 4 5]
      paired (partition 2 (interleave xs ys))]
  (reduce (fn [same [x y]]
            (if (= x y)
              (conj same x)
              (reduced same)))
          [] paired))

Sergiusz Bleja15:07:51

Ah, yes, I see it now too, thanks. I'm curious, would there be a way of iterating over the two sequences without creating paired first?

Sergiusz Bleja15:07:32

It 'feels' like it should - but I could not figure it out.

Pavel Klavík16:07:50

I don't think there is something like that, but partition and interleave should both be lazy, so it should work well

Pavel Klavík16:07:32

I would probably just remove paired from let and add it directly into reduced at the end

👍 1
Sergiusz Bleja16:07:03

(I'm using paired in a different expression too on in this case)

Travis Jefferson16:07:15

Here’s my quick take using loop

(defn take-matches [coll1 coll2]
  (loop [[x & xs] coll1
         [y & ys] coll2
         acc []]
  (if (or (nil? x) (nil? y) (not= x y))
    (recur xs ys (conj acc x)))))

👍 1

Probably the wrong way to do it but...

(defn pairs
  ([xs ys] (pairs xs ys []))
  ([xs ys acc]
   (if (or (empty? xs) (empty ys?) (not= (first xs) (first ys)))
     (recur (next xs) (next ys) (conj acc (first xs))))))


Very similar to the loop example lol


(defn pairs
  ([xs ys] (pairs xs ys []))
  ([xs ys acc]
   (if (or (empty? xs)
           (empty? ys)
           (not= (first xs) (first ys)))
     (recur (next xs)
            (next ys)
            (conj acc (first xs))))))


More readable version.


There’s an every? Higher order function as well


(defn pairs
  ([xs ys] (pairs xs ys []))
  ([xs ys acc]
     (= xs ys) xs
     (not= (first xs) (first ys)) acc
     :else (recur (next xs)
            (next ys)
            (conj acc (first xs))))))
Also works and doesn't need empty checks for equal inputs

👍 1

@somedude314 i think you might be well served by spec here


You mean a function that makes use of spec, right?


not sure what distinction you are driving at


but categorically, spec is designed to attack this problem


Yeah, thanks for the pointer. I believe I will use it but it seems I need to wrap it somehow since (spec/conform ::langs :en) for example return the value if available which is what I want but doesn’t throw an exception when the value isn’t available. It returned invalid which make it as good as using string literal if used directly


there's (s/valid? ::spec obj)


you could also spec a function that uses this spec and instrument it at dev time


lots of ways to do this. i don't see the reason to throw exceptions here though


(spec/def ::class-statuses #{:active :archived :deleted})
(defn enum
  [val vals]
  (if (spec/valid? vals val)
    (throw (Exception. (format "%s is not a valid value of %s spec." val vals)))))
(enum :active :myproj.constant/class-statuses)
This is the behaviour I need in some of my Ring handlers. Am I missing something?

Binny Zupnick19:07:13

something odd is happening in that I have this function: (defn prettify-date   `[date]`   `(println date)`   `(println (str date)))` and when I run it from with the param sent from a date queried from postgres (via next.jdbc) the output is: #inst "2020-07-03T21:00:00.000-00:00" 2020-07-04 but when I copy and paste #inst "2020-07-03T21:00:00.000-00:00" and run it in the REPL manually, the function prints: #inst "2020-07-03T21:00:00.000-00:00" Sat Jul 04 00:00:00 IDT 2020 I'm a bit confused why it seems to be getting the same input, but casting it to a string differently. Anyone have any idea what's going on? Thanks!


SQL dates and other dates will pr-str (readable stringify) identically, but have different toString implementations (what str uses)


a simpler example of the same behavior

(ins)user=> (println (map inc [1 2 3]))
(2 3 4)
(ins)user=> (println (str (map inc [1 2 3])))
[email protected]


the pr-str behavior that println used shows the lazy seq as a list, the toString behavior which str introduces gives a dumb object identity reference


maybe the simpler way to say all this is that println uses pr, and pr does conversions, with the general goal that what it writes be readable


by using str you prevent pr from doing its thing, by handing it a string

Binny Zupnick19:07:00

wow okay thanks! It took me awhile to find this bug and once I did, it was quite the head-scratcher. I'll look into pr and use that. Thanks, @noisesmith!


if you want unambiguous logging of values, the function returned by (juxt type pr-str) is helpful



(ins)user=> (def expose (juxt type pr-str))
(ins)user=> (expose (map inc [1 2 3]))
[clojure.lang.LazySeq "(2 3 4)"]
(ins)user=> (expose (java.util.Date.))
[java.util.Date "#inst \"2020-07-04T19:34:38.391-00:00\""]

Binny Zupnick19:07:19

What I'm trying to do is make it human-readable. I assume I'm not the first to want to do that, though, so this is probably a solved problem...


usually the string returned by pr-str is clear, in that case, and that function can be used inside whatever logging function you like


though of course prn will just print with the same rules pr-str uses

Binny Zupnick19:07:57

pr-str simply returned #inst "2020-07-03T21:00:00.000-00:00" for me so it didn't seem to act as wanted


@binny If that was a SQL DATE then it's likely to have .toString() rendering just a date -- but under the hood it's a java.sql.Date, which extends java.util.Date and that's going to be local -- with a timezone -- hence the five hour offset. Dates (and timezones in particular) are hard on the JVM, and SQL types make that even harder (esp. since PostgreSQL has both timezone-aware and timezone-neutral SQL types 😐

Binny Zupnick19:07:20

I'm getting that sense! I'm sure you're well-seasoned, though, with directly printing date types from postgres via next.jdbc. What's the easiest/most sensible way to do that?


I always use an explicit date formatter, as noisesmith suggested.


(but I also generally avoid SQL DATE types unless I have a very specific need for date-only columns)