Fork me on GitHub
#beginners
<
2020-02-09
>
Cameron01:02:44

@jason358 that's really interesting, I would not have imagined the performance changing (as unless I missed something, the code itself is just straight interop), I'd like to look more into that myself

Cameron01:02:32

oh you know actually I wonder, there's something I'm gonna try

Cameron01:02:16

So, indeed when using ^ints I see you get a faster speed for aset as you mention, and while I don't know the details on what sorts of optimizations Clojure makes behind the scenes (you might say that is my bedrock, when it comes to how low I've dug into Clojure internals) , I just wondered "now that it too has the info that we're setting ints, what if its optimization is too to just flat out skip to Array.setInt , or something similar?". And then I remembered that our aset-int is not just calling Array.setInt, its calling that and coercing the value into an int before it calls that, and so I am wondering if its this extra step that, when the two are possibly otherwise equal, is knocking aset-int into second place . Well, so far I tested it and sure enough, I get the same speed for aset ^ints and flat out calling (. java.lang.reflector.Array (setInt ^ints...)) I'm not 100% sold yet, though, as I'm getting sometimes wildly different timings for both, and I'd like to sit down and properly profile them. Anyways, so that's a possibility.

andy.fingerhut01:02:56

It is fairly quick to learn to use it, and it helps avoid a lot of pitfalls in performance measurement (in particular, it goes a long way to 'warm up' an expression, or cause the JVM's JIT compiler to optimize the byte code to native code, before starting its measurements)

andy.fingerhut01:02:49

Even so, I would recommend using it to measure some largeish number of iterations of aset or aset-int calls, rather than only a single one.

andy.fingerhut01:02:51

If you are familiar with Java, or are interested in looking at Java source created by decompiling JVM byte code, there is also https://github.com/clojure-goes-fast/clj-java-decompiler

Cameron01:02:13

ah thank you, I have not used criterium, although I was considering it a few minutes ago

Cameron01:02:52

yes actually I've been interested in getting a bit deeper for a while

Cameron01:02:07

ohh this goes right into the repl, that's really nifty

andy.fingerhut01:02:15

There are other projects under the 'clojure-goes-fast' group of projects that may also interest you

andy.fingerhut01:02:29

This web site links to several blog articles by the author of the clojure-goes-fast projects: http://clojure-goes-fast.com

andy.fingerhut01:02:24

Lots of good stuff there, if you are interested. If you do dig in, you are welcome to ask about implementation details in #beginners, but #clojure channel will probably have an on-average more-in-depth target audience for such topics.

Cameron02:02:51

oh, I hope I didn't give the impression I'm in #beginners to ask questions (not that there's anything wrong with that) ahaha I'd def feel more comfortable asking in #clojure, I just like to follow all the channels and help out when I can 😄 this overall comment itself was just for dmitry's question (although I realize you probably know that and are just pointing out where questions related to that theme would best be answered)

leonoel08:02:25

java.lang.reflect.Array accessors are notoriously slow https://bugs.openjdk.java.net/browse/JDK-8051447

Cameron16:02:46

@jason358 final update (hopefully), I dug into it deeper with Andy last night and sure enough its not what I said (or partially not, I have to look again now, I'm currently reviewing / doing a proepr write up of this since this was last night), but seems to have to do with aset actually having an inline implementation that calls another function entirely (`clojure.lang.RT/aset` is called, which actually just straight-up wraps arr[ind] = val and is overloaded to run generically for Object[], and more specifically for the primitive arrays like int[]. So when called with the ^ints annotation, its flat out calling

static public int aset(int[] xs, int i, int v){
	xs[i] = v;
	return v;
}

Cameron16:02:58

I wonder if it would be a good addition to take aset-int (and the rest of the primitive aset-* )and also add an inline to call their respective RT/aset functions?

jsn18:02:50

Thanks, I've been using just using aset since my benchmarks clearly showed it to be much faster (because amalloy @IRC told me it might be)

Ivan Koz02:02:54

why don't we use java convention for classpath package names, i.e ./org.domain.project.core?

andy.fingerhut02:02:57

Who do you mean by "we"? http://clojure.org does.

andy.fingerhut02:02:17

It is project by project whether someone does or not, and many people have obviously chosen not to.

Ivan Koz02:02:49

i'm not speaking of maven groupid

Ivan Koz02:02:24

rather ns package path, to avoid class-path collision

andy.fingerhut02:02:43

My answer is the same either way 🙂

andy.fingerhut02:02:14

A few projects do, most do not.

Ivan Koz02:02:51

i said we, because out of many clojure libs i've seen only few used conventional package names

andy.fingerhut02:02:13

we is a set of individuals, each of whom made their own choice, so there could be N different reasons.

andy.fingerhut02:02:42

I suspect a common one is: it is a pain when starting a small open source project to create a new official domain name, if you do not already have one

Ivan Koz02:02:46

so i'm looking to hear about these reasons

andy.fingerhut02:02:18

you do not necessarily know in advance whether it will become widely used. Once established, changing the name is a pain for users of a library.

andy.fingerhut02:02:17

Also, in practice, people are good at avoiding conflicts, and/or the newer one deferring to an existing/established one if a conflict is discovered.

andy.fingerhut02:02:36

I don't know statistics or examples in the Python world, but they don't use the Java convention, and have a much larger collection of library names than Clojure does, I believe, and I haven't heard that the Python world can't find a way to get along without using the Java naming conventions.

Ivan Koz02:02:09

interesting, thank you

andy.fingerhut02:02:30

If there were billions of people not using the convention and creating a library every day, there would be many more conflicts. The actual rate of library creation for Clojure, Java, and Python are much lower than that, and people creating new libs aren't completely unaware of existing names, because they are typically collected together into package repositories/directories

👍 4
Alex Miller (Clojure team)03:02:36

In my opinion, you should use a top level ns that is something you own, via either trademark or domain name, or just maybe a conferred id via github user (which I wish we had more strongly established a convention around). And if you’re not doing that, you’re wrong. But I also don’t want to be the jerk telling people what to do all the time.

Alex Miller (Clojure team)03:02:49

And ditto the lein/clojars convention for groupid

andy.fingerhut03:02:36

io.github.<username> is certainly one domain name everyone "owns" simply by virtue of creating a Github account with a unique name.

andy.fingerhut03:02:19

Long term (e.g. decades) I do not know whether Github would ever allow those account names to be reused.

andy.fingerhut03:02:01

but my previous comment implies switching from "accidental conflicts of names" to "malicious intentional conflicts of names". Avoiding accidental conflicts among cooperating people is pretty easy.

seancorfield03:02:21

I asked folks about next.jdbc before I went from alpha to beta. One of the things I asked about was the namespace structure, since I wondered about next.jdbc vs something like seancorfield.next.jdbc -- no one seemed to care and I really felt kind of uncomfortable about my name being in everyone's :require clauses 😞

Ivan Koz04:02:30

i would be absolutely fine seeing your name every day @U04V70XH6 =)

👍 4
didibus07:02:59

I kind of hate single part namespaces, like just the name of the lib. But I also think the full reverse domain convention of Java is excessive.

didibus07:02:32

I like going with just: org-name/lib-name/ns-name

didibus07:02:02

where org-name can be your own favourite online alias or whatever.

Aviv Kotek09:02:37

Using multi-methods and trying to dispatch on wildcards. e.g: (defmulti foo :type) (defmethod foo :rectangle OR :circle (fn [x] (println "i'm rectangle or a circle"))) (foo {:type :rectangle})

=> "i'm rectangle or a circle
how can I get desired behavior? couldn't find anything on docs

bartuka10:02:52

yeah, that's very weird rsrs. But what is happening is this: The keyword ::default is used as a * operator, however, when you call the defmulti with (nil :b) the defmulti does not find a dispatcher for it, so it will go direct to the :default implementation. Right there, he is using the get-method function and looking for an implementation that matches [*, :b] which is saved in the recover variable. after this step he is only validating that recover is not the :default implementation that he is current in [or it would produce a call to itself again] and then he calls this new implementation at (recover * :b)

bartuka10:02:23

I just don't know if it is really necessary to use the (.addMethod...

bartuka10:02:28

you can see that this is a bit hacky, because if you try to call (recover ::default :b) he will find the implementation and will not print that message

Aviv Kotek10:02:50

then what do you suggest? stick to the 'clever-disaptcher' option then?

bartuka10:02:05

for this specific case you show, I would go with the clever dispatcher and use a keyword like :rectangle-or-circle in the defmethod to make my intent clear

Aviv Kotek10:02:53

Maybe this worth a SO thread?

Aviv Kotek10:02:59

Could be a solution we don't know?

bartuka11:02:12

for sure it could have o/

Ben Sless12:02:48

It sound more like you want to do pattern matching and not necessarily dynamic dispatch

Ben Sless12:02:33

Have you looked into using core.match or other solutions based on it (such as defunhttp://github.com/killme2008/defun)

Alex Miller (Clojure team)15:02:49

multimethods support arbitrary keyword hierarchies - why not create a parent that :rectangle or :circle derive from?

Alex Miller (Clojure team)15:02:25

(def shapes (-> (make-hierarchy)
                (derive :rectangle :shape)
                (derive :circle :shape)))
  (defmulti foo :type :hierarchy #'shapes)
  (defmethod foo :shape [shape]
    (println "I'm a" (:type shape) "shape"))
  (foo {:type :rectangle})

👍 4
🚀 8
bartuka09:02:42

@aviv I would reproduce this behavior by writing a smarter dispatcher, something like:

(defn clever-dispatcher [{:keys [type]}]
  (cond
    (or (= type :rectangle) (= type :circle)) :rect-circle
    :else :square))

(defmulti foo (fn [v] (clever-dispatcher v)))

(defmethod foo :rect-circle
  (fn [x] (println "i'm rectangle or a circle")))

(foo {:type :rectangle})

bartuka09:02:56

but might be better to have two defmethod [:rectangle and another to :circle] and factor out the function that implements the behavior out, then call this function inside each defmethod to "re-use" the common code.

Aviv Kotek10:02:58

yea I thought about those options, but thought there's maybe any wildcard support to make it cleaner

Aviv Kotek10:02:29

there is a wildcard example in docs, but couldn't grasp it

bartuka10:02:55

can you share the link? I could not find it

Aviv Kotek10:02:19

let's continue on thread

Oz12:02:39

Is there a trick in cider/emacs to structurally edit a map? e.g, I have this line

{:padding    "1em"})
and I want to turn it to
{:padding    "1em"
   :margin     "1em"})
without traveling to the "}" mark before going down a line. It's a really small difference, but I think it makes sense.

flefik13:02:59

you mean when writing code in the editor?

flefik13:02:14

Is this what you are looking for?

didibus18:02:04

Don't think there's a default way no

Oz18:02:46

@UAEFFG05B thanks a lot for the resources! Its probably worth to invest a little more in practicing working with paredit, and it will get more fluent, and if its still bugging me I'll try to write a snippet to deal with it

didibus02:02:27

If you're looking for something good in emacs. Try adjust-parens + aggressive-indent-mode + smartparens

didibus02:02:37

Best combo in the world if you ask me

jwoods18:02:38

What is the easiest way to parse a time string to clojure time?

"2019-01-01T00:34:36"

Baris Aydek19:02:24

Hey. I have a beginner question. I'm trying to use namespaced keywords with :: . So I have 2 modules/files: (ns ex.core) and (ns ex.product) in product I have:

(ns ex.product)

(defn product-price [product] 
  (product {:car 2000 ::ps4 500}))

(product-price ::ps4) 
in core I have
(ns ex.core (:require [ex.product :as product]))

(def fee-usd 1.5)

(defn cost-of-product [p-type] 
  (+ (product/product-price p-type) fee-usd))

(cost-of-product :product/ps4)
in product, the call product-price returns 500 as expected while in core I have NullPointerException What am i doing wrong?

bartuka19:02:57

::ps4 is fully qualified, so it should be the same as :ex.product/ps4 . The usage of ::product/ps4 should work too [merged the two answers]

Baris Aydek19:02:17

yeah it worked with ::product/ps4

👍 4
Baris Aydek19:02:29

yeah confirmed. both full namespace :ex.product/ps4 and ::product/ps4 works. Thank you

Aleed20:02:09

can keywords not be passed directly as function arguments? e.g. a function called foo that destructures its args, this usage throws an arity error (foo :key1 :key2) so if I change function foo to not destructure args and just take a vector, this works (foo [:key1 :key2])

hiredman20:02:49

They can, your destructuring is likely incorrect

Aleed20:02:27

@hiredman hmm it's macro, so maybe that's why? here's what i'm basically doing

(defmacro foo
     [& args#]
     `(apply foo* (cljs.core/clj->js ~args#)))

Aleed20:02:01

ah now that I think about it it's maybe the # suffix

hiredman20:02:41

Just don't write macros until your are more familiar with the language

hiredman20:02:40

That just straight up doesn't need to be a macro

Aleed20:02:18

i wanted to convert cls values to js at compile time

hiredman20:02:45

No, you don't, and that macro is not doing that

hiredman21:02:00

a clojure macro is a little program writing in a meta language that takes in a clojure expression and returns a new clojure expression, it is confusing because the meta language is also clojure

hiredman21:02:34

it might actually be slightly easier to follow in clojurescript because in cljs macros are programs written in clojure that take in clojurescript expressions and return clojurescript expressions (the meta language and language are not the same)

hiredman21:02:10

so to write a macro you need to:

hiredman21:02:21

1. understand the language (clojure or clojurescript)

hiredman21:02:30

2. understand the meta language (clojure)

hiredman21:02:20

3. understand quoting (which determines if your expression is in the language or the meta language)

hiredman21:02:25

looking at your macro(trying to use apply to invoke a function on a js value, and the way you are handling varargs), and what you said about it (saying it converts cljs values to js at compile time, when the way stuff is quoted means it definitely doesn't do that, and understanding the difference between the language and the metalanguage makes that super problematic even if you got quoting right), makes me think you might need to work on all 3 points (really #3 is critical)

Aleed21:02:32

the js function takes a list of args, if i was calling it via js i'd just destructure them foo(...args) from google search found that I can use apply to have similar effect, not sure if there's a better function for that.

Aleed21:02:14

but yea, I'm definitely still a beginner, just wanted to use a macro for generating styles to be consistent with macros used with cljs react* wrapper I'm using. I tried following their code to have a similar effect in my own macro (https://github.com/Lokeh/helix/blob/master/src/helix/impl/props.cljc#L62-L63) so yeah, I'm not sure why you're saying it doesn't do that at all but as you mentioned, perhaps I do need a better grasp at the language first

hiredman21:02:17

so that code isn't a macro, and doesn't translate cljs data structures into js datas structures at compile time

hiredman21:02:23

the #? causes the dialect (clojure or clojurescript) to determine what the reader reads, so on clj you will get

`(->js ~(val entry))
but on cljs you will get
(->js (val entry)))
neither of which is a macro

hiredman21:02:54

the first is a quoted form, the second is a function call to the ->js function

hiredman21:02:42

I don't write clojurescript so I am not sure if it's apply function does things differently from clojure's, but clojure's apply function takes the arguments as a seqable thing, so doing the equivalent of what you are doing (taking the collection of arguments, dumping it in to an array, and then calling apply) is bizarre.

hiredman21:02:12

the ... syntax in js is also not destructuring (I think I've seen it called spreading), so to avoid confusion it is best not to refer to it as that. destructuring is a binding syntax that allows you to not just bindg a name to a value, but bind names to specific parts of a value, and clojure and javascript both have some version of this

hiredman21:02:21

(it does look like ... is re-used as part of the destructuring syntax in js, which might be confusing)

Aleed23:02:04

ah yes, the word I was looking for was spreading. and yeah, I know it's not a macro, but that function is then being used inside a macro. so i figured the effect would be the same. but anyway, appreciate the attempt at clarification. will try and stick to functions for now until i become more familiar w clojure

hiredman00:02:06

Because the macro language for clojurescript is clojure, the version of that function being called from the macro is the clojure one that returns bthe quoted form, so ->js is not called until runtime