Fork me on GitHub
#beginners
<
2020-07-04
>
zach03:07:45

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?

phronmophobic03:07:33

how are you running the js? node?

zach03:07:52

yah, node!

phronmophobic03:07:55

and what made you switch from clojure to clojurescript?

phronmophobic03:07:48

if the startup time is an issue, you can try babaska, https://github.com/borkdude/babashka

zach03:07:03

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.

zach03:07:21

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.

zach03:07:44

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.

phronmophobic03:07:45

I would definitely check out https://github.com/borkdude/babashka in that case

☝️ 3
zach03:07:10

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?

phronmophobic03:07:55

I'm actually not sure

phronmophobic03:07:10

clojurescript is also pretty reasonable

zach03:07:18

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

zach03:07:49

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

zach03:07:43

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.

zach03:07:39

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

zach03:07:22

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

metal 3
phronmophobic03:07:26

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

zach03:07:37

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

zach03:07:17

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.

phronmophobic03:07:49

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

zach03:07:43

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.

👍 3
zach03:07:09

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

zach03:07:08

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.

zach03:07:50

Thank you for the help!

phronmophobic03:07:05

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

phronmophobic03:07:40

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

solf04:07:30

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.

zach05:07:28

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

Lukas10:07:49

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 https://clojuredocs.org/clojure.core/constantly 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

Lukas10:07:30

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

👍 3
noisesmith16:07:03

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

noisesmith16:07:15

this could be a badly constructed NaN check

Aron10:07:01

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 : (

Lukas11:07:34

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

Alex Miller (Clojure team)11:07:12

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

stopa10:07:14

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?

dharrigan11:07:00

i use vim with conjure, it works great.

dharrigan11:07:28

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

stopa11:07:21

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

👍 3
Pavel Klavík11:07:17

but most of it I replaced with structural editing anyway

👍 3
stopa11:07:12

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

stopa11:07:42

Thank you!

Olical11:07:48

And #conjure if you have questions or need help 😄

❤️ 3
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

noisesmith16:07:21

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

noisesmith16:07:52

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.

noisesmith19:07:37

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.

noisesmith19:07:02

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

dharrigan11:07:54

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

dharrigan11:07:04

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

hugo11:07:39

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)

👍 3
Olical11:07:26

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!)

👍 3
Lukas11:07:16

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.)

Lukas11:07:16

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

hugo11:07:15

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

Alex Miller (Clojure team)11:07:26

fn takes an unqualified symbol as function name

Alex Miller (Clojure team)11:07:53

The back tick will cause pred-const to be qualified

👍 3
❤️ 3
Lukas11:07:09

Thanks a lot

Alex Miller (Clojure team)11:07:04

This is a classic macro issue

Alex Miller (Clojure team)11:07:10

What is the text of the error message you were getting?

Lukas11:07:03

Unhandled clojure.lang.Compiler$CompilerException
   Error compiling src/polymatheia/query.clj at (23:4)
   #:clojure.error{:phase :macro-syntax-check,
                   :line 23,
                   :column 4,
                   :source
                   "/home/exa/workspace/polymatheia/src/polymatheia/query.clj",
                   :symbol clojure.core/fn}
             Compiler.java: 6972  clojure.lang.Compiler/checkSpecs
             Compiler.java: 6988  clojure.lang.Compiler/macroexpand1
             Compiler.java: 7093  clojure.lang.Compiler/analyzeSeq
             Compiler.java: 6789  clojure.lang.Compiler/analyze
             Compiler.java: 7095  clojure.lang.Compiler/analyzeSeq
             Compiler.java: 6789  clojure.lang.Compiler/analyze
             Compiler.java: 6745  clojure.lang.Compiler/analyze
             Compiler.java: 3820  clojure.lang.Compiler$InvokeExpr/parse
             Compiler.java: 7109  clojure.lang.Compiler/analyzeSeq
             Compiler.java: 6789  clojure.lang.Compiler/analyze
             Compiler.java: 6745  clojure.lang.Compiler/analyze
             Compiler.java: 6120  clojure.lang.Compiler$BodyExpr$Parser/parse
             Compiler.java: 5467  clojure.lang.Compiler$FnMethod/parse
             Compiler.java: 4029  clojure.lang.Compiler$FnExpr/parse
             Compiler.java: 7105  clojure.lang.Compiler/analyzeSeq
             Compiler.java: 6789  clojure.lang.Compiler/analyze
             Compiler.java: 7174  clojure.lang.Compiler/eval
             Compiler.java: 7132  clojure.lang.Compiler/eval
                  core.clj: 3214  clojure.core/eval
                  core.clj: 3210  clojure.core/eval
    interruptible_eval.clj:   91  nrepl.middleware.interruptible-eval/evaluate/fn
                  main.clj:  437  clojure.main/repl/read-eval-print/fn
                  main.clj:  437  clojure.main/repl/read-eval-print
                  main.clj:  458  clojure.main/repl/fn
                  main.clj:  458  clojure.main/repl
                  main.clj:  368  clojure.main/repl
               RestFn.java:  137  clojure.lang.RestFn/applyTo
                  core.clj:  665  clojure.core/apply
                  core.clj:  660  clojure.core/apply
                regrow.clj:   20  refactor-nrepl.ns.slam.hound.regrow/wrap-clojure-repl/fn
               RestFn.java: 1523  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   84  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:   56  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:  155  nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
                  AFn.java:   22  clojure.lang.AFn/run
               session.clj:  190  nrepl.middleware.session/session-exec/main-loop/fn
               session.clj:  189  nrepl.middleware.session/session-exec/main-loop
                  AFn.java:   22  clojure.lang.AFn/run
               Thread.java:  832  java.lang.Thread/run

1. Caused by clojure.lang.ExceptionInfo
   Call to clojure.core/fn did not conform to spec.
   #:clojure.spec.alpha{:problems
                        ({:path [:fn-tail :arity-1 :params],
                          :pred clojure.core/vector?,
                          :val polymatheia.query/pred-const,
                          :via
                          [:clojure.core.specs.alpha/params+body
                           :clojure.core.specs.alpha/param-list
                           :clojure.core.specs.alpha/param-list],
                          :in [0]}
                         {:path [:fn-tail :arity-n],
                          :pred
                          (clojure.core/fn
                           [%]
                           (clojure.core/or
                            (clojure.core/nil? %)
                            (clojure.core/sequential? %))),
                          :val polymatheia.query/pred-const,
                          :via
                          [:clojure.core.specs.alpha/params+body
                           :clojure.core.specs.alpha/params+body],
                          :in [0]}),
                        :spec
                        #object[clojure.spec.alpha$regex_spec_impl$reify__2509 0x3bc0f844 "clojure.spec.alpha$regex_spec_impl$reify__2509@3bc0f844"],
                        :value
                        (polymatheia.query/pred-const
                         [polymatheia.query/x]
                         (clojure.core/= polymatheia.query/x :key)),
                        :args
                        (polymatheia.query/pred-const
                         [polymatheia.query/x]
                         (clojure.core/= polymatheia.query/x :key))}
                 alpha.clj:  705  clojure.spec.alpha/macroexpand-check
                 alpha.clj:  697  clojure.spec.alpha/macroexpand-check
                  AFn.java:  156  clojure.lang.AFn/applyToHelper
                  AFn.java:  144  clojure.lang.AFn/applyTo
                  Var.java:  705  clojure.lang.Var/applyTo
             Compiler.java: 6970  clojure.lang.Compiler/checkSpecs
             Compiler.java: 6988  clojure.lang.Compiler/macroexpand1
             Compiler.java: 7093  clojure.lang.Compiler/analyzeSeq
             Compiler.java: 6789  clojure.lang.Compiler/analyze
             Compiler.java: 7095  clojure.lang.Compiler/analyzeSeq
             Compiler.java: 6789  clojure.lang.Compiler/analyze
             Compiler.java: 6745  clojure.lang.Compiler/analyze
             Compiler.java: 3820  clojure.lang.Compiler$InvokeExpr/parse
             Compiler.java: 7109  clojure.lang.Compiler/analyzeSeq
             Compiler.java: 6789  clojure.lang.Compiler/analyze
             Compiler.java: 6745  clojure.lang.Compiler/analyze
             Compiler.java: 6120  clojure.lang.Compiler$BodyExpr$Parser/parse
             Compiler.java: 5467  clojure.lang.Compiler$FnMethod/parse
             Compiler.java: 4029  clojure.lang.Compiler$FnExpr/parse
             Compiler.java: 7105  clojure.lang.Compiler/analyzeSeq
             Compiler.java: 6789  clojure.lang.Compiler/analyze
             Compiler.java: 7174  clojure.lang.Compiler/eval
             Compiler.java: 7132  clojure.lang.Compiler/eval
                  core.clj: 3214  clojure.core/eval
                  core.clj: 3210  clojure.core/eval
    interruptible_eval.clj:   91  nrepl.middleware.interruptible-eval/evaluate/fn
                  main.clj:  437  clojure.main/repl/read-eval-print/fn
                  main.clj:  437  clojure.main/repl/read-eval-print
                  main.clj:  458  clojure.main/repl/fn
                  main.clj:  458  clojure.main/repl
                  main.clj:  368  clojure.main/repl
               RestFn.java:  137  clojure.lang.RestFn/applyTo
                  core.clj:  665  clojure.core/apply
                  core.clj:  660  clojure.core/apply
                regrow.clj:   20  refactor-nrepl.ns.slam.hound.regrow/wrap-clojure-repl/fn
               RestFn.java: 1523  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   84  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:   56  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:  155  nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
                  AFn.java:   22  clojure.lang.AFn/run
               session.clj:  190  nrepl.middleware.session/session-exec/main-loop/fn
               session.clj:  189  nrepl.middleware.session/session-exec/main-loop
                  AFn.java:   22  clojure.lang.AFn/run
               Thread.java:  832  java.lang.Thread/run

Lukas11:07:19

this was the original error msg

Alex Miller (Clojure team)13:07:05

What editor or tool were you using? And what Clojure version? I spent a lot of time in Clojure 1.10 and 1.10.1 making this better but it’s somewhat tool dependent so I’m just curious about your experience. I would expect way less (and better) output from a base repl in 1.10.x.

Lukas20:07:53

Hey thanks for all the effort you putting in. I can check when I'm at home but I guess I'm not on the newest version yet. Anyways I really appreciate your work :hugging_face:

Lukas09:07:59

To answer your questions: I use GNU Emacs 26.3 (doom-emacs with cider). My Clojure version is clojure 1.10.1-7. My overall experience with the error messages is actually quite satisfying, the error message tells me the location of the error and I usually use the repl from there to track the error down. In the particular case I posted was, that I already knew the line of the error, but the error message doesn't lead me anywhere to close my lack of knowledge (at least it doesn't do it in a prominent way, that a newbie like me can figure it out 😇). For example a useful keyword to search for or something similar. I hope this helps and thanks again.

Alex Miller (Clojure team)12:07:44

So I’m using you’re using Cider? The Clojure error triager can turn the stack you saw into something a little less daunting and that’s what you should see on other repls.

Lukas15:07:22

Hey you are right. The error message I've got was inside the cider error-buffer. That's error msg inside the repl:

Syntax error macroexpanding clojure.core/fn at (src/polymatheia/query.clj:22:4).
polymatheia.query/pred-const - failed: vector? at: [:fn-tail :arity-1 :params] spec: :clojure.core.specs.alpha/param-list
polymatheia.query/pred-const - failed: (or (nil? %) (sequential? %)) at: [:fn-tail :arity-n] spec: :clojure.core.specs.alpha/params+body
I've just picked up emacs for clojure. There so much to learn and I haven't connected all the information yet 🙈😇

Alex Miller (Clojure team)15:07:44

Yeah, that’s way more focused but still a pretty confusing spec error. We might be able to tweak that spec to make it better. Thanks

Alex Miller (Clojure team)15:07:39

The spec here is basically expecting an optional fn name then the params in an arity-1 function. In this case the qualified symbol didn’t match the option so it’s trying to match the qualified symbol it has with the next thing - the param list. So it’s actually missing the potential match that is the intended one entirely. It’s actually a pretty subtle but common case to look at.

Alex Miller (Clojure team)15:07:25

But the user clue here should really be the wrong value - polymathiea.query/pred-const

Lukas18:07:44

Thanks again. Really appreciate it.

stopa11:07:39

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

hugo12:07:00

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.

👍 3
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)

👍 3
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 😃

Frosku14:07:07

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

Frosku14:07:10

There are libs specifically for matrix math

Frosku14:07:15

That's why I was wondering

adam15:07:00

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

Frosku15:07:58

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

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

Frosku15:07:04

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

adam15:07:05

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.

Frosku15:07:04

I like failjure rather than exceptions.

adam15:07:21

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

👍 3
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))
    acc
    (recur xs ys (conj acc x)))))

👍 3
Frosku16:07:04

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)))
     acc
     (recur (next xs) (next ys) (conj acc (first xs))))))

Frosku16:07:23

Very similar to the loop example lol

Frosku16:07:24

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

Frosku16:07:36

More readable version.

dpsutton16:07:36

There’s an every? Higher order function as well

Frosku16:07:33

(defn pairs
  ([xs ys] (pairs xs ys []))
  ([xs ys acc]
   (cond
     (= 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

👍 3
dpsutton15:07:34

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

adam15:07:05

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

dpsutton15:07:20

not sure what distinction you are driving at

dpsutton15:07:31

but categorically, spec is designed to attack this problem

adam16:07:03

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

dpsutton16:07:20

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

dpsutton16:07:42

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

dpsutton16:07:05

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

adam16:07:06

(spec/def ::class-statuses #{:active :archived :deleted})
(defn enum
  [val vals]
  (if (spec/valid? vals val)
    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!

noisesmith19:07:05

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

noisesmith19:07:18

a simpler example of the same behavior

(ins)user=> (println (map inc [1 2 3]))
(2 3 4)
nil
(ins)user=> (println (str (map inc [1 2 3])))
clojure.lang.LazySeq@7c42
nil

noisesmith19:07:01

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

noisesmith19:07:43

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

noisesmith19:07:59

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!

noisesmith19:07:09

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

noisesmith19:07:56

@binny

(ins)user=> (def expose (juxt type pr-str))
#'user/expose
(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...

noisesmith19:07:17

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

noisesmith19:07:34

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

seancorfield19:07:21

@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?

seancorfield19:07:29

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

seancorfield19:07:48

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