Fork me on GitHub
#clojure
<
2019-07-30
>
danielstockton12:07:21

Does anyone know why not= was chosen over !=? My brain just decided to ask itself.

cmack13:07:36

I can’t answer with authority, but I would wager a combination of history and consistency. In most lisps that I’ve experienced, (not ...) is the negation function. Also in most Lisps “!” never carried the meaning of “not” as it does in C language descendants.

danielstockton13:07:40

I assumed so @cmack, it's just like removing a paren in (not (... There was a lot of new syntax in Clojure however, compared to other lisps.

henrik14:07:09

What’s the tidiest inverse of (vec some-map)?

dpsutton14:07:54

Turning a vector of key value pairs into a map?

henrik14:07:08

No, a vector of vectors of pairs. Edit: sorry, yes, I misunderstood.

Andrew14:07:25

(into {} %)

👍 8
Andrew14:07:52

where % is the result of (vec some-map)

lilactown16:07:31

should IPrintWithWriter/-pr-writer be expected to print valid EDN?

Alex Miller (Clojure team)16:07:43

there is no "edn safe" printing path

lilactown16:07:39

I voted 🙂

Alex Miller (Clojure team)16:07:13

this is a common problem and it doesn't have a good general solution, really it's up to you to ensure you have data that will print to valid readable edn right now

Alex Miller (Clojure team)16:07:55

but it seems like something that should have a better answer

lilactown16:07:40

yeah. I’m working on a REBL-like app for CLJS, but it requires sending/receiving the datafied form over a wire. not having a reliable way of ensuring something that a user application tap>s out can be read on the other side is rough

lilactown16:07:35

specifically, I had a hugely nested map with a Reagent RAtom like:

{:current-state #<Atom: 111, ...>}
which took a bit of time to troubleshoot. I couldn’t figure out how to get the reader to tell me what form it couldn’t read, either

Alex Miller (Clojure team)17:07:24

well you shouldn't have that problem in Clojure, as it will fall back to an #object tagged literal (which is still readable!)

Alex Miller (Clojure team)17:07:35

so that might be something to change in cljs

lilactown17:07:55

it will fallback in the reading or the writing?

lilactown18:07:52

I think the issue is that the implementation of -pr-writer for the reagent.ratom/RAtom type writes out #<Atom: something>, which isn't readable

lilactown18:07:33

I'm currently calling pr-str on the whole data structure to as a way to "write" the EDN

lilactown18:07:44

it sounds like you're saying that the behavior on the JVM would ignore this implementation and print #object[reagent.ratom/RAtom], or I'm using the wrong tool (`pr-str`) to do this?

Alex Miller (Clojure team)18:07:48

more specifically, print-method is defined on Object to print in a readable way

Alex Miller (Clojure team)18:07:44

Clojure used to print objects without a printer in an unreadable way but we changed that in Clojure 1.6 (iirc)

Alex Miller (Clojure team)18:07:01

and cljs could do the same

lilactown18:07:22

but if the object has their own implementation of printing, that doesn't apply, right?

Alex Miller (Clojure team)18:07:35

this is the fallthrough if no print is defined

Alex Miller (Clojure team)18:07:39

user=> *ns*
#object[clojure.lang.Namespace 0x51b01960 "user"]

lilactown18:07:52

right, I see the same behavior in CLJS

Alex Miller (Clojure team)18:07:13

there is no reader defined for #object, but you can install a default-data-reader-fn to use the built-in tagged-literal function as a reader

lilactown18:07:46

so for my case, the issue is the way Reagent implements printing on their custom atom type. but also that there's nothing that really says that printing should be valid EDN anyway

Alex Miller (Clojure team)18:07:53

well #<Atom: 111, ...> above is not readable

Alex Miller (Clojure team)18:07:20

well I'd say it's generally bad to print in a way you know won't be printable

Alex Miller (Clojure team)18:07:32

so whoever is doing that shouldn't imo :)

lilactown18:07:27

great, I'll open an issue with Reagent then

Alex Miller (Clojure team)18:07:00

or there should be something additional being done to install a print-method

Alex Miller (Clojure team)18:07:06

not sure which is appropriate here

Alex Miller (Clojure team)18:07:23

but atoms are a good case where it's probably possible to print something, but is that useful to you?

4
lilactown19:07:32

not sure I understand the question tbh

lilactown19:07:58

I'm not sure how useful printing just an atom is for me, except that it happens to be a part of a larger structure that I want to inspect

Alex Miller (Clojure team)19:07:25

if you have a stateful atom and print it to a string, does it make any sense to reconstitute it back into an object later when other objects can no longer refer to it

Alex Miller (Clojure team)19:07:36

if you're just inspecting, then you probably don't care

lilactown19:07:15

atm, I just want to make it passably readable. it doesn't need to hydrate it into an actual atom; just using the default tagged-literal would be enough for me

lilactown19:07:09

hydrating atoms could be useful for saving to disk and then resuming application state on startup, but not something I'm worried about atm

csd19:07:06

Would a multimethod be faster than the following:

(defn execute-on-type [type x y z]
  (case type
    :foo (execute-foo x y z)
    :bar (execute-bar x y z)))

csd19:07:26

not clear on what sort of magic happens under the hood

noisesmith19:07:42

it would be slower, but it's extensible to new keys without changing its code

noisesmith19:07:06

multimethods are definitely not there for performance

markw20:07:22

@csd that is just screaming for protocols

✔️ 4
Alex Miller (Clojure team)20:07:47

well with a keyword type, probably multimethods are better

noisesmith20:07:56

you could make a placeholder type, but it would be clumsy yeah

markw20:07:59

Assuming there is some requirement to stick with keywords - it just seemed like type based dispatch to me

SgtZdog20:07:37

eyeballing the code for how case vs multimethod are implemented, I think you'll find multimethod to be a little slower, but it will be easier to extend. (case) can use hashing to run against pre-calculated values, whereas best case you get with multimethod is a pass on the initial (=) check in (isa?). I would prefer the multimethod for its easier extensibility (were I on the project).

noisesmith20:07:19

also case doesn't do the var indirection that multimethods do

noisesmith20:07:44

which can effect how things can inline when the JIT kicks in

Alex Miller (Clojure team)20:07:10

as usual, use appropriate skepticism with microbenchmarks

noisesmith20:07:01

right - but the initial question was a microbenchmark question (is this dispatch faster than this other one)

Alex Miller (Clojure team)20:07:15

but general commentary still valid

markw20:07:25

What does protocol default mean in that benchmark? A protocol on java.lang.Object?

SgtZdog20:07:27

Given that (according to the blog) the greater the variety in your uses the less the margin gets, I would be really disinclined to write using (case)

SgtZdog20:07:18

You're also on the order of micro-optimizations here where you're better served by readability.

mpdairy20:07:24

does anybody know the best way to launch a clojure repl within clojure? I want to make my clojure program into an uberjar that, when run, just dumps the user into a clojure repl in my module and prints a little help message.

SgtZdog20:07:53

As a newb, I'd guess something like (clojure.java.shell/sh) function call

noisesmith20:07:51

start clojure.main as your init class

SgtZdog20:07:16

^better idea

mpdairy20:07:29

well maybe I can load my own module, then have it call the thing in clojure.main that launches a repl

SgtZdog20:07:34

Probably, and that's probably what you'll want to actually do anyway since you will want to do setup stuff for the user on startup (like read a config file, do your (requires), etc).

noisesmith20:07:00

or if you require your module, when clojure.main starts your module will be preloaded - I think there's a way to select the ns clojure.main starts in as well

SgtZdog20:07:11

You mean by editing the clojure source file to (require '[myns])?

SgtZdog20:07:03

(nvm, answered by your link)

noisesmith20:07:59

yeah - It would be more like -i "(doto 'my.ns require in-ns)" if the -i effects the repl itself

noisesmith20:07:39

and maybe also -e for any prep code that shouldn't be run at the top level of that ns

mpdairy20:07:23

Oh cool, thanks, that looks like it will work

noisesmith20:07:56

this is what clj / clojure are using also, you could potentially leverage those depending on what you are doing

mpdairy20:07:51

oh yeah clojure.main/repl looks easiest, thanks @dpsutton. Now i have to figure out how to exit the one I just launched inside of my repl in emacs...

SgtZdog20:07:10

(exit) function will close the repl

SgtZdog20:07:30

or alt+f4 maybe? I'm more a windows guy...

mpdairy20:07:10

(exit) didn't work for some reason...

mpdairy20:07:16

i just killed java

noisesmith20:07:26

(exit) is an nrepl feature

noisesmith20:07:34

control-d makes the repl process stop

noisesmith20:07:45

(System/exit 0) makes the vm gracefully exit

noisesmith20:07:33

control-c will also make the repl stop, and shut down the vm, unless you set up a signal handler like nrepl does

mpdairy20:07:28

oh thanks, I'll just add my own exit function that calls (System/exit 0) so the users can exit easily

noisesmith20:07:45

that's the secret behind the interruptible-eval function @dpsutton linked to actually - in the big picture it's part of machinery that links a thread you can kill to a signal handler to kill that thread instead of your vm

SgtZdog20:07:56

figures, I always use nrepl, so didn't even realize that.

noisesmith21:07:44

something I literally used today at work: $ env $(grep -v '#' .env) rlwrap java -cp $(cat class-path) clojure.main - the class-path file was generated via a previous run of lein with-profile +repl cp

noisesmith21:07:46

this gives me a repl with all the same definitions and config as my lein / nrepl but without any leiningen or nrepl code running, just clojure.main plus my own code I explicitly run (all this done in order to have a cleaner set of stack traces for a tricky stalling issue)

noisesmith21:07:27

turned out I was eagerly consuming a lazy-seq backed by a network stream that wasn't receiving input

noisesmith21:07:55

with nrepl, 9/10 of the stack trace data from jstack was either leiningen or nrepl code / threads; removing that made finding the real problem much easier

noisesmith21:07:23

of course I would have the same result out of the box if the project was using clj :D

borkdude22:07:30

the :author field is commonly used on namespaces. is it also used on vars? and why not :authors?

hiredman22:07:13

:author is common in clojure contrib namespaces, I am not sure it is common outside of those, it definitely isn't used in the closed source clojure code I've worked on

hiredman22:07:44

I suspect it is :author instead of :authors, because as a feature it wasn't really designed, someone thought "oh, namespaces can have metadata, neat, I'll attach some" and they just picked :author, and others just copied it along

hiredman22:07:30

:author in clojure contrib is likely from back when contrib was in svn as a single repo, so without it it was hard to figure out who "owned" a given source file

hiredman22:07:22

git does a better job of tracking that sort of thing

borkdude22:07:46

:author also occurs quite a few times in clojure itself

andy.fingerhut22:07:22

apparently its use did not catch on like wildfire

hiredman22:07:15

my guess is the usage in clojure itself fall in to two camps: projects that moved from contrib in to core (I think this is just clojure.test) and places where stu came along and added it, which I dunno if he was inspired by the usage in contrib, or contrib was inspired by him there

emccue22:07:07

I've never really found a usecase for derive with multimethods

emccue22:07:48

Has anyone used the whole keyword hierarchy thing for real?

emccue22:07:05

I feel like there is some pattern I am missing

noisesmith22:07:52

@emccue the same case as concrete inheritance in eg. java, except not only do you define where implementations are derived from, but also the derivation rules on a method-by-method basis

noisesmith22:07:03

I've never found it useful in practice myself

hiredman22:07:06

https://github.com/clojure/clojure-contrib/commit/b0179b612c0fc7dcc9a4e4115bbbfb1330442109 appears to be the first :author tag in contrib, looks like it was added for the autodoc tool that is used to generate documentation

hiredman22:07:26

so it exists for a tool that is used to generate docs for http://clojure.org

emccue23:07:20

@noisesmith yeah but with concrete inheritance in java you maintain method compatability or are ostensibly guaranteed a subset of fields/slots

emccue23:07:35

The whole mechanism is more general than that

noisesmith23:07:08

right - it's not a similar mechanism, but has some of the same justifications / use cases

noisesmith23:07:27

it's the only real form of inheritance clojure offers beyond mucky stuff in gen-class

noisesmith23:07:44

(and the weird/buggy proxy-super)