Fork me on GitHub
#beginners
<
2019-06-07
>
papachan01:06:22

Finally i get it working. found this: https://figwheel.org/docs/hot_reloading.html

👍 4
johnj02:06:56

Can a string in a deps.edn span multiple lines?

alexmiller02:06:21

strings in edn can span multiple lines, just like clojure

alexmiller02:06:12

but I suspect the main place you'd want to use one in a deps.edn would be in something like jvm-opts, which has known escaping issues with whitespace

alexmiller02:06:27

what's your actual goal?

johnj02:06:55

yep, to pass -Dclojure.server.repl="{:port 5555 :accept clojure.core.server/repl}" to jvm-opts

johnj02:06:57

wanted to break that since I'm also passing :address

alexmiller02:06:13

the workaround for this is to replace all spaces with commas, which act as whitespace for clojure/edn but don't have escaping issues in the shell

johnj02:06:10

newlines too?

johnj02:06:12

is working with all in one line

alexmiller02:06:29

not sure if you can break up the line or not

johnj02:06:46

I'll leave at one line, no biggie thanks

johnjelinek05:06:53

I'm using tools.deps -- what's the common way to build an uberjar?

johnjelinek05:06:28

I added the following to my deps.edn:

:aliases {
  :uberjar {:extra-deps {uberdeps {:mvn/version "0.1.4"}}
            :main-opts ["-m" "uberdeps.uberjar"]}
}

johnjelinek05:06:45

but when I run clj -R:uberjar -- it just takes me to a repl

johnjelinek05:06:56

is the programmatic API the only way to get this to execute?

johnjelinek05:06:05

oh, I got it to execute with: clj -m uberdeps.uberjar but only after moving the dep to :deps and removing the alias

seancorfield05:06:23

No idea about tonsky/uberdeps but we use (my fork of) depstar at work for this.

johnjelinek05:06:24

but, now I notice my code isn't being added to the package

johnjelinek05:06:45

@U04V70XH6: got a link to depstar?

seancorfield05:06:57

-R won't be enough, you need -A

seancorfield05:06:13

Because you need main opts as well as the resource deps adding.

seancorfield05:06:26

clj -A:uberjar

seancorfield05:06:54

(for depstar you would specify the name of the JAR file you want built -- no idea about tonsky's package)

seancorfield05:06:03

(includes depstar and many others)

johnjelinek05:06:36

hmm ... still running into trouble

johnjelinek05:06:03

example:

# clj -A:native-image
Exception in thread "main" java.io.FileNotFoundException: -A:native-image (No such file or directory)
        at java.base/java.io.FileInputStream.open0(Native Method)
        at java.base/java.io.FileInputStream.open(FileInputStream.java:196)
        at java.base/java.io.FileInputStream.<init>(FileInputStream.java:139)
        at java.base/java.io.FileInputStream.<init>(FileInputStream.java:94)
        at clojure.lang.Compiler.loadFile(Compiler.java:7570)
        at clojure.main$load_script.invokeStatic(main.clj:452)
        at clojure.main$script_opt.invokeStatic(main.clj:512)
        at clojure.main$script_opt.invoke(main.clj:507)
        at clojure.main$main.invokeStatic(main.clj:598)
        at clojure.main$main.doInvoke(main.clj:561)
        at clojure.lang.RestFn.applyTo(RestFn.java:137)
        at clojure.lang.Var.applyTo(Var.java:705)
        at clojure.main.main(main.java:37)

johnjelinek05:06:37

deps.edn:

;; The deps.edn file describes the information needed to build a classpath.
;;
;; When using the `clojure` or `clj` script, there are several deps.edn files
;; that are combined:
;; - install-level
;; - user level (this file)
;; - project level (current directory when invoked)
;;
;; For all attributes other than :paths, these config files are merged left to right.
;; Only the last :paths is kept and others are dropped.

{;; Paths
  ;;   Directories in the current project to include in the classpath

  ;; :paths ["src"]

  ;; External dependencies

 :deps {org.clojure/clojure {:mvn/version "1.10.0"}
        org.clojure/data.json {:mvn/version "0.2.6"}
        com.amazonaws/aws-lambda-java-core {:mvn/version "1.0.0"}
        nrepl {:mvn/version "0.6.0"}}

  ;; Aliases
	;;   resolve-deps aliases (-R) affect dependency resolution, options:
	;;     :extra-deps - specifies extra deps to add to :deps
	;;     :override-deps - specifies a coordinate to use instead of that in :deps
	;;     :default-deps - specifies a coordinate to use for a lib if one isn't found
	;;   make-classpath aliases (-C) affect the classpath generation, options:
	;;     :extra-paths - vector of additional paths to add to the classpath
	;;     :classpath-overrides - map of lib to path that overrides the result of resolving deps

  ;; :aliases {
  ;;   :deps {:extra-deps {org.clojure/tools.deps.alpha {:mvn/version "0.2.196"}}}
  ;;   :test {:extra-paths ["test"]}
  ;; }
 :aliases {:native-image
           {:main-opts ["-m clj.native-image core"
                        "--initialize-at-build-time"
                        ;; optional native image name override
                        "-H:Name=core"]
            :jvm-opts ["-Dclojure.compiler.direct-linking=true"]
            :extra-deps
            {clj.native-image
             {:git/url ""
              :sha "567176ddb0f7507c8b0969e0a10f60f848afaf7d"}}}}

  ;; Provider attributes

  ;; :mvn/repos {
  ;;   "central" {:url ""}
  ;;   "clojars" {:url ""}
  ;; }
 }

seancorfield05:06:20

This seems unrelated to your previous question?

johnjelinek05:06:56

well .. I tried to switch gears from the link you sent me with one of the other packagers

johnjelinek05:06:05

I can switch to stay on topic -- I'll try depstar

seancorfield05:06:10

This is wrong: "-m clj.native-image core" -- you should specify a vector of arguments, not a single string containing multiple arguments.

seancorfield05:06:54

["-m" "clj.native-image" "core" ...]

seancorfield05:06:42

Bear in mind: depstar does no AOT and adds no manifest -- so whether it's suitable or not will depend on your expectations.

seancorfield05:06:09

I do not know what tonsky/uberjar does in that respect. All these different packaging tools do different things.

johnjelinek05:06:51

ok, shifting gears:

{:deps {org.clojure/clojure {:mvn/version "1.10.0"}
        org.clojure/data.json {:mvn/version "0.2.6"}
        com.amazonaws/aws-lambda-java-core {:mvn/version "1.0.0"}
        nrepl {:mvn/version "0.6.0"}}

 :aliases {:depstar
           {:extra-deps
            {seancorfield/depstar {:mvn/version "0.2.1"}}}}}

johnjelinek05:06:29

# clojure -A:depstar -m hf.depstar.uberjar target/clj.jar 
Exception in thread "main" java.io.FileNotFoundException: -A:depstar (No such file or directory)
        at java.base/java.io.FileInputStream.open0(Native Method)
        at java.base/java.io.FileInputStream.open(FileInputStream.java:196)
        at java.base/java.io.FileInputStream.<init>(FileInputStream.java:139)
        at java.base/java.io.FileInputStream.<init>(FileInputStream.java:94)
        at clojure.lang.Compiler.loadFile(Compiler.java:7570)
        at clojure.main$load_script.invokeStatic(main.clj:452)
        at clojure.main$script_opt.invokeStatic(main.clj:512)
        at clojure.main$script_opt.invoke(main.clj:507)
        at clojure.main$main.invokeStatic(main.clj:598)
        at clojure.main$main.doInvoke(main.clj:561)
        at clojure.lang.RestFn.applyTo(RestFn.java:137)
        at clojure.lang.Var.applyTo(Var.java:705)
        at clojure.main.main(main.java:37)

johnjelinek05:06:55

# tree
.
├── deps.edn
├── src
│   └── core.clj
└── target
    └── clj.jar

johnjelinek05:06:16

core.clj:

(ns core
  (:gen-class))

(defn -main [& args]
  (println "Hello, World!"))

johnjelinek05:06:36

so, that's the whole repo -- but I still get that error ☝️

seancorfield05:06:48

What is that clojure command? What does which clojure produce?

johnjelinek05:06:12

# clojure -h
Usage: clojure [dep-opt*] [init-opt*] [main-opt] [arg*]
       clj     [dep-opt*] [init-opt*] [main-opt] [arg*]

The clojure script is a runner for Clojure. clj is a wrapper
for interactive repl use. These scripts ultimately construct and
invoke a command-line of the form:

java [java-opt*] -cp classpath clojure.main [init-opt*] [main-opt] [arg*]

The dep-opts are used to build the java-opts and classpath:
 -Jopt      Pass opt through in java_opts, ex: -J-Xmx512m
 -Ralias... Concatenated resolve-deps aliases, ex: -R:bench:1.9
 -Calias... Concatenated make-classpath aliases, ex: -C:dev
 -Spath     Compute classpath and echo to stdout only
 -Srepro    Use only the local deps.edn (ignore other config files)
 -Sforce    Force recomputation of the classpath (don't use the cache)
 -Spom      Generate (or update an existing) pom.xml with deps and paths
 -Sverbose  Print important path info to console

init-opt:
 -i, --init path     Load a file or resource
 -e, --eval string   Eval exprs in string; print non-nil values

main-opt:
 -m, --main ns-name  Call the -main function from namespace w/args
 -r, --repl          Run a repl
 path                Run a script from a file or resource
 -                   Run a script from standard input
 -h, -?, --help      Print this help message and exit

For more info, see:
 
 

seancorfield05:06:55

That makes no sense then. clojure should not treat -A:depstar as a file.

4
johnjelinek05:06:47

not sure what to do then

seancorfield05:06:52

[email protected]:~/clojure/john$ tree .
.
├── deps.edn
├── src
│   └── core.clj
└── target

2 directories, 2 files
[email protected]:~/clojure/john$ cat deps.edn
{:deps {org.clojure/clojure {:mvn/version "1.10.0"}
        org.clojure/data.json {:mvn/version "0.2.6"}
        com.amazonaws/aws-lambda-java-core {:mvn/version "1.0.0"}
        nrepl {:mvn/version "0.6.0"}}

 :aliases {:depstar
           {:extra-deps
            {seancorfield/depstar {:mvn/version "0.2.1"}}}}}
[email protected]:~/clojure/john$ cat src/core.clj
(ns core
  (:gen-class))

(defn -main [& args]
  (println "Hello, World!"))
[email protected]:~/clojure/john$ clojure -A:depstar -m hf.depstar.uberjar target/clj.jar
Building uber jar: target/clj.jar
[email protected]:~/clojure/john$ java -cp target/clj.jar clojure.main -m core
Hello, World!

lread11:06:48

FYI Here’s tonsky’s recent blog entry on why he created uberdeps https://tonsky.me/blog/uberdeps/

johnjelinek13:06:29

@U04V70XH6: works now -- looks like I was running clj 1.9.0 instead of 1.10.0

johnjelinek13:06:41

even though 1.10.0 was in deps.edn

seancorfield15:06:33

@UE21H2HHD that's an interesting read, thanks! My depstar ended up fixing several bugs in the original -- and also no longer copies files to a temp tree, so it's much faster now (that change fixed a Windows compatibility issue as well as removing a security vulnerability that both exist in the original).

seancorfield15:06:32

Tonsky's issue of using the default classpath -- that's by design since you can control what's on the classpath through aliases. His solution doesn't work at all for the deps.edn setup we use at work -- several packagers fail that test.

seancorfield15:06:56

@U0FEHF1RS glad to hear you got things working!

👍 4
lread15:06:59

@U04V70XH6 yeah, I remember following that Windows bug here on Slack. Trippy (in the LSD sense).

johnjelinek01:06:27

@U04V70XH6: can you clarify the differences with the classpath design? I'm trying out uberdeps now and I'm getting the classpaths I'm expecting with the alias

seancorfield02:06:10

@U0FEHF1RS According to the docs uberdeps reads your deps.edn which means that if you use :override-deps in your base deps.edn file, it won't work -- which is how we have things set up at work. Each project deps.edn has {} for each dependency's version. You either have to reproduce the entire tools.deps path to make that work, or you should rely on the classpath (which is what depstar does).

seancorfield02:06:50

deps.edn tooling should work from the classpath, not trying to read and parse the system, user, and project EDN files -- that was the path I went down with boot-tools-deps and I quickly realized it was a big ol' quagmire and not the right way to do things.

seancorfield02:06:47

I know why people go down that path (having gone down that path myself!) so I totally get whyTonsky didn't like depstar and wrote his own -- but it's a mistake 😐

seancorfield02:06:26

It works in all the simple cases. It seems like the "right thing" to do. But it doesn't work in the real world, once you get into more complicated projects.

seancorfield02:06:00

The classpath is the System of Record. Trying to replicate the way clj works in every program is a fool's errand 🙂

👍 4
seancorfield03:06:18

I'll be interested to chat to him about cljs builds since I don't do any of that -- I suspect even the cljs stuff can be done with appropriate aliases to create the "right" classpath (with the .js, without the .cljs) for depstar but I haven't tried that. I do like the idea of a verbose mode for displaying everything that ends up in the JAR 🙂 I should add that feature to depstar!

👍 4
Ahmed Hassan11:06:04

@U04V70XH6 Bear in mind: depstar does no AOT and adds no manifest -- so whether it's suitable or not will depend on your expectations. How does both of these things effect project or final result? I mean how do I know if they are suitable or not?

seancorfield20:06:20

@UCMNZLJ93 Are you asking what a manifest is and why AOT might be important?

👍 4
seancorfield20:06:27

The manifest is a file a JAR that can specify the main (compiled) class, the entry point, so you can do java -jar path/to/the.jar and it'll know what to run. If you don't have a manifest, you can't use java -jar, you have to use java -cp path/to/the.jar clojure.main -m my.entry.ns -- clojure.main is the name of the main class to start, and it (Clojure) takes the -m option and it requires that namespace and calls -main in it.

👍 4
seancorfield20:06:35

The default new project created by Leiningen has AOT enabled and compiles the "main" namespace, and when you build an uberjar, Leiningen puts a manifest in, specifying that namespace (which is compiled to a class).

👍 4
seancorfield20:06:48

But, as you can see above, you don't need to use AOT, nor a manifest. The only benefit of AOT is faster startup of your application.

👍 4
Ahmed Hassan03:06:08

@U04V70XH6 Thanks, that is the answer I was looking for. I'm using clj and your awesome library depstar for my project.

papachan11:06:00

When i run lein fig:test i have this:

Figwheel Server: Resource not found
Keep on figwheelin' yep

papachan11:06:15

it ran the tests correctly behind

papachan11:06:41

not sure about how useful is this message

RafaMedina13:06:44

No really sure but, maybe, because it doesn't start the server, you're running a test so I think there's no reason for launch it

papachan14:06:41

@UK3MP6QKZ ah so. so its for CI purpose right?

RafaMedina17:06:52

I'm no sure about it, certainly, I only have 3 or 4 weeks learning clojure for hobby, C# and CLJ aren't the same at all hahaha

RafaMedina17:06:03

I started with luminus template and then I found "better" ways, ex. figwheel-main so I decided migrated it and I'm here now hahaha

Stefan14:06:23

What’s the “proper” way to do string interpolation in Clojure? I found core.incubator but that seems old; is that still the way to do it? https://github.com/clojure/core.incubator

alexmiller14:06:28

is format sufficient?

alexmiller14:06:57

(or cl-format if you want an insane set of options)

Stefan14:06:32

Yes that works as well. Is that how people tend to do it? I kind’o like having the more descriptive ~{name} in the string instead of the %... things.

alexmiller14:06:18

I usually use format personally as that's built-in

alexmiller14:06:38

core.incubator is no longer an active project

alexmiller14:06:59

which doesn't mean that stuff doesn't work there, but ...

alexmiller14:06:44

I don't know if the other utility libs out there have other options (medley, useful, tupelo, etc)

Stefan14:06:40

Right, I’ll dig around a bit, thanks Alex!

eskemojoe00717:06:22

I'm using luminus and buddy to attempt to create user authentication. Going through the buddy-auth tutorials, I was trying to combine the signed jwt with a session like authentication. I store the jwt as a cookie. Then have wrap-user-cookie function that takes the cookie and puts it in the request header. That function looks like:

(defn wrap-user-cookie
  "Middleware to convert session cookie token value to authentication header."
  [handler]
  (fn [{:keys [cookies] :as req}]
    (if-let [token (get-in cookies ["session" :value])]
      (-> req
          (assoc-in [:headers "authorization"] (str "Token " token))
          handler)
      (handler req))))
Is this recommended way of doing things?

didibus18:06:37

I keep thinking it would be sweet to extend cl-format to support maps as inputs

didibus18:06:01

It's the only thing "ancient" about it versus other string interpolation functions

didibus18:06:46

Mostly because all my coworkers hate me when I use it and not go for freemarker or something like that

Suni Masuno19:06:34

Is there a 'between' kinda thing based on compare or should I make my own?

noisesmith19:06:58

does (let [order [x y z]] (= order (sort order))) count?

noisesmith19:06:17

it's not built in per se, but it's compact because it leverages built in behaviors

Suni Masuno19:06:09

I'm a bit performance constrained and sort sets off my speed flags, plus it's string comp, but it's pretty code.

noisesmith19:06:12

actually! that's just sorted? - so the answer is yes, and it's called sorted?

Suni Masuno19:06:25

⁉️ love it!

dpsutton19:06:26

i looked at sorted and its not what i thought it was

Suni Masuno19:06:51

it sounded so close

dpsutton19:06:54

"Returns true if coll implements Sorted"

noisesmith19:06:54

@UKDFMDWDD sorr, that's not what sorted? actually does

💔 4
dpsutton19:06:06

i did exactly that two days ago is why i knew this one

Suni Masuno19:06:28

< and > do what I want for nums, just not strings.

noisesmith19:06:45

short-circuiting version

(ins)user=> (defn is-sorted? [& args] (every? (partial apply (comp (complement pos?) compare)) (partition 2 1 args)))
#'user/is-sorted?
(cmd)user=> (is-sorted? 1 2 3)
true
(ins)user=> (is-sorted?  2 1 3)
false

noisesmith19:06:22

it compares every sequential pair in the arglist, returning false if any pair are in the wrong order (equality side by side is allowed)

Suni Masuno19:06:16

That is A) exactly what I need and B) really neat to read

noisesmith19:06:30

haha, glad I could help

noisesmith19:06:59

tried to stuff as many useful idioms as I could in there, this being #beginners and all

Suni Masuno20:06:57

(->
       layer                                                                                            
       :asdf
       (subs 5 10)
       #(is-sorted? "abc" % "aef"))
throws error
java.lang.ClassCastException: clojure.lang.Symbol cannot be cast to clojure.lang.IPersistentVector, compiling:(com/MYSTUFF

Suni Masuno20:06:03

Did I do a real dumb?

noisesmith20:06:45

user=> '#(foo)
(fn* [] (foo))
user=> 

noisesmith20:06:54

see how it expands to an fn form?

noisesmith20:06:54

user=> (macroexpand '(-> x (fn [y] (bar y))))
(fn* x ([y] (bar y)))

Suni Masuno20:06:02

Which is not what I want here, I want the function it would expand to?

Suni Masuno20:06:20

So the -> is gonna jamb stuff into the function def all funny like?

noisesmith20:06:26

the function doesn't get called with x as an argument, the form is updated to insert the symbol

noisesmith20:06:48

-> / ->> are not function composition tools, they are form rewriting tools

Suni Masuno20:06:10

My ramda history bites me again.

noisesmith20:06:16

one option here is to change (-> x #(foo)) to (-> x (#(foo)))

noisesmith20:06:35

another option is to bind the function (via let or using defn) and use the function name

Suni Masuno20:06:17

Which is probably more technically correct. Or I could switch to as->

noisesmith20:06:32

right, all these options work

noisesmith20:06:13

just remember that the arrow macros don't do function composition, they do form rewrites, so you need to consider what the form that ends up being compiled would look like sometimes

Suni Masuno20:06:45

I should use macroexpand more in the repl, shouldn't I?

noisesmith20:06:04

I find it helps as a thing to try when results are unexpected sometimes

noisesmith20:06:24

user=> (->> (+ x y) (let [x 31 y 11]))
42
- my favorite example showing that an arrow macro is rewriting code, not composing functions

Suni Masuno20:06:30

Ok yeah, that is a bit new thinks to think for me

noisesmith20:06:16

don't actually use ->> like that - it's just an example to prove that it doesn't do what it intuitively seems like at first

Suni Masuno20:06:36

Yeah, a pit not to twist your ankle in, so to speak.

Ahmed Hassan10:06:48

@U051SS2EU So, comp is used for functional composition. Right?

dangercoder19:06:18

If you were to build a new web-app today, would you use leiningen or deps.edn? I had some success with deps.edn today in a small app.

dangercoder19:06:41

Created an uberjar and smacked into a docker container.

donaldball19:06:32

I’d use deps unless I needed a lein capability or plugin

ghadi19:06:08

what did you use for the uberjar @UBN9SNVB4?

dangercoder19:06:51

I used sean corefields fork of depstar. https://github.com/seancorfield/depstar

drone20:06:26

I don’t understand why deps.edn is a thing separate from lein. lein already handles dependency management, building, and distributing. for those that prefer deps.edn/clj, what do you use it for? I’m puzzled why anyone would prefer to manually cobble together random tools to replicate lein. but maybe deps.edn/clj users don’t have the same workflow?

drone20:06:20

it just describes what it does, which are all things that lein already does (and then some, like uberjars)

donaldball20:06:45

One point: lein is a build tool, deps is an invocation tool

donaldball20:06:17

Deps allows code to depend on things other than maven artifacts, e.g. clj files in git repos

drone20:06:07

it seems like you’d want your dependencies and other project info described in a single place (maybe call it project.clj? 😉) and then use it for both building and invocation, which lein does

donaldball20:06:24

But lein is a great tool that’s long proved its worth! I don’t think the authors of deps would advocate switching away from it just because

donaldball20:06:05

project.clj has to be macro-expanded in order to be evaluated, while deps is pure data

drone20:06:48

it’s just frustrating when cognitect seems to intentionally splinter the community in order to maintain control of the ecosystem. I was away for a few years when deps came to the scene, so maybe it was introduced in such a way that made it clear it wasn’t meant to replace lein. but the fact that we’re having this conversation now seems like that wasn’t effective, if it ever happened

seancorfield21:06:57

When we started with Clojure at work back in 2011, lein was the only option. We found it very limiting over time and switched to boot in 2015. Last year we wanted to streamline our dev/test pipeline and switched to clj/`deps.edn` and we've been very happy with that.

seancorfield21:06:50

For simple stuff, where you don't care about startup time for general tasks, lein is fine. boot is much more programmable/extensible -- but it also has downsides (complexity, the fileset abstraction can introduce performance issues and have weird effects on some dev/test workflows, edge case async bugs in pod handling).

seancorfield21:06:58

For where we are, with our 80k lines of Clojure at work, clj/`deps.edn` is a better fit for a large monorepo with two dozen subprojects, and we have much more control over versions, tooling, etc.

seancorfield21:06:45

clj/`deps.edn` don't replace lein or boot as a whole, but there's a lot of stuff you can do easily with them.

seancorfield21:06:27

If you have Java code as well as Clojure, clj/`deps.edn` won't fit, for example (although you can do stuff to make it work).

seancorfield21:06:58

Bottom line: if you're happy with lein, keep using it; if you're happy with boot, keep using it; if you're just starting out, try clj/`deps.edn` and see whether you need anything beyond that.

andy.fingerhut22:06:01

I do not work for Cognitect, nor am I any kind of spokesperson or know all that they know, but they have always made it pretty clear that deps isn't meant to do everything lein can do. One of the reasons it was introduced was to depend on Clojure source code versions in Git repos directly, without having to create a JAR or other artifact from it. It seems to have been at least partially motivated by a use case within Datomic Cloud for deploying apps to AWS clusters.

andy.fingerhut22:06:52

The fact that the question was asked is because some people have found that they preferred tooling like deps (plus some other tools such as depstar) over lein, given the choice, but not everyone does.

andy.fingerhut22:06:19

The talk "Spec-ulation" by Rich Hickey provides more motivation/background/"why" than I can do justice to in a short note: https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/Spec_ulation.md

drone22:06:20

thanks for the reference

andy.fingerhut22:06:54

Also check out some public talks about Datomic Cloud if you are interested in how it arises in that context. This is the one that I saw: https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/DatomicIons.md

drone22:06:55

I think deps (and spec) make me uncomfortable because they are “core” projects that displaced community projects that were de facto standards. certainly, lein isn’t perfect, but was there ever an attempt to work with technomancy and others to provide a simpler, deps-like core or package lein with Clojure? I’m also not opposed to competition, we don’t all have to use the same tooling and different approaches result in general improvement. but Cognitect has the unfair advantage that their tools are shipped with Clojure. people may end up preferring deps simply because they don’t need to install it, not because it is actually better

andy.fingerhut23:06:03

But you do need to install deps. It doesn't come with the base Clojure JAR or anything like that.

andy.fingerhut23:06:04

I don't have a horse in this race -- if people don't like it that Cognitect gives away free software that competes with other free software, I don't know what to say that will make you comfortable with that.

andy.fingerhut23:06:06

Making your own thing that you have control over is often an order of magnitude easier than convincing someone else to change what they are doing, especially in ways that are not compatible with what they are doing already

drone23:06:03

perhaps I’m mistaken, but it looks like clj and clojure commands are included during install according to: https://clojure.org/guides/getting_started

andy.fingerhut23:06:37

Sure, but you can install Leiningen and avoid that entirely, if you don't want it.

andy.fingerhut23:06:27

It is on the getting started page because if it were not, the fact that the Clojure JAR by itself is no longer enough would make the example "java -classpath ..." command line that used to be there so long, that it would be off-putting.

drone23:06:31

ok, so then clj/deps is packaged with Clojure

andy.fingerhut23:06:34

You can install Leiningen, create a new project, and it will get all of the Clojure JARs needed for your project just fine, even if you never ever touch clj/clojure/deps

andy.fingerhut23:06:10

So at least one way of thinking is that clj/clojure/deps is packaged with Clojure, just as much as Leiningen is packaged with Clojure.

drone23:06:36

right, and nowhere is that mentioned in the getting started page. there is only a brief mention that lein and boot exist, but nothing to indicate that they can be used to sidestep the “official” installation scripts which include clj

drone23:06:09

these finer points aren’t really the issue. it’s the approach to develop something internally to replace something already built by the community without attempting to work with the community

andy.fingerhut23:06:17

They have always said that clj/clojure does not replace Leiningen, nor is it intended to. They don't want clj/clojure to do everything Leiningen does, repeatedly. Fairly often on the #tools-deps channel, whenever people ask "Can clj/clojure do this thing that Leiningen does?" the answer is often "out of scope"

andy.fingerhut23:06:22

I am not trying to argue into making you comfortable with any of this. But the fact is that Cognitect wanted something different, paid to build it, and then give it away. They give you prominent instructions on how to install and use it. They mention Leiningen, perhaps not as prominently, on a Clojure getting started page. If that is the stuff of world domination, I'm all for it.

andy.fingerhut23:06:48

They aren't exactly dominating the world yet 🙂

drone23:06:03

hah, that’s not what I’m claiming or meaning to imply

drone23:06:23

I just think they’re uninterested in working with the community, and the Clojure community is too small for that to be sustainable

drone23:06:30

and it has already caused many significant contributors to leave

seancorfield23:06:03

@UCF779XFS that Getting Started page you linked to says

Build tools

Build tools provide a range of capabilities for building, running, and deploying Clojure libraries and applications. The two most popular Clojure build tools are Leiningen and Boot.
(linked to the lein/`boot` websites).

drone23:06:13

thanks, I mentioned that

drone23:06:29

nothing in there implies you don’t need to have already run the installers at the top of the page

seancorfield23:06:43

I think you're being extremely unfair to the hard-working folks at Cognitect who provide all this stuff for free to us.

andy.fingerhut23:06:48

My impression is that when they build stuff, they are quite particular about how they want to do so, and doing design work on such things with a community is really, really tough, and they would rather not go to so much trouble. For a related take from the creator of Elm on that, here is yet another talk I like 🙂 https://github.com/matthiasn/talk-transcripts/blob/master/Czaplicki_Evan/TheHardPartsOfOpenSource.md

drone23:06:49

you and I know that, but we’re not the target audience for that page

drone23:06:56

I can be appreciative of the work Cognitect has done and still criticize their interactions with the community

seancorfield23:06:55

Most books and tutorials that beginners would use direct them to Leiningen. Some tutorials would direct beginners to Boot. Some direct them to clj/`deps.edn`.

seancorfield23:06:37

Such criticisms are not helpful nor particularly constructive -- and really don't help the community either. Saying they're "shitty" is insulting and information-free (and, frankly, isn't appropriate language for a professional international community like this -- with my Admin Team hat on).

drone23:06:42

anyway, I appreciate the complementary thoughts from you both. I didn’t mean to ramble on so

andy.fingerhut23:06:12

Sure, you can be both, and many people are. The criticisms aren't necessarily going to generate any more light than heat, I would guess, but sure, everyone is free to do so.

drone23:06:35

I realize speaking up doesn’t mean anything changes, but I think occasionally voicing opinions is reasonable, no?

drone23:06:00

how would you or Cognitect know anyone still using Clojure and contributing to the ecoystem is bothered by it otherwise?

seancorfield23:06:37

You can certainly express opinions and criticism without generalizations like "they're shitty"...

drone23:06:26

and as far as language goes, I rarely use colorful language and don’t believe I ever have here before.

seancorfield23:06:46

If you have suggestions for improving the http://clojure.org website, you could create an issue against the GitHub repo for the site, or submit a pull request with updated wording. They might reject it and they may explain why, but that would be constructive (and polite).

drone23:06:49

yes, but not a generalization. an adjective for the behavior I had already described in messages before it

drone23:06:27

it’s ironic, because the sort of behavior I was describing here is exactly the sort of thing that tends to make people contribute less

andy.fingerhut23:06:54

You ask: "how would you or Cognitect know anyone still using Clojure and contributing to the ecosystem is bothered by it otherwise?" Having been keeping half an ear on the community for years now, that seems like a probability 0 situation.

drone23:06:43

@U04V70XH6 if you don’t like that colorful adjective, I apologize. I didn’t intend to offend anyone. if it’s against the the community etiquette, please show me where. you claimed I was making a generalization, but you either misunderstood or missed the previous messages where I attempt to describe the kind of community interaction that has been bothering me. I do not appreciate what seems like an attempt to shame or threaten me by posting the etiquette link without referencing anything there and mentioning your “admin hat” simply because you disagree with me

andy.fingerhut23:06:10

It is a pretty darned short etiquette page, and does explicitly give advice on discussions of disagreements of how something has been made, or should be made.

drone00:06:16

I’m not sure I agree that statement, or indeed the entire page, applies well to conversations about community. deps.edn was only the example, the topic was collaboration between Cognitect and established community projects. :man-shrugging:

donaldball00:06:13

A bit tangential: https://www.therepl.net/episodes/23/ has a very thoughtful conversation with Zach Tellman on clojure community interaction that is nuanced, insightful, and respectful, at least to my ears.

👍 4
seancorfield00:06:41

@UCF779XFS The Code of Conduct for this Slack https://github.com/clojurians/community-development/blob/master/Code-of-Conduct.md -- specifically "Inappropriate Language". The TL;DR is to think of this forum as a professional, international business group where your co-workers, boss(es), clients, etc could all be present...

drone00:06:47

thank you. I’ll edit my message accordingly

seancorfield00:06:44

NP. It's in a thread so only a handful will see it. It was intended to be more of a reminder of "tone" when offering criticisms and that we're all trying to be professionals here 🙂

seancorfield00:06:22

I don't agree with all of Cognitect's decisions but they're pretty clear about their vision so I can usually understand their decisions. It's the bane of OSS project maintainers' lives globally that some folks will always be unhappy with how you run a project and/or some decisions you make...

seancorfield00:06:24

For example, in creating the next generation of clojure.java.jdbc I thought long and hard about whether it should go in Contrib (as new namespaces in that project) or live externally. Keeping it in Contrib had a number of benefits but I knew that it would also cause several complaints from people who don't like the Contrib development model.

seancorfield00:06:36

In the end, I decided to keep it outside Contrib but that means I'm now maintaining two JDBC libraries with different contribution/development models, making my life harder.

drone00:06:53

yeah, I empathize with that, OSS is hard. programming languages perhaps in particular

lread00:06:55

One of the things I love about the Clojure community and these Slack channels is how friendly, civil and courteous everyone is! You guys are the best!

❤️ 8
seancorfield00:06:57

I've been doing OSS development for about 30 years now, so I've seen a lot of the ugly side of the work...

lread00:06:28

There is a really good No Manifestos podcast with Mia https://www.nomanifestos.com/episodes/3 She chose Clojure because she found the people involved to be (I paraphrase) smart, happy and pleasant.

lread00:06:49

I found that to be quite interesting and impressive.

lread00:06:06

Yeah @U04V70XH6, I only dipped me toe into OSS a few years ago and… well let’s just say I’m happy I found the Clojure community.

drewverlee21:06:44

(let [[t d] (split-with #(< % 12) (range 1e8))] [(count d) (count t)]); #<OutOfMemoryError java.lang.OutOfMemoryError: 
Is it correct to say this happens because, by keeping t around, we end up building that lazy rangeb into a realized list. As opposed to reversing d and t which rule mean that as we counted d we could just discarded that number/ sum it into the return value?

seancorfield22:06:23

@drewverlee count is what is forcing the realization here and that's why you get OoM

seancorfield22:06:25

user=> (let [[t d] (split-with #(< % 12) (range 1e8))] [(first d) (first t)])
[12 0]

seancorfield22:06:44

That doesn't realize all of t or d so it's "safe".

noisesmith22:06:39

I'd think the locals clearing would be smart enough to know count doesn't need to hold the head

noisesmith22:06:02

or maybe I misunderstand what count on a lazy-seq does

seancorfield22:06:22

user=> (let [[t d] (split-with #(< % 12) (range 1e8))] [(bounded-count 1000 d) (bounded-count 1000 t)])
[1000 12]
only realizes the first 1,000 or so which is also safe here.

seancorfield22:06:28

t is only a small sequence -- 12 elements -- d is the long one, and split-with is lazy (because it's a call to take-while and a call to drop-while which are both lazy).

hiredman22:06:06

split-with returns a pair of lazyseqs, each of which has a reference to the original sequence

hiredman22:06:03

d is holding a reference to the head of the range, while you are walking entire range (throwing parts away) and counting it

hiredman22:06:15

sorry, I didn't notice the original code has d and t in the other order when counting, so swap that all around

seancorfield22:06:50

user=> (let [[t d] (split-with #(< % 12) (range 1e8))] [(count t) (count d)])
[12 99999988]
@hiredman is right. Sorry for misleading/missing the point there @drewverlee

drewverlee23:06:29

thanks everyone 🙂.