Fork me on GitHub
#clojure
<
2020-05-18
>
jdkealy00:05:50

^ On that point, why do so many libraries use that syntax of optional key/value arguments in functions and call (apply hash-map args) instead of just having their functions take a map? Is there some benefit to:

(-> (session ring-app) 
    (request "/search" :request-method :post
                       :params {:q "clojure"}))
VS
(-> (session ring-app) 
    (request "/search" {:request-method :post
                        :params {:q "clojure"}}))

andy.fingerhut01:05:00

A few Clojure core functions do it, too, e.g. http://clojure.java.io/reader and http://clojure.java.io/writer, so I suspect part of the answer to "why" is "there were public examples of it in Clojure libraries, so it must not be an absolutely terrible idea".

😂 4
andy.fingerhut01:05:58

I have no measurements to say whether this is happening, or not, but would not be surprised if there were a trend towards passing a map instead.

Chris Lester17:05:37

I know as I was tackling both a logic based state machine, and an unrelated xform rule engine (and definitions for the rulesets) that there are lots of examples in core logic (and other places) that use vectors in a similar way. It felt like Rich was stating (sometimes directly) in places that vectors were idiomatic and you should use them if possible. That might play into it. Vector's can also be "more readable" when defining collections of rules or facts, but that doesn't seem to be the case here.

seancorfield01:05:04

@jdkealy That's a good question. Named arguments are good for users to type in but, as you noticed, bad for programmatic use. Like @andy.fingerhut I suspect that the overall move is to a single options map argument instead -- although a DSL/API designed purely for human use rather than programmatic use might reasonably make use of named arguments (such as Spec).

seancorfield01:05:51

I think the parts of Clojure that use that approach -- named arguments -- are older parts and maybe it's less common in more recent additions?

seancorfield01:05:41

FWIW, when I took over clojure.contrib.sql back in 2011, it was all named arguments (and based around a single dynamic Var for the current DB spec). Over time I moved it to use an options hash map instead, and pass an argument instead of relying on the dynamic Var, both at the suggestion of the Clojure/core team.

👏 16
jdkealy01:05:44

Good to know it’s considered more idiomatic. I much prefer the maps!

seancorfield01:05:23

It's much easier to compose functions that use hash maps and it's only two additional characters for humans to type.

💯 8
potetm01:05:36

one if you have auto-closing parens!

12
EmmanuelOga04:05:16

hi, using genclass: is there a way to get a this reference on a constructor? found an old thread that gave me no answers: https://groups.google.com/forum/#!topic/clojure/0fvbdFC0be4 Imagine the target class something like:

class MyFactory extends SaxonTransformerFactory { public MyFactory () { super(); this.processor.register("My awesome thing"); } }
That is, I need to extend ctor so I can configure a few things on it after it gets created. If you are curious, this is for a java library (JAXP) that will load the class with reflection... I'll do:
System.setProperty("javax.xml.transform.TransformerFactory", "my.package.MyFactory");
.... and then some other library code somwehre else will load MyFactory. This is to explain why I need genclasss, and to augment the constructor, no other way around it 😭

EmmanuelOga04:05:52

I can't get genclass to work I was thinking of implementing it as java code... genclass would be nice because I'm not sure how I'm gonna provide the data for those register calls when implementing it as pure java. Probably will have an static ArrayList of this to register on the ctor... feels a bit hairy 🙂

EmmanuelOga04:05:18

btw, genclass has one of the worst documentation pages of all things... pretty frustrating</RANT>

EmmanuelOga04:05:28

but I think I found what I'm looking for!

EmmanuelOga05:05:38

yaaaay success 🙂

EmmanuelOga05:05:41

(ns rainbowfish.xslt-factory
  (:gen-class
   :name rainbowfish.XsltFactory
   :extends net.sf.saxon.jaxp.SaxonTransformerFactory
   :post-init configure))

(defn -configure 
  [this & args]
  (let [processor (.getProcessor this)]
    (println "JAXP XSLT initialized with processor " processor)))

EmmanuelOga05:05:08

never used gen-class before, seems like I need to make sure (compile rainbowfish.xslt-factory) is always called at least once before grabbing the generated class, rainbowfish.XsltFactory

zzzzpoi07:05:13

Can someone help me. Why multithread sval of lazy-seq will make the cpu to 100% useage?

wombawomba09:05:43

Does anyone know why libraries typically have a foo.core namespace (rather than just foo)? Is this considered a best practice? Or are library authors just taking after clojure.core?

dharrigan09:05:57

What I’ve seen is that most libraries use “core” as the entry, whereas applications use “main” This is not universal, and imho doesn’t really matter so long as there is internal consistency.

dharrigan09:05:07

it’s just a name after all 🙂

wombawomba09:05:42

Okay, makes sense 🙂 I’m working on a single-namespace library and having foo.core feels unnecessary to me, whereas going with just foo feels a bit.. unconventional.

p-himik09:05:52

I don't know about Clojure, but in ClojureScript single-segment namespaces can result in errors: https://clojure.atlassian.net/browse/CLJS-1004

p-himik09:05:56

Oh, it's also relevant for Clojure, although the reason is a bit different: https://groups.google.com/forum/#!topic/clojure/gOffhotk25Y

dharrigan09:05:57

@U15RYEQPJ it’s still good practice to have at least two namespaces

dharrigan09:05:02

i.e., foo.bar

dharrigan09:05:23

it helps to avoid clashes of namespaces

wombawomba09:05:05

okay, thanks!

dharrigan09:05:13

Here’s a style guide - although not universal, it has some good things in it.

dharrigan09:05:26

take from it what you will, reject what you don’t agree with 🙂

👍 4
wombawomba10:05:44

So considering how there seem to be multiple schema libraries (`schema`, spec.alpha, malli, …) in use — is it possible to design a library to support these libraries if/when they exist? Ideally I’d like to support more than one schema library, but without forcing projects that use my library to use or explicitly exclude them.

ikitommi11:05:56

libs like reitit abtract the routing schemas using a Coercion protocol making the schema impl swappable (parameter & response definitions, coercion and api-docs). Mixing the three in a same routing app would be asking for trouble, but still possible. I think same applies for all apps. Problem is of course, none of the three is complete and could be used for all things.

restenb15:05:59

any nice way to join vectors like this (every other element)? [1 2 3] + [4 5 6] => [1 4 2 5 3 6]

dpsutton15:05:17

check out interleave

restenb15:05:53

this came up in an effort to calculate moving averages, which can almost be done with (interleave coll (map average (partition period 1 coll))), but only almost

restenb15:05:51

doing it that way will always leave out the last element of coll

restenb15:05:49

trivial to re-add it at the end, but still

noisesmith15:05:51

@restenb have you tried partition-all - then you have to deal with a "dangling" coll

noisesmith15:05:25

(ie. a coll that isn't a full period elements)

restenb15:05:43

that'll have the opposite effect of adding the last element twice it seems (at least on my test data)

restenb15:05:28

so in the end no better than having to (conj moving-averages-of-coll (last coll))at the end

noisesmith15:05:36

btw relying on conj for ordering is a bad plan (that conj will insert at the beginning if you use interleave)

restenb15:05:40

not if I stuff it in a vector first 😉

restenb15:05:09

but yeah so far this is one of those "works, but smells kinda funny" situations

noisesmith15:05:51

you might get a more direct result with some sort of (reduce f {:state {} :result []} (map vector coll (partition period 1 coll)))

noisesmith15:05:03

calling conj in f to update the state and result

noisesmith15:05:42

actually given the way you are doing the moving average, you can just use [] result directly and need not carry state

noisesmith15:05:27

which means you could use (mapcat (fn [[x :as sample]] [x (average sample)]) (partition period 1 coll))

restenb15:05:50

that'll still leave out the last element though?

noisesmith16:05:58

(partition n 1 coll) and coll have a difference of n elements between the two

noisesmith16:05:14

maybe you don't even want partition

Ben Sless15:05:08

You don't need to calculate the entire average every time, you can use a queue as a window then just calculate the diff. It covers the tails as well

worlds-endless16:05:31

Are Pedestal and Reitit alternatives or are they in different service spaces?

Aleed16:05:01

alternatives, Reitit has a comparison in their FAQ: https://metosin.github.io/reitit/faq.html

isak16:05:32

It would only be an alternative to pedestal routing, not all of pedestal

isak16:05:43

Pedestal does a lot more than just routing

worlds-endless16:05:08

What else do you use Pedestal for? I haven't looked into it besides just seeing it on "inspired by..." lists

lilactown17:05:17

reitit only does routing, it doesn’t handle any http stuff

lilactown17:05:26

e.g. you would use reitit + ring

lilactown17:05:02

the bulk of pedestal covers what ring does and more

ikitommi18:05:45

reitit author here. Pedestal definitely has more batteries out-of-the-box. There are reitit-ring and reitit-http modules with a lot of helpers for building web apps too. And, there is reitit-pedestal , bridging the two. Have fun, with either one :)

4
dpsutton18:05:24

good question from a coworker. obviously understand that we can use private vars with (#'ns/private-var ...) but don't actually understand why. The reader ensures that we respect privacy?

noisesmith18:05:06

when compiling, private vars are not available for implicit lookup

noisesmith18:05:21

using var-quote sidesteps this mechanism

dpsutton18:05:30

implicit lookup is in the compiler or the reader?

noisesmith18:05:41

compiler, the reader just reads symbols

dpsutton18:05:48

er, obviously implicit lookup is in the compiler. just wondering which part throws i guess

noisesmith18:05:50

the error is a compiler error - it doesn't find anything for that symbol

noisesmith18:05:57

foo=> (defn- foo-private [] 42)
#'foo/foo-private
foo=> (ns user)
nil
user=> (foo/foo-private)
Syntax error (IllegalStateException) compiling foo/foo-private at (REPL:1:1).
var: #'foo/foo-private is not public
user=>(#'foo/foo-private)
42

noisesmith18:05:17

"syntax error" translates loosely to "error while compiling"

dpsutton18:05:43

ok. i see resolve and resolveIn in the compiler. just trying to trace how var-quote ends up with the allowPrivate = true

dpsutton18:05:50

(and thanks for your help @noisesmith)

noisesmith18:05:51

@dpsutton I should correct what I said above - clearly based on the message here it does look at private vars, it just throws if you use them "implicitly" (without a var dereference)

noisesmith18:05:55

it might help to look at the stack trace you get when you use a non-existant var inside defn

dpsutton18:05:19

now that is some smart thinking. thanks! so obvious in retrospect but hadn't thought that lol

noisesmith18:05:22

as compared to the stack trace for using a private var without explicit var quote

dpsutton18:05:47

yeah perfect. I think it goe through TheVarExpr which allows lookingup private. thanks

thelittlesipper22:05:55

(read-string "'some-symbol") returns (quote some-symbol) is there anyway to have it return the original 'some-symbol (or I guess just some-symbol) back instead?

noisesmith22:05:45

there's a library that makes that happen - what's the use case?

noisesmith22:05:26

oh, and clojure.pprint/pprint does this already

noisesmith22:05:07

@sansaripour btw (quote foo) is literally 'foo, it's the correct reader expansion of the shorthand

noisesmith22:05:19

just like @foo is the same as (deref foo) etc.

noisesmith22:05:50

the three of these are all equal:

user=> (= 'foo (quote foo) (symbol "foo"))
true

phronmophobic22:05:01

there are multiple ways to get some-symbol from (read-string "'some-symbol"). depending the use case, either of these might work: • if you know it’s going to be a quoted symbol: (second (read-string "'some-symbol")) • if you’re actually hoping to evaluate a string: (eval (read-string "'some-symbol"))

👍 4
noisesmith22:05:38

or, if you know you never want quoted forms anywhere in the output, and just want the unevaluated symbol instead, you can tree walk and replace all lists that start with quote with the second item in the list

4
noisesmith22:05:56

but it seems like a strange problem where usually the fix would be just not using ' in your edn files, which is why I ask what is actually being attempted

💯 4
thelittlesipper23:05:57

Thanks all for the help. I think I have a path forward now 👍 And, the use case is that I'm reading in the contents of a clojure (clj) file with read-string in search of a particular form that contains a particular map. I then return this map as a JSON string. The map may or may not contain keys formatted like 'some-symbol. Right now, the output JSON gets an un-welcomed heaping of (quote some-symbol) instead of the original 'some-symbol as a result of the read-string

thelittlesipper23:05:40

oh and Im using babashka to execute the above operation^

noisesmith23:05:35

OK - so to be clear 'foo is just a shorthand for (quote foo) and has the same meaning, if you need to print more recognizably you can use pprint (or copy what pprint does), but as a last resort you can absolutely do a tree-replace of (quote x) with x - it's a pretty unambiguous thing to test for an replace

thelittlesipper01:05:56

Yep, I understand this! Thanks! I was merely concerned with the textual representation and I should've clarified that

phronmophobic23:05:17

something like:

(clojure.walk/postwalk (fn [form]
                         (if (and (seq? form)
                                  (= 2 (count form))
                                  (= 'quote (first form)))
                           (second form)
                           form))
                       (read-string "{:a {:bar 'foo}}"))

💯 4
phronmophobic23:05:01

although if it’s actually a clj file, you might want to just require it and call the function that returns the data. if you need access to it from cljs, you may consider using cljc