Fork me on GitHub
#clojure
<
2019-10-24
>
wcalderipe03:10:11

Hey folks, how are you dealing with consistently and automatic code formatting in your teams (assuming that people are using different editors/IDEs)? I'm wondering if is there out in the wild something similar to lint-staged (https://github.com/okonet/lint-staged) for Clojure code formatting.

vemv12:10:41

https://clojars.org/formatting-stack (released on Clojars only - not on github yet) uses exactly this approach - integrating git strategies (staging area, or a diff across branches) with formatters/linters. Looking forward to a release in December 🙂

👍 4
wcalderipe00:10:51

@U45T93RA6 thanks for sharing it... Great lib you had over there. I'm wondering if you have a few configuration examples around so we can spike around it as well? If it help to solves our inconsistency code formatting issues here in Atlassian we'll be more than happy to contribute to the lib! 🙌

vemv04:10:25

oh, ace to hear! yes, will be happy to have "private alpha" users outside of work. if you download and uncompress the .jar (`[formatting-stack "1.0.0-alpha9"]` is the latest recommended version), you'll find a quite detailed README over there. There's also a (private) wiki, but you can live without it - there's nothing crucial over there. The recommended ways to use formatting-stack are described in README's Component/Integrant integration and Reloaded Workflow integration. Btw, since we don't use Integrant at work, so that specific integration might be lagging behind (can fix!). tldr: • in the Component system of a given project, you can place a formatting-stack.component/Component, and that's it. Reformatting will happen on (reset), observing git status • you also can use this as an IDE snippet: (clojure.tools.namespace.repl/refresh :after 'formatting-stack.core/format!). That helps in smaller projects that may not be using Component/Integrant. • And, you can also refer some repl helpers in your user.clj/dev.clj: https://github.com/nedap/speced.def/blob/afce350c00f2673c9210d9e6aacc3c5a0d22e2df/dev/dev.clj#L10-L12 We also are working on a CI integration (e.g. fail builds if formatters/linters fail, for the diff between $feature_branch and $default_branch). Might be ready this week Hope that helps you assess the tool. Can answer any DM 🙂

wcalderipe05:10:14

@U45T93RA6 thanks again for putting time on writing this explanation... we'll take a look in the solutions out there and yours included! 😄

✌️ 4
seancorfield04:10:27

@wcalderipe The "Clojure way" of formatting is pretty uniform across most editors/IDEs I think -- and there's the community style guide.

seancorfield04:10:32

There are linters but they're more about code constructs and some of the non-layout stuff in the style guide.

dpsutton04:10:32

CIDER and cursive usually have some small differences. I think colin does a lot of work to be amenable to us folks stuck on a 1970s editor 🙂

😄 4
seancorfield04:10:28

@dpsutton I'm a bit surprised that Cursive doesn't use "standard" paredit/parinfer etc indentation?

cfleming04:10:37

@seancorfield There really is no standard, and formatting varies pretty widely in the wild.

cfleming04:10:45

This is the closest thing to anything that could be considered a standard, if only because it’s the only option which isn’t form-dependent: https://tonsky.me/blog/clojurefmt/

seancorfield04:10:02

I'm a bit surprised to hear that -- I very rarely notice much variation across all the open source projects I work with. Perhaps there are small differences I just don't see at this point...?

hiredman04:10:18

I notice little differences in formatting between our code. Mostly in cases where the old version of clojure-mode I have gets it wrong.

seancorfield04:10:06

Tonsky's post is hardly a "standard" given how much it disagrees with the style guide and how CIDER / paredit / parinfer generally work 🙂

hiredman04:10:14

I have an key binding that reformats the current buffer, and often hit it without thinking about it (old habits)

cfleming04:10:33

It’s not a standard, but it’s the only thing that could eventually become one IMO.

seancorfield04:10:37

@hiredman maybe you're more OCD about code layout than me? 🙂 (and I thought I was pretty OCD 🙂 )

hiredman04:10:45

So I then see the little differences in the git diff

seancorfield04:10:02

Ah, I usually ignore whitespace in git diffs 🙂

hiredman04:10:30

Oh man, my last team, we had tests that would fail if code wasn't formatted the "correct" way

seancorfield04:10:52

I've been on teams like that -- in other languages.

hiredman04:10:18

So you would get build failures from the ci server, and I used to think that was cool

hiredman04:10:27

What a waste

seancorfield04:10:18

Life's too short to argue about whitespace and indentation, eh...

cfleming04:10:28

And things like 1-space vs 2-space list indents etc are the source of much debate: https://github.com/bbatsov/clojure-style-guide/issues/126. There are a couple of pull requests on the style guide repo trying to amend that.

cfleming04:10:12

I mean, it’s totally bikeshedding, my point is that it’s not really decided, and the style guide isn’t really gospel.

hiredman04:10:56

I had a PR on the style guide once, it got rejected, but to be fair I deleted all of it and replaced it with a sick Bruce Lee quote

4
🥋 8
seancorfield04:10:45

I remember seeing that thread and just sort of rolling my eyes and getting on with my life ¯\(ツ)

wcalderipe04:10:28

Thanks for your explanation and for the shared references@seancorfield @dpsutton @cfleming. I have see differences between editors and IDEs code formatting and it does surprises me that Clojure doesn't have an "one to go format" out there. Personally, I see code formatting as noise in PRs it does taking people away from what really matters.

seancorfield04:10:22

If someone sends me a PR with spurious reformatting in it, I just reject it. Show some respect when you work on an OSS project: follow the existing style/layout when you submit proposed changes.

wcalderipe00:10:51

Thanks for sharing @seancorfield, but unfortunately the page is responding with 404 for me. Maybe it's just a temporary downtime... I'll retry later!

wcalderipe05:10:13

@seancorfield thanks, this one works :thumbsup: i agree with the arguments there, however, it shouldn't be difficult to automate this process to avoid the formatting discussion all over again every time someone new joins the team!

seancorfield04:10:11

If I submit a PR to someone's project, I try hard to follow whatever style they're already using. My proposed changes aren't about the layout.

seancorfield04:10:30

(I will say that if you have the "tabs vs spaces" wars going on in your language community, you're going to see a lot more wild formatting differences in PRs... Clojure's mostly "2 spaces" so that sort of thing seems much rarer... mostly 🙂 )

👍 4
Mattias04:10:31

For all the power programmers wield, we still can’t let everyone use whatever visual layout and keep the central source in some normalized format. It’s always baffled me how we can’t separate presentation and storage in this specific instance, where we always go on about it everywhere else.

👍 12
mloughlin09:10:31

we like our text file based tooling too much! If we stored s-expressions in a database we could totally do loads of crazy stuff, but the tooling would be ad-hoc and language specific

sogaiu04:10:16

i try to follow the existing style -- if i can figure out what it is 🙂

8
vlaaad09:10:38

it would be nice to get at least some macros as values/functions... for example, or to provide a default in a map: (update opts :encoding or "UTF-8")

delaguardo09:10:12

plumbing library has a bunch of useful functions - http://plumatic.github.io/plumbing/plumbing.core.html

vlaaad09:10:43

thanks, but that was just an example of something that can behave both as macro and as function

andy.fingerhut09:10:47

you mean, added to clojure.core? Such additions are far and few between these days (saying no to an addition is temporary, saying yes to an addition is forever).

andy.fingerhut09:10:10

If that isn't what you meant, such a function is trivial to write, yes?

vlaaad09:10:10

it's trivial, sure, so it's about addition to clojure language. I just thought that there are usecases for "soft macros", or maybe they should be called "inlineable functions", so they are both functions which you can have as a value and do optimized stuff like short-circuiting and not evaluating some args it all when called as is

vlaaad09:10:34

probably not gonna happen ¯\(ツ)

cmdrdats09:10:48

Hi - sorry to interject current conversation, please thread replies on this message? I'm having issues with atoms and pr-str, edn/read-string... trying to figure out how to work around this: Clojure:

(clojure.edn/read-string (pr-str (atom "hello")))
Execution error at user/eval61644 (form-init4368157810432879853.clj:1).
No reader function for tag object
ClojureScript:
(cljs.reader/read-string (pr-str (atom "hello")))
#error {:message "No reader function for tag object.", ....
Obviously because:
(pr-str (atom "hello"))
=> "#object [cljs.core.Atom {:val \"hello\"}]"
How do I get clojure and clojurescript readers to read this value properly? I can workaround it by actually introducing a cleanup layer, but I'd really rather not...

vlaaad09:10:00

you really can't send the atom itself over wire, since it has mutable state which won't survive to string/from string conversion. if you want to read edn as some value for exploration purposes, you should use default reader like that:

(clojure.edn/read-string {:default tagged-literal} (pr-str (atom 1)))

vlaaad09:10:16

(tried in clojure, probably will work the same cljs)

andy.fingerhut09:10:01

Immutable values make a lot of sense to serialize and deserialize over the wire, but things get murky fast for mutable things.

andy.fingerhut09:10:17

i.e. "properly" can become very tricky to define precisely.

cmdrdats09:10:43

@vlaad thanks, that's good to know

cmdrdats09:10:48

@U0CMVHBL2 ye - you have a good point.. In my case I'm creating a derived atom and slapping it into my app state (which I'm sending off to record for later debugging)..

cmdrdats09:10:21

Need to revisit the implementation and see if I can slice in a cursor-atom for the derivative instead of creating a new atom

cmdrdats09:10:38

that would be a lot cleaner

cmdrdats09:10:59

interesting - rum/derived-atom actually handles getting passed in an existing atom in the options... perfect.

cmdrdats09:10:04

thanks for the help 😄

andy.fingerhut09:10:09

All clojure function calls cause all args to be evaluated before the function is called. Short-circuiting is impossible via a function call, unless you use techniques like passing futures/promises/lazy-seqs as args.

vlaaad09:10:14

yes the idea I've had is about extending the language itself, and probably not going to happen..

andy.fingerhut09:10:49

No worries. I've had plenty of ideas that are probably not going to happen, too 🙂

Shuai Lin09:10:59

A question about class loader for importing java classes (in repl): Does it use the per-thread context class loader? The example on pomegranate home page just doesn't work for me:

(use '[cemerick.pomegranate :only (add-dependencies)])
(add-dependencies :coordinates '[[joda-time "2.10.4"]]
                    :repositories (merge cemerick.pomegranate.aether/maven-central
                                        {"clojars" ""}))
The deps are indeed downloaded, and urls for the per-session-thread DynamicClassLoader did got updated to include the downloaded jars, so I can see pomegranate has done its part of the job. However, when trying to import the dependency I still got ClassNotFoundException
user> (import org.joda.time.DateTime)
Execution error (ClassNotFoundException) at java.net.URLClassLoader/findClass (URLClassLoader.java:382).
org.joda.time.DateTime
I inserted a breakpoint in the DynamicClassLoader.loadClass (a conditional breakpoint so it only hits when the target classloader is invoked) , and it never fired

Arto Kalishian10:10:01

In "Programming Clojure" book, it is claimed that for Clojure the number 0 is True but when I tried it on the REPL (true? 0) gives me -> false Any explanation?

Arto Kalishian10:10:28

Even (= 0 true) gives me -> false.

Arto Kalishian10:10:43

Is this outdated material or something?

lispyclouds10:10:12

@arto.eg this exactly checks if the value is true not truthy

Arto Kalishian10:10:34

So truthy has another check function?

lispyclouds10:10:31

so in clojure, falsy values are false and nil. everything else is truthy

lispyclouds10:10:09

so checking not falsy is probably what you want?

👍 4
vlaaad10:10:43

(if 0 :truthy :falsey) => :truthy

👍 4
lispyclouds10:10:40

probably surround your expr that you wanna run when truthy with a when-not

Arto Kalishian10:10:53

Great. Makes sense now! 👍

bronsa10:10:15

(and x true) to test for truthyness

👍 4
vlaaad11:10:22

(boolean x) to test for truthiness!

👍 4
ackerleytng14:10:08

Is it a bad idea to use take-while on the output of pmap?

dominicm14:10:38

Depends on the behavior you expect.

ackerleytng14:10:03

I'm expecting map but parallel, stopping after the first element that fails the predicate, in the sequence that map would do

dominicm14:10:58

I don't think pmap would give you that. I think it looks ahead, but I'm not certain.

Alex Miller (Clojure team)15:10:35

it goes N at a time, so you may do some extra work beyond what you take

Alex Miller (Clojure team)15:10:59

that seems inevitable for a parallel map and take

dominicm15:10:44

Yeah. It's fairly intrinsic.

Arto Kalishian15:10:30

I solved this problem with (sort) but I am not satisfied. Is there a better solution?

mpenet15:10:50

clojure.core/max comes to mind

Arto Kalishian15:10:05

It's restricted for this exercise as you can see in the red box.

Arto Kalishian15:10:55

It's restricted to allow beginners to think harder, which is good. But by using sort I just fooled the exercise so I wanted a better answer 🙂

danielneal15:10:05

I think this is a fine answer 🙂

Arto Kalishian15:10:43

Maybe I can become more specific.. I didn't think that using If conditions is Idiomatic.

Arto Kalishian15:10:59

So I wanted to know if there is a better clojure way than sort.

markmarkmark15:10:02

if you wanted to see the "proper" way to do it, I would just (source max) at a repl and see what it does.

👍 4
Arto Kalishian15:10:29

Never thought of that 🙂 Good point!

markmarkmark15:10:01

it's good to not do that at first when you're doing the 4clojure problems, but eventually you want to know "the right way" so that you can learn how you should do it

markmarkmark15:10:51

I remember when i first started the 4clojure problems all of my solutions had loop/recur... but I learned not to do that by studying the code in core.

markmarkmark15:10:18

It can be pretty dense, but it'll get easier and easier to reason about what the code is doing as you get better

Arto Kalishian15:10:29

Thanks for sharing that, because It's exactly what I tend to think when I start solving a problem.... and I am not happy loops come to mind first.

markmarkmark15:10:51

yeah, it's hard to get out of the mindset

markmarkmark15:10:58

though, loops aren't the worst ever

markmarkmark15:10:12

but eventually you start to see things in terms of reduce, map, etc

markmarkmark15:10:27

(p.s. try to do it with reduce)

Arto Kalishian15:10:39

Sometimes I am not able to draw a line between destructuring and pattern matching but anyway I would probably prefer them from loops.

markbastian15:10:58

This was how I did it: (fn[f & r](reduce (fn[a b](if (> a b) a b)) f r))

👍 4
danielneal15:10:37

(fn [& coll] (reduce (fn [x y] (if (> y x) y x)) coll)) might work also

👍 4
Arto Kalishian15:10:54

Yes guys I agree, I think I need to refactor my mind around reduce much more often.

Arto Kalishian15:10:59

It's a functional gem.

markbastian15:10:48

Generally, any time you have N things and want a single result use reduce.

👍 4
markbastian15:10:29

I should say "Single result after one pass over all N things".

sgerguri15:10:40

I'm going to be the black sheep here and suggest that you don't study the internals of the core namespace - there are precious few exercises at 4clojure where you actually need that level of optimization, more often than not it's the knowledge of what's in the standard library that will be most helpful. And for that, http://clojure.org/api/cheatsheet is indispensable. 🙂

12
dominicm15:10:10

It depends what you're trying to get out of 4clojure. If you want to know exactly how you'd do it if you were making the same trade-offs as Clojure, then by all means go for it. If you're just letting it guide you to new parts of the core library, then don't bother.

markmarkmark15:10:46

I think the advantage of looking at the core library is just that it's a specific, obvious place to look. You won't necessarily need the optimizations that they make, but it's just something to look at at all.

markmarkmark15:10:14

when you're starting out it's not obvious where to look for code

markmarkmark15:10:02

I wish that 4Clojure would just show you some random solutions from people (when you haven't followed anyone yet) when you solve a problem rather than making you follow people first.

borkdude17:10:50

what should I do with warnings like this in JVM 11?

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by clojure.lang.InjectedInvoker/0x00000008003cb040 (file:/Users/borkdude/.m2/repository/org/clojure/clojure/1.10.1/clojure-1.10.1.jar) to method com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl.parse(org.xml.sax.InputSource,org.xml.sax.HandlerBase)
WARNING: Please consider reporting this to the maintainers of clojure.lang.InjectedInvoker/0x00000008003cb040
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

Alex Miller (Clojure team)17:10:59

I think you can use --illegal-access=debug to get more info about what is happening

Alex Miller (Clojure team)17:10:48

it will often point to clojure here but only because the clojure reflector is involved

Alex Miller (Clojure team)17:10:03

(and fwiw, "future release" here does not include at least up through Java 13, so not sure how serious that admonishment is)

borkdude17:10:31

thanks, enjoying it 🙂

kenny17:10:04

That message is annoying because it prints to stderr. On prod app startup, this results in several lines getting logged as error messages. We have to implement custom filters to prevent those messages from triggering alerts.

bfabry17:10:00

anyone know a way to make (prn (quote (quote foo))) print ’foo instead of (quote foo)?

noisesmith17:10:13

the only thing I can think of is a postwalk with a list -> string substitution, but even then that wouldn't quite work with prn...

bfabry17:10:41

I know there’s hooks via print-method, maybe I can mess with the print-method for List

noisesmith17:10:27

user=> (w/postwalk (fn [f] (if (and (list? f) (= (first f) 'quote)) (symbol (str "'" (second f))) f)) [:a (quote (quote foo))])
[:a 'foo]

noisesmith17:10:05

(prn for that prints the same string you see in the result)

Alex Miller (Clojure team)17:10:49

why do you want to do that?

bfabry17:10:56

was looking at this SO question and assuming zprint was getting its base string from there. I may be wrong about that now I look closer though https://stackoverflow.com/questions/58308404/configure-symbol-quote-expansion-in-clojure-zprint

Alex Miller (Clojure team)17:10:41

there actually is custom support in the clojure printer for printing code, don't remember if it does the quote translation

Alex Miller (Clojure team)17:10:01

user=> (require '[clojure.pprint :as pp])
nil
user=> (binding [pp/*code-table* (assoc @#'pp/*code-table* 'clojure.spec.alpha/def #'pp/pprint-hold-first)] (pp/with-pprint-dispatch pp/code-dispatch (pp/pprint '(quote foo))))
'foo

Alex Miller (Clojure team)17:10:53

you don't even need all that, there's some extra stuff for spec support in that (dredging out of my notes)

bfabry17:10:02

pprint actually always did the correct thing, this is about zprint 🙂

maybenot17:10:29

Is there any concise overview to clojure interfaces and protocols? I mean seq, seqable, reducible etc. My intent is to understand for example why implementing IReduceInit for io/reader allows to use it in the context of a coll (e.g. in sequence and eduction)

Alex Miller (Clojure team)17:10:20

well, in general, Clojure is mostly implemented in terms of Java interfaces (and to a lesser extent, Clojure protocols). All of the core library is written to use those abstractions, not concrete implementations. If you know which things to fill in, you can make your stuff work like core stuff.

Alex Miller (Clojure team)17:10:52

self reducibility is implemented in the (open) CollReduce protocol, which has a coll-reduce function

Alex Miller (Clojure team)17:10:23

it's extended to IReduceInit/IReduce, which are Java interfaces that concrete Java classes can implement to hook into that protocol

Alex Miller (Clojure team)17:10:09

if you're doing Java stuff, it's probably best to implement one of those interfaces. if you're doing Clojure stuff, it's probably best to extend CollReduce on your thing

👍 4
Arto Kalishian19:10:35

Is there any news about updating the Clojure error messages? Any references to such news or it's not going to be implemented?

bfabry19:10:14

Which error messages? There were significant changes in both 1.9 and 1.10. I can't imagine the work there has completely finished either

Arto Kalishian19:10:11

Glad to here there was progress. But I am using 4clojure so I am not sure which version they're using. Maybe that's why.

bfabry19:10:42

Sadly 4clojure is on 1.4

Arto Kalishian19:10:15

Is it that hard to update it?

bfabry19:10:48

So a significant way behind. On the plus side you can usually copy paste code from 1.4 to. 1.10 and it works. Clojure is is very stable across versions.

Arto Kalishian19:10:52

The compiler should be backward compatible.

bfabry19:10:20

Generally it is other than very rare cases

bfabry19:10:54

I don't actually know what the deal is there. Possibly just no one willing to maintain it

bfabry19:10:20

Does seem like the current codebase is using Mongo and some old web frameworks. Might be part of people's reluctance to take it over

Arto Kalishian19:10:50

I am willing to take over it, but since I am a beginner probably will not be able to.

seancorfield19:10:58

(and then there's the cost of hosting it all)

seancorfield19:10:33

For folks working on 4clojure problems, there is now a dedicated channel #4clojure

carocad19:10:56

hey everyone, quick question. Is it possible to extend a Jvm type to support clojure functions like conj/cons/assoc ? In the same way of extend-protocol ?

Alex Miller (Clojure team)19:10:41

is it a type you make and control? or an arbitrary thing?

Alex Miller (Clojure team)19:10:06

if the former, then yes, by implementing the right interfaces. if the latter, then no.

carocad20:10:41

the latter 😞

carocad20:10:06

although I might be able to trick the system :thinking_face:

carocad20:10:17

thanks a lot for the info

Alex Miller (Clojure team)20:10:57

the other approach is to wrap - reify the interfaces and implement over the instance

carocad20:10:06

yeah I was thinking exactly of that. It might get dirty though but I guess it would work

Alex Miller (Clojure team)20:10:40

you can see examples of this in various places, like bean

Alex Miller (Clojure team)20:10:54

that's a proxy approach

Alex Miller (Clojure team)20:10:01

which has pros and cons

ec21:10:51

hey why i cant bind model of a table to a atom with seesaw? Im doing sth like this (def model-atom (atom [:columns .... :rows ....])) (bind model-atom (property :model table)

emccue22:10:37

@carocad you can't since those aren't protocols, but you can make a protocol yourself if you want

emccue22:10:04

or a multimethod

emccue22:10:44

or... if you are feeling absolutely evil

emccue22:10:07

(ns my.ns
  (:refer-clojure :rename {cons core-cons}))

(defmulti cons (fn [head tail]
                 (type tail)))

(defmethod cons :default
  [head tail]
  (core-cons head tail))

(alter-var-root #'clojure.core/cons (constantly cons))

emccue22:10:26

then you can extend it however you want

emccue22:10:11

which i cant seem to get to work at the repl, but in principle does the evil voodoo

seancorfield22:10:56

Possibly because Clojure core itself is direct linked?

Alex Miller (Clojure team)22:10:58

clojure core is aot compiled and direct linkeed

Alex Miller (Clojure team)22:10:08

so internal calls to cons will not see this redef