Fork me on GitHub
#clojure
<
2020-04-10
>
myguidingstar07:04:00

is there a version of pr-str that emit full versions of qualified keywords in maps instead of short forms? Something like {:foo/bar 1} instead of #:foo{:bar 1}

myguidingstar07:04:42

I need to debug some edn and it's impossible to jump to the desired keywords if they're in the short forms

dominicm07:04:25

@myguidingstar there are some print related dynamic variables you can bind.

👍 4
rovanion09:04:21

Does anyone happen to have a good way of getting the path to a file relative to another file?

phronmophobic09:04:50

works wonders

(def foo ( "/var/tmp/foo.txt"))
(def parent (.getParentFile foo))
(def sibling ( parent "bar.txt"))
(def sibling2 ( foo ".." "bar.txt"))
(.getCanonicalPath sibling2) ;; "/private/var/tmp/bar.txt"
(.getCanonicalFile sibling2) ;; #object[.File 0x1d74ad00 "/private/var/tmp/bar.txt"]
(def multiple-levels ( parent "abra" "cadabra" "log.txt"))
(.getAbsolutePath multiple-levels) ;; "/var/tmp/abra/cadabra/log.txt"

rovanion09:04:07

Hmm, I'm not sure I understand phronmophobic. There already exists two files in the filesystem and I want to calculate a path from one of them to the other. But that http://java.nio.fi

p-himik11:04:52

No, it's calling the Java meta method of the clojure.lang.IMeta interface.

✔️ 4
p-himik11:04:27

(. ^C x (m)) in Clojure is the same as x.m() in Java where x is of type C.

✔️ 4
Alex Miller (Clojure team)13:04:07

Also, FYI this is not the official Clojure source, which is at https://github.com/clojure/clojure

👍 4
Spaceman11:04:17

This isn't importing for me:

Spaceman11:04:27

(import com.braintreegateway)

Spaceman11:04:59

Execution error (ClassNotFoundException) at http://java.net.URLClassLoader/findClass (URLClassLoader.java:382). com.braintreegateway

p-himik12:04:13

IIRC Clojure doesn't have a direct analog to import x.*. You will have to provide each class' name.

Spaceman16:04:54

(import '[com.braintreegateway BraintreeGateway])
(import 'com.braintreegateway.BraintreeGateway)
(import [com.braintreegateway BraintreeGateway])
Nothing works

Spaceman16:04:00

(import (com.braintreegateway BraintreeGateway))
Doesn't work

p-himik16:04:44

Works for me. Are you using it by itself or in an ns form? Are you sure you have added the dependency to wherever you add dependencies?

Alex Miller (Clojure team)16:04:06

doesn't work == what happens?

jrychter07:04:37

Ahh, Braintree integration. Much fun lies ahead. You will probably need this subset for a reasonable minimum:

(:import (com.braintreegateway
            BraintreeGateway Environment Result
            ClientTokenRequest CustomerRequest AddressRequest PaymentMethodRequest SubscriptionRequest
            SubscriptionSearchRequest
            Subscription
            Subscription$Status
            WebhookNotification)

bbss13:04:59

Using deps.edn, how can I access resources of a dependency library. Say I have project-a that has {:deps {project-b {:local/root "/my-projects/b"}} :paths ["src", "resources"] } When running project-b in a repl it has access to files in resources of its directory, but when running repl with project-a, project-b will not be able to access its own resources .

Alex Miller (Clojure team)13:04:32

all :paths of dependency local libs should be on your classpath in a

Alex Miller (Clojure team)13:04:17

if you do clj -Spath you should see it in there

bbss13:04:20

@alexmiller Thank you, you're right. I'm not sure what went wrong before, I'm using cider, but now it's there too.

FlavaDave13:04:30

trying to swap! a map into an empty atom but get trailing nils.

user> (def foo (atom {}))
user/foo
user> (swap! foo assoc {:a 1 :b 2})
{{:a 1, :b 2} nil, nil nil}

dpsutton13:04:04

assoc takes keys and values, not a map. you're using it like merge which does take a map

cassiel13:04:05

(swap! foo assoc {:a 1 :b 2}) doesn't compile for me…
…but (swap! foo assoc :a 1 :b 2) does the job.

bbss14:04:30

@dgonsalves22 what you want to do seems more like reset! than swap! e.g. (reset! foo {:a 1 :b 2})

FlavaDave14:04:53

(swap! foo merge {:a 1 :b 2}) and (reset! foo {:a 1 :b 2}) both worked for me. Thanks guys

bbss14:04:01

@dgonsalves22 note that reset! will overwrite, if foo contains something important your merge version will be better.

FlavaDave14:04:49

@bbss I’m glad I have both of those to work with because im still not sure whether I want to be overwriting every time or not.

bbss14:04:57

I mostly use reset! when I don't care about the state being lost anyway (or explicitly want to clear it) often in clojurescript when reloading re-running some parts. swap! is usually what you want I'd say.

cassiel14:04:57

For clearing state I’d go with (swap! A dissoc :k1 :k2 :k3) where possible.

honza14:04:35

Can you recommend articles on how to structure Clojure programs? Both in the small and in the large. My programs always become such a mess.

lukasz15:04:00

In my experience (20+ Clojure apps, small and big-ish) things like Component are incredibly helpful to separate "application" structure from the biz logic

potetm15:04:19

@U08716WQP If you like, you could post a github link to your code, and I can leave some comments when I have time.

lukasz15:04:20

Interestingly enough - I cannot recall many articles about structuring Clojure applications

potetm15:04:42

I don’t know of any great articles on it. The best general advice I could give is: Flatten your logic. Stop creating functions.

💯 4
honza15:04:40

@U07S8JGF7 I’m intrigued. What do you mean by stop creating functions?

potetm15:04:42

At a literal level, I mean “stop using defn😄

potetm15:04:57

That’s obviously exaggerated.

potetm15:04:58

But it’s very important that every name you introduce carries a cost: the cost of remembering what it means. Clojure often allows you to be so concise, that inlining those functions is both more precise and less overhead.

❤️ 8
potetm15:04:09

I’ve found that difficult-to-read clojure programs often suffer from this problem.

potetm15:04:19

(the problem of “too many names”)

❤️ 4
potetm15:04:57

I think it’s a holdover from OOP where “short methods = good.”

honza15:04:31

What does flatten logic mean?

potetm15:04:23

When you stop creating nested function calls, your logic naturally gets pushed to the top level.

potetm15:04:59

So you end up with longer, flatter functions in general.

potetm15:04:47

Again, this is the opposite of a lot of OOP advice. But when you’re dealing primarily with immutable values, longer functions become easier to manage.

potetm15:04:26

Because, for example, you cannot silently tweak state on line 27 that affects a switch on line 124.

potetm15:04:03

very long function

potetm15:04:44

But I heard multiple people complain about this state machine during advent of code, and, for me, it was extremely easy to make changes because all of the logic was bundled together.

potetm15:04:56

Note that I could have broken this up and used multimethods or something. But 1. the op codes are a closed set and 2. that would have required figuring out how parse a bunch of values and pass them into the multimethod.

potetm15:04:24

You can avoid all those problems by cheating: Just putting everything you need in one scope.

noisesmith15:04:40

@U07S8JGF7 I wouldn't have thought to ask this question, butI like appreciate this insight - thanks

thanks 4
noisesmith15:04:28

regarding the organization of clojure code in general, I know of nothing better than Elements Of Clojure https://leanpub.com/elementsofclojure

✔️ 8
honza15:04:31

I think I get it.

potetm15:04:37

Elements of Clojure does a good job of putting words to this. I hesitate to recommend it because it’s so abstract I think it would be hard to understand if you don’t already have a pretty good idea.

jrychter07:04:37

Interesting thread. I am at a stage where I'm re-designing my application for the 5th or 6th time, as I discover new better ways to structure the code. One interesting thing I found is that the split into layers (UI, Application, Business Logic, Infrastructure) doesn't work that well. Specifically, Business Logic (which I call Domain) should not be a "layer" above infrastructure, but something entirely separate, because it is useful both in server-side Clojure and client-side ClojureScript.

jrychter07:04:00

But I'd love to read some books about designing large systems with Clojure. I recently read "Domain-Driven Design" by Eric Evans and found out that I've been practicing Domain-Driven Design all these years. Good ideas in that book, although it gets somewhat mired in the object-oriented tarpit.

potetm12:04:00

Yeah I’ve tried similar splits, and I found them equally unhelpful.

jmckitrick15:04:28

Well, any efforts to sneak Clojure into our Scala shop probably went out the window with this tweet being passed around: https://twitter.com/jamie_allen/status/1248019842877145089

lol 4
rage4 8
potetm15:04:35

Hope you’re not serious. This statement is obviously a bit of a joke.

potetm15:04:40

It’s one person who’s obviously very, very biased talking about his own feelings. No reason to count it as anything more than that.

jmckitrick15:04:34

Is he really joking? If so, it went over my head, lol. And pleasantly so….

potetm15:04:01

No, I don’t think he’s joking. But it’s clearly a joke of a statement 😄

ghadi16:04:42

typing things on the internet incurs no cost to the typer

☝️ 16
Alex Miller (Clojure team)16:04:09

are you saying typing has no tradeoffs? :)

ghadi17:04:58

it's a pretty good deal for the typer :)

jmckitrick16:04:57

True, but that led to an article by a scala ‘heavy’ who I respect, Haoyi, so I didn’t want to dismiss it out of hand.

Michael J Dorian16:04:36

Clojure is getting updates though, which seems like the opposite of "dead", and although I'm not looking at them now I remember the stack overflow stats being favorable. Also lisps don't tend to be mainstream, but that doesn't make them less useful 🤷

ghadi16:04:15

in this channel you won't find support for blind assertions without data

potetm16:04:47

There will never be data that could support such a statement.

potetm16:04:53

It’s ridiculous on its face.

Alex Miller (Clojure team)16:04:00

Reports of Clojure's death are common, and false :)

potetm16:04:03

It’s a statement about a feeling. Nothing more.

potetm16:04:12

(And nothing less.)

dvingo16:04:48

"Doing what's common produces only common results" - paraphrasing Ray Dalio

potetm16:04:58

It’s the exact same as when a gamer claims that some other game is dead. People are playing the game. People are enjoying the game. The statement says more about the speaker. It’s both a feigned superiority and an attempt to reassure themselves that their decisions are The Right Decisions™.

Alex Miller (Clojure team)16:04:53

I know Jamie, and he knows better than this

potetm16:04:43

We all do this. To be absolutely clear: nothing I said was an attempt to say, “never listen to him,” or “he is bad.”

potetm16:04:04

The only reason I can even recognize what he’s doing is because I do it. 🙂

colinkahn16:04:07

When using defspec from test.check is there a way to cleanup after the tests have run? I can wrap my props/for-all in a let for setup, not sure how to cleanup though.

Derek16:04:12

If you use test.chuck’s checking macro (https://github.com/gfredericks/test.chuck#alternate-clojuretest-integration) you should be able to use normal clojure.test fixtures

colinkahn16:04:01

Nice, yeah I was thinking of using a fixture but good to know that it doesn’t work out of the box

gfredericks13:04:52

I'd be surprised if it doesn't work out of the box

noisesmith16:04:26

what is even being mreasured in this graph?

p-himik16:04:04

"For this survey, we teamed up with conferences and communities across the JVM ecosystem" - an incredibly biased data source.

emccue16:04:44

Isn't clojure more popular than scala?

emccue16:04:01

by this cherry picked metric

Eric Scott18:04:34

I'm a bit confused here. Given these definitions:

(defrecord langStr [s tag]
  Object
  (toString [_] s))

(defmethod print-method langStr
  [literal ^.Writer w]
  (.write w (str "#langStr \"" literal "@" (:tag literal) "\"")))
This works as expected:
> (def gaol (langStr. "gaol" "en-uk"))
gaol
> (prn gaol)
#langStr "gaol@en-uk"
>
But in the REPL:
> gaol
{:s "gaol", :tag "en-uk"}
> 
Two questions: - What function/method is serving as the 'P' as REPL? - How can I get it to be equivalent to prn?

noisesmith18:04:45

the repl doesn't use to-string, it uses the system underlying prn, which is based on a multimethod

noisesmith18:04:20

iirc `*print-method*` but it shouldn't be hard to verify

noisesmith18:04:25

yeah, print-method

(ins)user=> (source pr)
(defn pr
  "Prints the object(s) to the output stream that is the current value
  of *out*.  Prints the object(s), separated by spaces if there is
  more than one.  By default, pr and prn print in a way that objects
  can be read by the reader"
  {:dynamic true
   :added "1.0"}
  ([] nil)
  ([x]
     (pr-on x *out*))
  ([x & more]
   (pr x)
   (. *out* (append \space))
   (if-let [nmore (next more)]
     (recur (first more) nmore)
     (apply pr more))))
nil
(ins)user=> (source pr-on)
Source not found
nil
(ins)user=> (source clojure.core/pr-on)
(defn pr-on
  {:private true
   :static true}
  [x w]
  (if *print-dup*
    (print-dup x w)
    (print-method x w))
  nil)
nil

Alex Miller (Clojure team)18:04:11

usually you want to override both print-method and print-dup

Eric Scott18:04:30

So print-dup is being called when I use the REPL rather than the print-method I've defined?

noisesmith18:04:45

you didn't define print-method, you defined toString I didn't read carefully

Eric Scott18:04:41

OK, so here's the complete definition:

Eric Scott18:04:48

(defrecord langStr [s tag]
  Object
  (toString [_] s))

(defmethod print-method langStr
  [literal ^.Writer w]
  (.write w (str "#langStr \"" literal "@" (:tag literal) "\"")))


(defmethod print-dup langStr [o w]
  (print-method o w))

(defn read-langStr [form]
  (let [langstring-re #"^(.*)@([-a-zA-Z]+)" 
        m (re-matches langstring-re form)
        ]
    (when (not= (count m) 3)
      (throw (ex-info "Bad langString fomat"
                      {:type ::BadLangstringFormat
                       :regex langstring-re
                       :form form})))
    (let [[_ s lang] m]
      (langStr. s lang))))

Eric Scott18:04:45

langStr defined in my data_readers.clj

Eric Scott18:04:06

But when I evaluate it I still get the original reader defined for a defrecord:

Eric Scott18:04:12

> #langStr "jail@en-us"

Eric Scott18:04:22

{:s "jail", :tag "en-us"}

Eric Scott18:04:17

@alexmiller is there something else I need to do to override the defrecord print-method?

Alex Miller (Clojure team)18:04:42

You’re using langStr there - might want to check that’s evaluating to the full class?

Eric Scott18:04:01

Well my data_reader has this clause:

Eric Scott18:04:04

langStr ont-app.sparql-endpoint.core/read-langStr

Alex Miller (Clojure team)19:04:56

Data readers switch on the tag symbol

Alex Miller (Clojure team)19:04:06

Printing switches on Java class

Eric Scott19:04:59

def jail #langStr "jail@en-us")
#'ont-app.sparql-endpoint.core/jail
(class jail)
ont_app.sparql_endpoint.core.langStr

Eric Scott19:04:52

Also ont_app.sparql_endpoint.core.langStr shows up in the output from (methods print-method)

Eric Scott19:04:19

I tried this:

Eric Scott19:04:24

(prefer-method print-method langStr clojure.lang.IRecord)
Execution error (IllegalStateException) at ont-app.sparql-endpoint.core/eval13294 (form-init4216778243462303189.clj:139).
Preference conflict in multimethod 'print-method': interface clojure.lang.IRecord is already preferred to class ont_app.sparql_endpoint.core.langStr

Eric Scott19:04:03

So there's just no way to get the REPL to print a defrecord except as {<k> <v>, ...} ?

Eric Scott19:04:21

Ah. Sure 'nuff deftype fixes the problem

Eric Scott19:04:25

Thanks guys!

Alex Miller (Clojure team)21:04:03

you can totally do this with defrecord - you may need to add a prefer-method though

Alex Miller (Clojure team)21:04:39

$ clj
Clojure 1.10.1
user=> (defrecord Rec [a])
user.Rec
user=> (prn (->Rec 1))
#user.Rec{:a 1}
nil
user=> (->Rec 1)
#user.Rec{:a 1}
user=> (defmethod print-method Rec [^Rec r ^.Writer w] (.write w ^String (str (:a r))))
#object[clojure.lang.MultiFn 0x5b022357 "clojure.lang.MultiFn@5b022357"]
user=> (defmethod print-dup Rec [^Rec r ^.Writer w] (print-method r w))
#object[clojure.lang.MultiFn 0x6ceb7b5e "clojure.lang.MultiFn@6ceb7b5e"]
user=> (->Rec 1)
1
user=> (prn (->Rec 1))
1
nil

Shako Farhad18:04:56

Anyone here who uses intellij with shadow-cljs? It seems that my cursive plugin doesn't work with shadow-cljs. Anyone know how to fix this?

Shako Farhad19:04:21

so just moving dependancies to deps.edn will make cursive work again?

dmarjenburgh19:04:16

The problem I had was that cursive doesn't recognize shadow-cljs.edn as a project configuration file. It does recognize deps.edn so if that's the problem it might fix it. Otherwise I suggest bringing up the question #cursive

Shako Farhad19:04:36

Ah I see! Ok I will check it out! Thanks!

Shako Farhad20:04:38

Unfortunatly it doesn't work. 😞 I am just getting this: https://gist.github.com/ShakoFarhad/4b73a62b2ed1dfe054473faa12d6e67e

lilactown22:04:27

it sounds like you have an error in your deps

lilactown22:04:40

it’s hard to help you troubleshoot without knowing what you’re doing

cfleming22:04:34

@U0121V7FXG8 The Shadow documentation has a section on how to work around this: https://shadow-cljs.github.io/docs/UsersGuide.html#_cursive

Shako Farhad23:04:57

I am getting this problem: https://gist.github.com/ShakoFarhad/39ab6314891431f540e2ccbac2c351d7 It talks about the http://java.erxe not being found. It is weird because it is at the given path. Nothing is missing. :s

Shako Farhad23:04:00

This is how my project.clj file looks

Shako Farhad23:04:50

And this is the shadow-cljs.edn file

lilactown23:04:57

do you have a package.json with the same version of shadow-cljs installed into node_modules?

Shako Farhad23:04:21

I will just keep using shadow-cljs and keep updating the project.clj file whenever I make a dependancy change in shadow-cljs.edn. That way cursive will keep working and I can still use shadow-cljs for building. It is perhaps a bit inefficient to keep two files up to date with one another, but at least everything works now 😄

Shako Farhad01:04:10

I fixed it! Apparently I needed a specific version of jdk. I had 13.0.2 but it complained about not finding 13.0.1. So I installed 13.0.1 and it works now. 😮

isak22:04:33

Is it possible to create a browser extension to that makes the browser devtools understand transit encoded edn?

potetm00:04:45

“transit-encoded edn?” You mean you serialize to EDN and then serialize to transit? Surely not.

potetm00:04:04

https://github.com/binaryage/cljs-devtools does this for EDN. I’m assuming you can use the same approach for transit.

isak14:04:15

Oh right I use that one, but it doesn't affect the network panel, so normal transit requests/responses are hard to read. Not sure if that is possible in a browser extension.

potetm18:04:37

No, it doesn’t support transit. But I’m pretty sure it could, if you made it do so 😄

isak18:04:29

Ok, I'll look into it. I'm happy to do it if possible.

lilactown21:04:31

it isn’t possible to get the network panel to show transit decoded

lilactown21:04:44

note that it might vary depending on which version of transit your app is using

lilactown21:04:00

the best option I’ve found is to copy the response and decode it using jet

isak21:04:12

ah I see, thanks @U4YGF4NGM