Fork me on GitHub
#clojure
<
2019-06-07
>
seancorfield00:06:27

Agreed re: Midje. Definitely not recommended by quite a few folks @hi135

bbrinck00:06:09

@hi135 FWIW, if you end up using spec for generative testing, expound can help format the results: https://github.com/bhb/expound#printing-results-for-check

bbrinck00:06:01

Orchestra is also useful if you plan to instrument functions during tests

souenzzo01:06:26

I use midje like this (deftest foo (fact (+ 1 2) => 3)) and it's fine. #cursive interation and clj -A:test:test-runner via cognitect-labs/test-runner works. Also I have the power of contains/just

seancorfield03:06:32

@souenzzo I've never found Midje's syntax readable, but it's good to know it can be used like that with deftest. With expectations.clojure.test that was my goal, so you can write (deftest foo (expect 3 (+ 1 2))) -- and expect lets you use specs, predicates, and you can "expect more" with destructuring etc.

Iwo Herka08:06:41

Cool, again: thanks for the info.

roklenarcic09:06:33

if I run lein uberjar with :aot :all in :uberjar profile, then all the macros should be expanded at compile time, right?

rickmoynihan10:06:13

Typically yes. Aside from anything funky like calls to eval.

yuhan11:06:56

Is it considered bad practice to use :refer :all even in a test namespace?

jumar11:06:00

It depends. I specifically avoid refer all because joker doesn't play well with that.

dpsutton13:06:41

and if you ever change to a cljc file you will have invalid cljs right?

4
yuhan13:06:30

hmm, I never considered that aspect of it - guess I'll just pick an arbitrary one-letter alias to use

dpsutton13:06:04

you can just use :refer [deftest testing is are] and get pretty far. I think i do that in addition to :as t

yuhan13:06:41

ah, I was referring to the namespace being tested, not clojure.test itself

dpsutton13:06:09

ah missed that.

yuhan13:06:01

often I write ad-hoc rich comment blocks in the source file itself to test functions, and then graduate those to actual tests where it makes sense

yuhan13:06:23

But I still think of the test ns as belonging to the same "environment", so having to go and add prefixes to all the vars seemed unnecessary compared to just a :refer :all

deep-symmetry16:06:15

I kind of like the approach of using :ss sut (for system under test).

yuhan11:06:13

i.e. when requiring foo from the ns foo-test

lread12:06:08

When just learning clojure, on my first clojure project I chose Midje because it seemed quite elegant to me at the time. I’m not sure I understand yet why some folks don’t like Midje. Is it that it tries to provide an easy way to write tests at the cost of complexity within itself? I don’t know. After dabbling in various open source projects for the last while, granted my sample size is small, I’ve not seen Midje used. Despite my original attraction to Midje, I don’t find myself suffering without it. Also, I think the author of Midje might have left the Clojure community?

Suni Masuno12:06:05

Hoping over to clojure from being a JS dev, and still pretty new. (loving clojure btw) Is there some approximate equivalent to eslint/prettier that allows for configurable static analysis/style rules? I'm finding myself wanting some programmatic way to declare conventions within the repo, and eslint and its ecosystem used to fill that niche in my life oh so wonderfully.

sveri12:06:38

@suni_masuno There is eastwood and cljfmt for instance . Also we have the clojure toolbox: https://www.clojure-toolbox.com/ which lists some more, but I am not sure how up to date it is.

Suni Masuno12:06:54

Ohhhh man, that toolbox link looks awful pretty, if it turns out to be old and crusty I may just cry.

sveri12:06:36

There are definitly up to date links inside, but I also found projects that are abandoned, so you just have to go through them.

Suni Masuno12:06:39

I can survive that, thanks for the heads up. ^_^ Man, this is in fact a LOT of what I'm looking for. I take it from eastwood/cljfmt getting separate billing that they did to be a bit more commonly used than the others listed?

sveri12:06:38

They were just the first that came to my mind and that I used myself. But, this is just me 😉

Suni Masuno12:06:57

At the very least this thing is making reading the project.clj sooooo much easier!

Alex Miller (Clojure team)12:06:24

the toolbox site takes PRs - if you find something out of date, please fix it!

Suni Masuno12:06:53

I'm something like 10 in and so far all's well, but I'll be sure to contribute if I find anything!

Suni Masuno12:06:44

I've also found a project or two in our project.clj that are in maintenance mode or otherwise look sketch and they aren't in the toolbox so... yeah I'm mildly impressed. 👏

sveri12:06:04

That doesnt have to be necessarily bad. Some stuff just works.

Suni Masuno12:06:23

Oh yeah, these are fine here, I'm just impressed toolbox seems clear of them.

Alex Miller (Clojure team)12:06:53

if you find missing stuff, you can add that too in a PR :)

borkdude13:06:13

@suni_masuno shameless plug, but there’s also my linter called clj-kondo: https://github.com/borkdude/clj-kondo/

borkdude13:06:36

it doesn’t have configurable conventions though, although you can turn off and on certain things

borkdude13:06:41

it could have configurable conventions, as an option. feel free to post an issue 😉

Suni Masuno13:06:54

I have at the very least learned that configurable linting isn't anywhere near as big a part of the clojure community as it is the JS. Though honestly I don't think clojure needs it anywhere near as bad as JS. ^_^

Suni Masuno13:06:15

And as a relative newb I'm finding all the rules people did think to write super useful

borkdude13:06:40

@suni_masuno you maybe want to look at “the” Clojure style guide: https://github.com/bbatsov/clojure-style-guide clj-kondo supports some of those

Suni Masuno13:06:01

Read it, found it awesome and love it. ^_^

mloughlin13:06:43

editing s-expressions is so much easier than curly-bracket/semi-colon languages when it comes to formatting 🙂

borkdude13:06:03

@michael.e.loughlin don’t even get me started about significant-whitespaced languages

mloughlin13:06:06

does parinfer count as significant whitespace? 😉

borkdude13:06:27

no, you’re still producing sexprs

borkdude13:06:53

I mean Python/Haskell

Suni Masuno13:06:26

Ohhh, thanks! ^_^

Suni Masuno13:06:43

Is there something wrong with me that I could read styleguides all day?

roklenarcic13:06:24

Someone runs my macro and passes in a namespaced symbol like so: (my-macro edn/read-string). How do I get the real namespace of this function I was passed in?

borkdude13:06:26

@roklenarcic what’s the the arg-list of your macro like? (defmacro my-macro [thing] ...)?

borkdude13:06:43

then that would be something like (namespace ~thing)

roklenarcic13:06:12

If I go:

(defmacro my-macro [f] (namespace f))

(my-macro end/read-string) => "edn"

roklenarcic13:06:36

or:

(defmacro my-macro [f] `(namespace ~f))

roklenarcic13:06:46

then I get clojure.edn$read_string cannot be cast to clojure.lang.Named

roklenarcic13:06:02

I'd like "clojure.edn"

borkdude13:06:31

are consumers of your macro supposed to pass in an unquoted symbol?

borkdude13:06:36

or can it be anything?

borkdude13:06:23

@roklenarcic I guess you can do something like this, but I’m still not sure what your use case is:

user=> (defmacro my-macro [sym] `(namespace (symbol (resolve '~sym))))
#'user/my-macro
user=> (my-macro contains?)
"clojure.core"

roklenarcic14:06:12

thanks I'll try that

roklenarcic14:06:31

my use case is that I want the macro to generate some keywords that match the ns of the first argument

Alex Miller (Clojure team)14:06:06

note that that use of symbol is new in 1.10

Alex Miller (Clojure team)14:06:15

so won't work on older versions

Suni Masuno14:06:50

Ok, bigger question. I'm finding a lot of veeery long let statements (to my uninitiated eye) something like...

(defn 
  something
  [ ~stuff~ ]
  (let [a1 ( ~some stuff~)
        a2 ( ~some stuff using a1~)
        a3 ( ~some stuff using a1 and a2~)
        a4 ( ~some stuff using a1 and a3~)
        a5 ( ~some stuff using a2 and a4~) ~many of these are 2-5 lines long~
        ~more lines, usually around 10-15~
        a17( ~some stuff using a15 and a16~)]                                                                                                                              
        {[:stuffs ~hard coded data output shape useing a15-17~]}))
this feels like C++ crammed into clojure to me, but is this a pattern I just don't know?

Alex Miller (Clojure team)14:06:33

it depends, but I think you're right to be somewhat wary of such code

hoynk14:06:24

On my tests, I would like to mock a few functions that access the database. Any suggestions on a good mocking library or maybe I should just use "with-redefs-fn"?

donaldball14:06:53

When testing things with side effects like this, you tend to have three options: 1. directly mock, e.g. with with-redefs 2. build up and tear down a real db 3. devise a protocol for db access and provide e.g. a reified mock impl for tests The choice is situational but I tend to use 2 and 3 more heavily.

hoynk14:06:07

To be honest, I usually go for 3 🙂. Since most people here tend to use 1 (on python code), I am trying to explore it and how it would feel in clojure code.

Suni Masuno14:06:18

I did find a stack overflow related, but it seemed fuzzy on the answer. Is this a legit division in the community maybe? https://stackoverflow.com/questions/19492584/let-bloat-or-idiomatic-clojure

manutter5114:06:13

Personally I think the “bloated” let style is actually a pretty good way to create “executable documentation,” assuming you give the intermediate values names that are informative and instructive.

Alex Miller (Clojure team)14:06:38

if there are really a lot of intermediate meaningfully-named calculations, this may be fine

Alex Miller (Clojure team)14:06:50

tasteful inlining can shorten that up

Alex Miller (Clojure team)14:06:13

macros can remove a lot of duplication if you see repeated stuff

Suni Masuno14:06:01

It feels so... iterative instead of declarative. Although I suppose no one is promising declarative as a goal of clojure

manutter5114:06:22

Also, I’m currently trying to work my way thru an issue where a key bit of code is one big long -> macro, and I have to use the debugger because it’s a 3rd-party lib inside a 3rd party lib, and I’m basically stuck because there’s no way to inspect the intermediate values, so 😞

manutter5114:06:42

That said, while I prefer let, blocks, I find that in my actual code, they’re usually not very long. Mostly 6 lines in the block, or less.

Alex Miller (Clojure team)14:06:26

some code really is a series of computations that occur in order

👍 4
andy.fingerhut14:06:28

It is iterative, but in Clojure it is still often purely functional (it isn't restricted to be, but often is)

andy.fingerhut14:06:29

well, sequential may be a better word than iterative there. Even then, lazy sequence processing can cause execution order to be different than it might first appear to be.

Jimmy Miller14:06:31

The code in that question is interoping with side effectful code. So it is going to be a bit more imperative. It has to be because of the imperative nature of the library it is calling. Let's can be a good tool for referencing values, especially if they are used multiple times in an expression. In general. I try to make my code so you can easily evaluate sub expressions in the repl. I find let's can often be annoying in that context.

bmaddy14:06:38

I thought I saw something added to clojure a while back that allows you add a new dependency to an already running repl. Am I imagining this? Does anyone know what I'm thinking of here? I think it was something in clojure itself and more recent than pomegranate. Context: every once in a while I'd like to load something like a profiling library, but not often enough that I want to put it in my project or profiles.clj.

Alex Miller (Clojure team)14:06:40

you're probably remembering the add-lib stuff in deps.edn

Alex Miller (Clojure team)14:06:15

which has not actually been integrated yet but is available for use on a branch via git dep

bmaddy15:06:05

Yep, that's totally it--thanks Alex!

Alex Miller (Clojure team)15:06:10

http://insideclojure.org/2018/05/04/add-lib/ (recent sha to use is e160f184f051f120014244679831a9bccb37c9de )

👍 4
dpsutton15:06:44

any idiom that blends cond-> and doto such that the "does" can be conditional?

danielneal15:06:50

@dpsutton you might like https://github.com/Engelberg/better-cond although I've found it can divide opinions 🙂

danielneal15:06:09

you can put side-effecting :do blocks in there

dpsutton15:06:06

little too heavy and new-languagegy for this. just gonna cond-> into doto forms this time

dpsutton15:06:19

but thanks for the recommendation. if we had it already i migth have

danielneal15:06:21

ah yeah, it's not exactly right for your use case anyway - sounds like you're banging on a object and want to do make some of the bangs conditional

dpsutton15:06:49

exactly. need to prevent serializing a null property to "null"

Suni Masuno15:06:17

@manutter51 In previous lives we had things like tap in ramda for just that, executes whatever it's code is over args but returns args unmodified. Is there no clojure version of that?

andrea.crotti15:06:41

does anyone know if I can create a zip file with ZipOutputStream without having files to zip?

andrea.crotti15:06:28

I mean given a vector of strings for example, and a mapping to assign to each of them a filename in the zip file, can I write them out directly in the stream?

andy.fingerhut15:06:16

No authoritative knowledge from me, i.e. I have not done it myself before. The Java API page for ZipOutputStream strongly implies the answer is "yes", although you will have to make the appropriate method calls when you want to finish one file inside the resulting zip file, and start another. Also, it is based on OutputStream, which only lets you write bytes to it, not Java String directly, so you will need to pick an encoding for those strings (e.g. UTF-8).

andy.fingerhut15:06:58

If by your question you were hoping to avoid some minutes of experimenting with the Java API in case the answer is "no", then I haven't helped here 🙂

andrea.crotti15:06:49

hehe no just asking if it's possible

andrea.crotti15:06:06

I did experiment already but I could not find out to still pass an input-stream without a file

andrea.crotti15:06:17

I'm using these two functions

(defmacro with-entry
  [zip entry-name & body]
  `(let [^ZipOutputStream zip# ~zip]
     (.putNextEntry zip# (ZipEntry. ~entry-name))
     ~@body
     (flush)
     (.closeEntry zip#)))

(defn zip-files
  "Zip all the given files into the desired output file"
  [output-filename files]
  (with-open [output (ZipOutputStream. (io/output-stream output-filename))]
    (doseq [f files]
      (with-open [input (io/input-stream f)]
        (with-entry output f
          (io/copy input output))))))

andrea.crotti15:06:34

so I guess I just have to do the various magic bytes transofrmation

andrea.crotti15:06:41

where it's doing the input and io/copy

andy.fingerhut15:06:40

This Java method getBytes is one way to convert a string to UTF-8, or any other encoding supported by your JVM: https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#getBytes-java.lang.String-

andy.fingerhut15:06:19

I am not sure the (flush) call you have will do anything useful there, as I believe it flushes the *out* OutputStream by default.

andy.fingerhut15:06:24

Maybe replace that with (.flush zip#) ? But perhaps the (.closeEntry zip#) call flushes for you. I don't know.

andy.fingerhut15:06:23

If you have input-stream for each entry, you should not need to manually do any conversion from strings to bytes. The io/copy method should preserve the original byte sequences of the input files without any encoding/decoding stuff going on.

andrea.crotti14:06:40

thanks @U0CMVHBL2 in the end I sorted it out by doing

(defn zip-content
  "Zip all the given files into the desired output file"
  [output-filename edns]
  (println (format "zipping %d files into %s" (count edns) output-filename))
  (with-open [output (ZipOutputStream. (io/output-stream output-filename))]
    (doseq [[entry-name content] edns]
      (with-entry output entry-name
        (let [bs (.getBytes (str content))]
          (.write output bs 0 (count bs)))))))

andrea.crotti14:06:48

which seems to work welle

andy.fingerhut15:06:36

glad you got it working

Suni Masuno15:06:12

@manutter51 I think judicious use of doto should do the same thing, right?

Iwo Herka15:06:13

Quick question: Is https://github.com/stuartsierra/component recommended as of 2019?

vemv12:06:48

have used it extensively in 2019 in a bunch of projects, as happy as ever with it I'd just make sure to use it via metadata-based protocol extension, to avoid Reloaded workflow issues. (Added in component 0.4.0)

jeroenvandijk15:06:23

There are some extensions paths, but you can start there

Jakub Holý (HolyJak)15:06:29

Is it possible to attach a Java annotation when creating a class with (proxy..) ?

Alan Thompson17:06:55

Nice blog entry on not trying to do much with :prep-tasks in lein: https://grishaev.me/en/lein/

deep-symmetry19:06:02

Hmm, there is some truth to this. But unfortunately the one case where I need to build my documentation using Antora needs to happen even for lein repl, because it is served by an internal web server even when you are running locally from source.

Suni Masuno17:06:34

I've noticed a slow but steady trend towards make in the JS world as well. Gulp/Grunt seem to have entirely passed on, and even npm scripts are slowly losing favor

Suni Masuno17:06:55

Imagine, a world where all the languages use the same build tool. ❤️

dominicm17:06:28

make is rather terrible in many ways though. It's not syntax aware. Gulp always seemed like a great solution to me.

Suni Masuno17:06:47

What do you mean by syntax aware? (says the curious newb)

dominicm18:06:33

Eg foo.c had a #include on bar.c, but make has no idea.

dominicm18:06:05

It can't perform any kind of dependency analysis automatically. Even though our code has those dependencies already.

borkdude18:06:12

is mach still being used in juxt? https://github.com/juxt/mach

dominicm18:06:56

A little, but not much

dominicm18:06:59

Why do you ask?

borkdude18:06:12

because make is being discussed

dominicm18:06:23

Derp, sorry. Didn't look up 😂

dominicm18:06:02

Just to clarify, I like make for many reasons. But I don't think it's where we should aspire to finish.

Suni Masuno19:06:28

How do I handle a comp ( -> and ->>) when my funcs go back and forth from data first to data last?

noisesmith19:06:16

the other options listed here are OK, but ->> is usable inside -> for arbitrary alternation as well

noisesmith19:06:43

(-> foo (bar) (->> (baz quux)) (bar again))

😮 4
noisesmith19:06:49

in fact any of the arrow macros can be used inside ->, that's why they all take a value as the first arg

Suni Masuno19:06:18

That is downright clever

jumar08:06:24

Note that you probably shouldn't do this regularly - see https://stuartsierra.com/2018/07/06/threading-with-style ("Dont mix -> and ->>")

nickmbailey19:06:00

"as->" ?

✔️ 4
Suni Masuno19:06:40

90% as good... works for me

Suni Masuno19:06:10

Also, I must admit it's a kinda clever idea

borkdude19:06:24

or just use let 😉

borkdude19:06:12

you find my comment mean? I didn’t intend it so 😉

Suni Masuno19:06:00

Naw, but tone humor is really hard in text. ¯\(ツ)

lread13:06:18

@suni_masuno I find Clojurians are typically smart, kind and generous. If tone is ever in question for you, you can assume friendly. simple_smile

nickmbailey19:06:44

and just avoid threading altogether you mean?

yuhan19:06:59

You might be interested to macroexpand the as-> form to see what it actually does under the hood 🙂

nickmbailey19:06:06

oh gotcha, yeah i just jumped in at the end there

borkdude19:06:23

@suni_masuno when you need to thread through 17 expressions, it’s maybe good to chop it up into a few lets or functions

Suni Masuno19:06:03

That seems pretty reasonable, I suppose finding that balance is part of the art.

deep-symmetry19:06:29

Neat! I was invited to write a two-page spread for a new O’Reilly book titled “97 Things Every Java Programmer Should Know” that is also being serialized on Medium. Their process is a lot faster than it used to be—I wrote a draft yesterday, and it was just published: https://medium.com/97-things/rediscover-the-jvm-through-clojure-fa2ac5e29cbb

❤️ 8
fabrao20:06:53

Hello all, I want to generate a clojure.java.jdbc query infinite loop and leave it running without wait response. Is that possible?

ghadi20:06:10

probably subject to timeouts on your jdbc driver. I'm going to go out on a limb and say that it's probably not a good idea

ghadi20:06:50

I'm sure you could increase timeouts on the driver

fabrao20:06:03

@ghadi I have to do it for my unit test, that I´ll create blocking session to test if it exists

ghadi20:06:10

what do you mean by "infinite loop"? looping in clojure or in some PL/SQL or something?

fabrao20:06:50

"DECLARE @value int
SET @value = 1
WHILE 1=1
BEGIN
SET @value = @value
END

fabrao20:06:24

if I execute it in unit test, it will block the execution for the rest of test

fabrao20:06:55

so, I want to execute it async

fabrao20:06:45

without leave thread executing, only in database

fabrao20:06:24

If I use in thread, I´ll kill both if stop thread

fabrao20:06:26

it´s kind of execute and forget it

ghadi20:06:03

you can put it in a background thread like a future, but without seeing the rest of the SQL this seems like there is a race condition in there

ghadi20:06:56

you might want to ask in #sql -- might have more specific expertise than me

fabrao20:06:45

ok, thanks for your help

lvh21:06:02

is there a clever way to match against multiple regexes simultaneously?

lvh21:06:27

(and knowing which one matched in the end)

lvh21:06:52

I can think of not clever ways, like generating a new pattern with named groups, each with a gensymd name, matching an empty string, and then make every pattern an alternation

lvh21:06:05

instaparse parses regexes?

4
hiredman21:06:21

in at least 2 ways

hiredman21:06:20

1. it can handle context free languages which are a superset of regular languages 2. it has some syntax for specify terminals as regexes

lvh21:06:40

oh hey, yep, I didn't know about regex terminals

lvh21:06:03

(i mean regular expression in both the common java syntax and the parser-theory sense here)

hiredman21:06:05

there are some alternative java regex libraries as well, which might give you more options then the built in stuff

dpsutton21:06:15

so you'll make an ambiguous grammar and get all parsings from it?

lvh21:06:22

yeah my first guess was re2j, which doesn't have that

hiredman21:06:51

it drives me up the wall that I can't just combine Patterns with some kind of or combinator

lvh21:06:51

my second guess was the "make a new pattern with alternations and empty named groups in front"

lvh21:06:19

instaparse's regexp is a real regular expression, not the full-fledged pcre-style thing with backrefs, right?

lvh21:06:01

(I guess I don't realllllly care, but I happen to know all of these regular expressions are actually regular)

lvh21:06:30

anyway hooray, thanks @hiredman

hiredman21:06:53

that I don't know, I think it just hands them to the Pattern constructor

lvh21:06:33

source agrees

lvh21:06:33

that's somewhat unfortunate because I already do sequential matches, and presumably instaparse can't do any better (computationally, I mean -- maybe syntactically, sure)

aengelberg21:06:22

that's correct, instaparse will defer to java regexes re: performance of each individual parse

aengelberg21:06:22

the main goal of the regex terminal is to attempt to plug the syntactic power of regexes into the high-level parse / backtracking engine of instaparse

aengelberg21:06:31

it isn't a super clean fit since instaparse assumes a 1:1 mapping from start-positions in the string to regex results, where it could actually be 1:many

lvh21:06:45

I mean, it doesn't matter tremendously because since Matcher only supports .group(String groupName) you still have to enumerate all possible groups to know which one matched exactly, meaning I'm not going to be happy with how this looks regardless

lvh21:06:57

in conclusion time to write java-regexp-syntax.bnf

aengelberg21:06:31

if instaparse had a "build your own combinator" support would that help at all?

lvh21:06:18

I mean I've never used instaparse's combinators but I have a hard time believing you wouldn't have all the stuff you needed for regular languages, so yeah

lvh21:06:21

I would asusme the way to implement this is to parse the regex Java(TM) pattern string with instaparse, compile the parse to an instaparse parser with combinators, then have {:A firstpattern :B secondpattern ...}

lvh21:06:40

(I have a day off which is why I think that's a good idea)

andy.fingerhut22:06:19

Any "pure" regex (i.e. one from classic automata theory that represents a regular language, without some of the lookahead/lookbehind stuff that Perl and some other regex libraries added on top of that) can be combined with any number of other regexes to create a deterministic finite automata that recognizes the intersection, union, and/or complement of those languages, too. It can make the number of states in a deterministic finite automata get pretty large.

SCBM22:06:48

I'm wondering if generators in clojure test.check can be seeded somehow to create deterministic random results from generators