clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
nikolavojicic 2020-12-10T01:37:04.284Z

Is there a way to transform:

(with-open [in (io/reader f)]
  (vec (csv/read-csv in)))
Into something reducible, like:
(into []
      (map first)
      (reducible-csv-rows f))
Library: [org.clojure/data.csv "1.0.0"]

alexmiller 2020-12-10T01:46:03.285300Z

The result of read-csv is a lazy seq iirc, which is reducible

alexmiller 2020-12-10T01:46:39.286Z

The vector is also reducible of course, just not necessary

2020-12-10T01:47:10.286500Z

is "reducible" a reference to the reducers library, or a shorthand for something I've forgotten?

2020-12-10T01:48:31.287800Z

(oh I see it mentioned but not actually defined in the page about reducers https://clojure.org/reference/reducers)

nikolavojicic 2020-12-10T01:57:53.292900Z

I took reducible naming from https://www.grammarly.com/blog/engineering/building-etl-pipelines-with-clojure-and-transducers/ See lines-reducible Reducible here means it reifies IReduceInit. reducible-csv-rows should, when reduced, open a file, yield parsed rows on demand and close it. Something similar to next.jdbc/plan

2020-12-10T01:58:44.293300Z

OK, that definitely does not exist

2020-12-10T02:01:31.295100Z

you can reduce / transduce on a lazy-seq, but there is a small overhead that IReduceInit could hypothetically eliminate

2020-12-10T02:02:48.296Z

you would end up using some internal functions from clojure.data.csv I guess

2020-12-10T02:03:19.296800Z

perhaps wrapping the row parsing into a transducer?

2020-12-10T02:03:39.297300Z

as it's currently designed, that's not abstracted and you always get a lazy-seq

2020-12-10T02:04:28.298500Z

but the amount of work do the refactor / performance benefit might not be worth it(?)

nikolavojicic 2020-12-10T02:09:08.300800Z

It's not about performance, I have to wrap everything with with-open. It's ok... but I prefer api like lines-reducible from the blog, instead of with-open + line-seq.

2020-12-10T02:11:18.303200Z

right, when the data is presented as a lazy-seq, you can't make it self-cleaning like that

2020-12-10T02:16:00.305100Z

I had a similar struggle with a tag-soup xml parser, where I just wanted to read until I found some payload and then move on, but the underlying implementation, because it used a lazy-seq abstraction plus being self-cleaning of resources, ended up being a huge memory leak. It only discarded inputs and streams when you read them to the end, and all partially read streams stayed alive in a gc root owned by a cleanup thread

2020-12-10T02:16:37.305700Z

either with-open or non-lazy IReduceInit would have saved me a lot of headache

2020-12-10T03:29:42.308900Z

Using fdef is a bit inconvenient with named arguments, because there's no easy spec for declaring a map of some keys, s/keys forces me to s/def evey value again, which is pretty annoying for fdef with named argument. Is there a way around that?

2020-12-10T03:31:14.309600Z

Its like I'd need a s/keys that lets me do: (s/keys :req-un [:arg1 string? :arg2 ::some/thing])

2020-12-10T03:31:24.309800Z

So I can do:

(s/fdef fn
  :args (s/cat :args (s/keys :req-un [:arg1 string? :arg2 ::some/thing])))

alexmiller 2020-12-10T04:05:01.310900Z

Do you mean like kwargs? Use s/keys* as a regex op for that

2020-12-10T04:09:05.311100Z

Hum, maybe, I'll look at it

2020-12-10T04:10:59.312600Z

Ok, no I don't think so, that still requires me to define spec for the keys

seancorfield 2020-12-10T04:11:00.312700Z

@didibus keys* is what you need for named arguments but you'll still need to write a spec for each of them. You want something like what's in Spec 2 I think...

seancorfield 2020-12-10T04:11:26.313200Z

...Spec 2 allows for unqualified keys with inline predicates.

seancorfield 2020-12-10T04:11:38.313600Z

(although of course Spec 2 is not ready for use yet 🙂 )

seancorfield 2020-12-10T04:12:15.314600Z

And I gather the fdef part of Spec 2 is going to get a rewrite once Rich figures out what it should look like...

2020-12-10T04:12:35.315100Z

Ya, inline predicate is what I need 😄, defining a whole spec just for some small arg from a function is too much

2020-12-10T04:13:43.315700Z

I guess I'll wait for Spec 2 😖

Ben Sless 2020-12-10T07:47:22.318200Z

Hey all, I'm getting some weird behavior with core.match and debugging - I was toying around with a port of https://www.reddit.com/r/Clojure/comments/6uuk5r/the_most_beautiful_program_ever_written/ and tried adding let bindings to the interpreter It should work, but when I try to evaluate even a basic expression I get "no matching clause for ,,," error Even weirder, when I instrument the definition (with CIDER's debugger) it does work What's going on? Code for reference:

(defn eval-expr
  [expr env]
  (match
   expr
   (x :guard number?) x
   (x :guard symbol?) (env x)
   (['zero? x] :seq) (zero? (eval-expr x env))
   (['inc x] :seq) (inc (eval-expr x env))
   (['dec x] :seq) (dec (eval-expr x env))
   (['* x y] :seq) (* (eval-expr x env) (eval-expr y env))
   (['if pred then else] :seq) (if (eval-expr pred env)
                                 (eval-expr then env)
                                 (eval-expr else env))
   (['fn [x] body] :seq) (fn [arg] (eval-expr body (fn [y] (if (= x y) arg (env y)))))
   (['let [sym expr] body] :seq) (eval-expr `((~'fn [~sym] ~body) ~expr) env)
   ([rator rand] :seq) ((eval-expr rator env) (eval-expr rand env))))

(def environment (fn [y] (throw (ex-info "oops"))))

(eval-expr
 '(let [x 1]
    x)
 environment)

mozinator2 2020-12-10T08:50:35.319200Z

anyone else has been having this problem ?

Error building classpath. Could not transfer artifact org.clojure:spec.alpha:jar:0.2.176 from/to central (<https://repo1.maven.org/maven2/>): status code: 416,
reason phrase: Range Not Satisfiable (416)
with default latest clojure on archlinux installed using the package manager

mozinator2 2020-12-10T08:55:16.320600Z

with this in ~/.clojure/deps.edn I got clojure working again

:deps {
      org.clojure/clojure {:mvn/version "1.10.1"}
      org.clojure/core.async {:mvn/version "1.3.610"}
      org.clojure/spec.alpha {:mvn/version "0.2.187"}
      org.clojure/core.specs.alpha {:mvn/version "0.2.44"}
    }

alexmiller 2020-12-10T09:24:40.321200Z

what version is that? clj -Sdescribe

alexmiller 2020-12-10T09:26:10.321600Z

that error sounds like a partial/broken download of something

alexmiller 2020-12-10T09:26:22.321900Z

or the maven repo being cranky

alexmiller 2020-12-10T09:27:22.322700Z

probably worth doing rm -rf ~/.m2/repository/org/clojure/spec.alpha and then clj -Sforce with your original deps.edn

2020-12-10T16:42:45.324300Z

is it possible to make the rlwrapped clj to support auto completion?

2020-12-10T16:42:48.324500Z

rebel starts too slow

2020-12-10T16:45:00.325100Z

basically i want a clj repl with a better tab completion support but without dragging down the start up time too much.

2020-12-10T17:30:10.330500Z

Another qualified keyword question 🙂 Let's say you're wrapping an api service called "foobar" that streams event messages as maps to you with keys like :time and :amount. You have a client function that creates a client for this service that takes some settings that you expose which aren't' necessarily terms service itself uses, such as :sandbox? and :creds. I'm thinking I should qualify both these setting keys as well as the keys in the event maps themselves. Do you you use the same qualifier for both, ie :foobar/sandbox? as a setting as well as :foobar/time in the event message? Or do you somehow differentiate between terms I make up and terms that are just exactly taken from their own names?

seancorfield 2020-12-10T17:59:06.331300Z

@i Develop a workflow where you only (re)start a REPL very rarely? 🙂 I have my REPLs running for days, sometimes weeks...

2020-12-10T18:59:53.331400Z

the rlwrap config will add things to the completion dictionary if the repl prints them or you type them in, so things like clojure.repl/dir can help

2020-12-10T19:00:23.331600Z

there's probably some clever way to get the repl to print a bunch of things on startup just to put them in the completion dictionary for the current repl...

2020-12-10T19:03:23.331800Z

(ins)user=&gt; ;(s/uni&lt;tab&gt; ; no completion available
(ins)user=&gt; (doseq [n (ns-publics 'clojure.set)] (println (str "s/" (key n))))
s/union
s/map-invert
s/join
s/select
s/intersection
s/superset?
s/index
s/subset?
s/rename
s/rename-keys
s/project
s/difference
nil
(ins)user=&gt; (s/uni
s/uni&lt;tab&gt;  s/union     
(ins)user=&gt; ; (s/union ; now tab completes

rdgd 2020-12-10T22:48:52.335200Z

Ran into an interesting corner today while writing a custom spec conformer, I learned that when using certain macros like if-let or when-let, one of the branches cannot be the keyword literal :clojure.spec.alpha/invalid

(when-let [foo true] :clojure.spec.alpha/invalid)

Syntax error macroexpanding clojure.core/when-let at (...).
:clojure.spec.alpha/invalid - failed: any? at: [:body]

alexmiller 2020-12-10T22:49:17.335600Z

yeah, that's a known issue and probably not something we're going to fix

rdgd 2020-12-10T22:49:27.336Z

yeah, I figured as much

alexmiller 2020-12-10T22:49:35.336200Z

easy work around is to (def invalid ::s/invalid) then use that

rdgd 2020-12-10T22:49:51.336500Z

for sure

rdgd 2020-12-10T22:50:16.337Z

not an issue that's gonna stop me from using spec! that's for sure :-)

alexmiller 2020-12-10T22:50:26.337200Z

also, you probably shouldn't use custom spec conformers :)

rdgd 2020-12-10T22:50:44.337500Z

oh? I would be interested to hear more about why not

alexmiller 2020-12-10T22:51:44.338400Z

conformers are intended to be used primarily for making new spec types, not for serving as a data meat grinder to conform/coerce your way to a particular value

rdgd 2020-12-10T22:52:46.339500Z

when you say new spec types, do you mean the act of def'ing a new spec? like so (s/def ::foo (s/conform #(...))?

alexmiller 2020-12-10T22:52:53.339700Z

no, I mean making new kinds of specs

alexmiller 2020-12-10T22:53:06.340100Z

for example, conformers were used to make s/keys*

alexmiller 2020-12-10T22:54:04.341400Z

(I'm assuming you're talking about s/conformer but maybe I misunderstood)

rdgd 2020-12-10T22:54:13.341600Z

I'm following, a bit. Granted I've only delved so deep into spec, surely not at the level you have, so I guess what I would like to understand is in what way is that not just a layer of indirection/abstraction?

borkdude 2020-12-10T22:54:36.342200Z

it's very rare that a user would need to use the :clojure.spec.alpha/invalid keyword, I think.

rdgd 2020-12-10T22:55:17.343500Z

ah yes, indeed, clojure.spec.alpha/conformer not conform

alexmiller 2020-12-10T22:55:28.343900Z

"conforming" is about parsing data and explaining why choices were made in the parsing

alexmiller 2020-12-10T22:55:37.344300Z

not about coercing data into a particular shape

alexmiller 2020-12-10T22:56:36.345300Z

you can to some degree use it for the latter, but eventually you will start to lose s/unform reversibility, automatic generation, etc

alexmiller 2020-12-10T22:56:56.345700Z

so we recommend you not do that. if you want to transform data, use clojure functions to transform it

alexmiller 2020-12-10T22:57:16.346200Z

there's a reasonable chance spec 2 will not include s/conformer

rdgd 2020-12-10T22:58:42.348Z

gotcha, gotcha... the way I'm finding it useful, however unidiomatic it may be, is to create specs for REST API params, and conforming them in the handler, checking to see that they are valid or not, and then passing the data (now type coerced) into the core functions of the application

rdgd 2020-12-10T22:59:38.349500Z

spec has seemed to me like a tool which fits that job nicely, particularly considering in non-production environments, returning explain strings along w/ a 400 can be useful when working on distributed systems w/ distributed ownership (and ownership of documentation :-P)

alexmiller 2020-12-10T23:00:16.350400Z

if you transform during conforming, you have thrown away information about the original value

borkdude 2020-12-10T23:01:21.351800Z

@ryan.is.gray I've heard good things about #malli for this kind of purpose (validating and coercing in one go)

rdgd 2020-12-10T23:01:23.352Z

true, but in the context of validating inputs of http requests, the type is always a string and will have to change at some point for some applications

borkdude 2020-12-10T23:01:57.352800Z

it's not a replacement for spec, they serve a different kind of niche I believe

rdgd 2020-12-10T23:02:26.353300Z

I read that first article on malli when it dropped a couple months back, am interested, and am planning on digging in when I have to time to

borkdude 2020-12-10T23:02:52.353700Z

I'm curious to see where each of the approaches go in the long term

borkdude 2020-12-10T23:03:51.354300Z

btw, I have found a nice use for the conformed output and then processing it with another pattern matching library (plucking values out of the conformed data structure) here: https://github.com/borkdude/grasp#pattern-matching

borkdude 2020-12-10T23:06:15.355Z

@ryan.is.gray This may also be interesting to look at: https://github.com/exoscale/coax

rdgd 2020-12-10T23:09:19.355400Z

likewise

rdgd 2020-12-10T23:09:51.355900Z

thanks for the interesting discussion guys

rdgd 2020-12-10T23:10:02.356200Z

and the links