This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-09-19
Channels
- # aws (2)
- # beginners (135)
- # boot (20)
- # chestnut (7)
- # cider (18)
- # clara (5)
- # cljs-dev (50)
- # cljsrn (30)
- # clojure (252)
- # clojure-italy (9)
- # clojure-losangeles (5)
- # clojure-russia (8)
- # clojure-spec (33)
- # clojure-uk (5)
- # clojurescript (32)
- # clr (4)
- # cursive (5)
- # data-science (1)
- # datascript (1)
- # datomic (40)
- # emacs (1)
- # fulcro (18)
- # graphql (11)
- # hoplon (3)
- # lein-figwheel (2)
- # lumo (47)
- # off-topic (2)
- # om-next (3)
- # onyx (10)
- # pedestal (22)
- # protorepl (6)
- # re-frame (7)
- # reagent (38)
- # ring (1)
- # ring-swagger (5)
- # rum (3)
- # spacemacs (19)
- # specter (5)
- # vim (13)
- # yada (16)
(This is likely a cljs, cljsbuild or version clash problem, but it was triggered by moving to clojure 1.9.0-beta1, so posting here)... Moving https://github.com/deg/trilystro to the new Clojure beta caused cljsbuild to fail with:
Sep 19, 2017 1:46:59 PM com.google.javascript.jscomp.LoggerErrorManager println
SEVERE: /home/deg/Documents/git/projects/trilystro/target/cljsbuild-compiler-1/cljs/core.js:3579: ERROR - Parse error. primary expression expected
case ##Inf:
^
This is in the function cljs.core/hash@deg This is a known thing re cljs - might ask over in #clojurescript or #cljs-dev on status
@alexmiller - Yup, @mfikes had the answer on #cljs-dev. But, looks like this will bite all ClojureScript projects that try the beta, until ClojureScript is updated to match. Can a warning be added to the beta release notes, meanwhile?
@sfalcon I personally prefer using Yesql over a Clojure DSL for SQL. It allows you to write everything in actual sql files and gain all SQL tooling from that.
@sfalcon I really like https://www.hugsql.org/
I was just looking into that the other day @bostonaholic. What's the reloading situation like?
@sfalcon I prefer https://github.com/jkk/honeysql, SQL as data structures
HoneySQL gave us the advantage of being able to programmatically build up SQL queries. Like have a base one and then add a join onto that and a different where clause to filter and such. Not sure how you'd do that with YesQL
Any opinions on tools.namespace
reloading (possibly reloaded.repl
) vs plain repl reevaluation?
I'm using reloaded.repl
at the moment, but I'm starting to feel as if going back to plain repl evaluation might be simpler. Though I'm not sure if changed to my ring handler would be picked up without restarting the server.
interesting, so the trend is to use libraries that use SQL as data structures as opposed to what Korma (for example) does
I used Korma before but something about the DSL feels off to me, will have a look at those libraries as well thank you
@sfalcon @michaeldrogalis elaborates a bit more on this Korma vs HoneySQL (data-centric vs otherwise) on the (very nice) Clojure Remote keynote: https://youtu.be/kP8wImz-x4w?t=1409. Thanks for that, mr. Drogalis!
@aisamu Anytime 🙂
I'm quite confused about this example...I have one function that appears to return a long, yet when I call inc
(or any other numerical function) on it the compiler tells me it's an Object: https://gist.github.com/Sophia-Gold/6e0abe5080b4db078220031ff3c95d6d What am I missing here?
by default the params and return vals of all functions are of type Object
you’ll need to type hint the return to get something different
(defn foo ^long [String string] ...)
the inner loop of foo
will used boxed longs as written now, but that could be rewritten with loop/recur to use primitive longs
Oh, interesting. I always default to thinking reduce
would be more performant through using the Iterator interface.
You can see in this example that it returns a Java long. What am I misunderstanding?
primitive means a primitive long
type, java.lang.Long
is a boxed object, that's the distinction i was making
@bronsa sorry, you're right about that. I for some reason thought there was clojure.lang.long
the jvm has a primitive long
type (which is not an object type) and a boxed class java.lang.Long
, type
will never return long
even if a function returns a primitive long, because type
only works on objects and thus will cause a primitive value to be boxed
no function can because it would have to accept both objects and primitive values, and the clojure compiler would cause values to be either boxed or unboxed as it deems appropriate, so not retaining the real type
if that's all that you care about than as @sophiago said, warn-on-boxed
/`warn-on-reflection`
@sophiago what are you calling a compiler feature? I get it now, never mind
And whats with this:
(defn ^long foo [x] (int x))
(type (foo 12))
=> java.lang.Integer
?but because the metadata on var names will get evaluated, you'd have to (defn ^{:tag 'long} foo ..
(i.e. explicitly quoting it)
otherwise you're tagging foo with the value of the long
function rather than with the long
symbol
var meta is evaluated. Function type hints are not.
moreover, the compiler will not emit primitive versions of the function if you tag the var meta rather than the argvec
I think the takeaway is that you should nearly always type hint the function return, not the var
@alexmiller sorry i''m not understanding, are you saying what I've said is wrong? because it isn't :)
no, I’m trying to agree with you :)
Hum, so would the resulting cast be around the var access instead of the function return?
I meant “the type hints interpreted by defn”
Actually, I'm definitly a bit confused about type hints. I've noticed somtimes you can type hint wrongly, and all works, no warnings or errors, sometimes you get runtime type errors.
what happens in (defn ^{:tag 'long} foo ..)
case is that foo returns an Object and at callsite it might get unboxed as a long, if the compiler deems it useful/necessary
@didibus yeah, this (afaict) is because of clojure's GIGO attitude. I don't particularly like it in this case (I think we could safely warn at compiler time) but it's gotten a bit better than it used to be
it’s not GIGO, it’s because it’s a hint, and the hint might be wrong
you might not know it’s wrong till runtime
so you can fall back to Object
well, if something is definitely not a type/class at compile time then it's not a type hint
well that I agree with
I can't see how the compiler accepting a funcntion as at type hint w/o warning/erroring out is not GIGO
I’m in full support of that being an error
it’s the (defn ^long foo ...)
one
it’s more like (defn foo ^clojure.string/join [x] (int x))
- the hint is a function not a type
But, about the other one, I understand maybe it compiling, but why does it work once its compiled to jvm byte code?
@bronsa patch welcome for https://dev.clojure.org/jira/browse/CLJ-1674 or whatever other jira is out there for this problem
but if, say, Integer and String had 2 methods with identical signatures, than that would fail at runtime
I mean, in java you can't type the return to string and have it return int. So does clojure ignore the hint amd still returns Object?
and for function return types, it only enforces them when that value is used in an interop form
e.g. (.foo (bar))
will enforce the type hint on the return type of bar
(at callsite of foo
!) but (identity (bar))
won't
type hints are only to prevent reflection (and in some cases, boxing), they're not to enforce static types
I see, okay, that explains I think the inconsistency of wrongly typed type hints I've seen. It'll fail at runtime with java interop.
an example of this behaviour is
user=> (deftype Foo [a])
user.Foo
user=> (deftype Bar [a])
user.Bar
user=> (defn ^Foo x [] (Bar. 1))
#'user/x
user=> (x)
#object[user.Bar 0x4983159f "user.Bar@4983159f"]
user=> (.a (x))
ClassCastException user.Bar cannot be cast to user.Foo user/eval158 (NO_SOURCE_FILE:8)
the incorrect type hint doesn't cause (x) to fail, it causes host interop on (x) to fail
Whats the idea of not enforcing the hint to always box/unbox or cast ? I don't see a negative in enforcing, except you'd know quicker that you hinted it wrong
well, the short answer is: type hints are not type assertions, the long(er) answer is: the compiler might now better or enforcing could affect redefinition
(defn foo ^String [x] (int x))
(type (foo 12))
Id expect this to be equal to
Object foo(Object x) { return (String)((int) x); }
and fail at the cast at runtimeI'm not talking static checks. But runtime type errors. Ok, ya I understand, so they're unpredictable and mostly for interop and certain unboxing situation.
as to enforcing at return type vs callsite -- I believe it's a performance compromise of sorts
I guess I'll restrict their usage to only when the compiler warns for boxing or needs it to dispatch.
Some people use them as programmer hints, like documentation. But that had mislead me, because sometimes the hint is wrong, yet all works. I see that's a pretty bad use of them now. Spec is the way to go.
I actually had a related question. Is anyone familiar with Zach Tellman's primitive-math library? I'm wondering exactly how it differs from using unchecked-math in clojure.core. I'm not sure how long the latter has been around, but was under the assumption that library was still useful for providing reflection warnings for math operations.
The warn-on-boxed stuff was kind of done about the same time as primitive-math (independently). The warn-on-boxed stuff will cover you in most situations, but it’s not perfect and won’t catch all cases (particularly in return paths). I have not looked at primitive-math closely enough to know for sure, but it may be useful in some cases that are not well-caught by warn-on-boxed.
I have nothing against it :) (but there is some overlap)
That's pretty much exactly what I told someone in a GitHub issue who was trying to tell me they're exactly the same (with more color that reflection could occur in the return paths) so happy to know I was mostly right 🙂
Oh, I suppose I should also mention that afaik primitive-math is more comprehensive. For example, it includes equality and bitwise operators.
@alexmiller Hi! I was just reading you blogpost http://insideclojure.org/2014/12/15/warn-on-boxed/ and tried to use no.disassemble
with boot but requiring the dependency doesn’t seem to be enough (some jvm flag is missing I think, looking a the lein plugin)
Could you guide me on how to set it up? Or is there another tool I can use for the same purpose?
you should look at the docs at https://github.com/gtrak/no.disassemble or the related leiningen plugin https://github.com/gtrak/no.disassemble/tree/master/lein-nodisassemble
or ping @gtrak here!
lein-disassemble is an easy @hmaurer then lein repl
then (require '[no.disassemble :refer [disassemble]])
I found this https://github.com/gtrak/no.disassemble/blob/master/lein-nodisassemble/src/lein_nodisassemble/plugin.clj#L20, but I am not quite sure what path I should pass
actually, could this cause an issue with boot’s approach of loading dependencies after the JVM is started? @seancorfield
You'd specify the Java agent option via BOOT_JVM_OPTIONS
. Pass -javaagent:/path/to/the/nodisassemble.jar
(you'd need to run Boot at least once without that in order to download the JAR to .m2/repository
and then you'd have the (long) path to the JAR)
@seancorfield thanks. So something like this? BOOT_JVM_OPTIONS='-javaagent:~/.m2/repository/nodisassemble/nodisassemble/0.1.3/nodisassemble-0.1.3.jar'
Sorry, yet another boxing question... Are there best practices for converting characters to numbers? Using the primitive-math casting operators throws an error so I have to switch to those from clojure.core. Not sure if this is something to log an issue with over the primitive-math casts or whether it falls under one of those cases where I'm just not being warned about reflection when using the core versions.
@hmaurer Example
(! 687)-> BOOT_JVM_OPTIONS="-javaagent:/Users/sean/.m2/repository/nodisassemble/nodisassemble/0.1.3/nodisassemble-0.1.3.jar" boot -d nodisassemble repl
...
Initializing NoDisassemble Transformer
boot.user=> (require 'no.disassemble)
nil
boot.user=> (in-ns 'no.disassemble)
#object[clojure.lang.Namespace 0x5c26a243 "no.disassemble"]
no.disassemble=> (println (disassemble (fn [])))
// (version 1.5 : 49.0, super bit)
public final class no.disassemble$eval2043$fn__2044 extends clojure.lang.AFunction {
// Method descriptor #6 ()V
// Stack: 1, Locals: 1
public disassemble$eval2043$fn__2044();
...
no.disassemble=>
I think you'll need the full path, not ~
@seancorfield yep I just realised that. It works now
Unrelated question (well, related to the discussion we had earlier on uberjars): how do you usually deploy boot applications to production?
I love Boot for experimenting with new libraries 🙂 No need for a project folder or anything 🙂
Mostly we build uberjars. @alexmiller's comment about them being "evil" notwithstanding, it's the most convenient way so we only need Java and the JAR file on the target system.
(that said, we still also deploy .clj
source files since we use them inside other apps that predates our uberjar approach)
Could you imagine a jar downloading its dependencies on startup using boot? Or am I misunderstanding something?
Don't do that, even if you could. Try to have reproducible builds, which leaves zero chance of anything having differed. Better to resolve all things beforehand, and then copy those in one go to your hosts. That's the only way to be 100% sure all hosts are perfectly in sync.
So we still have boot repl
available on production too (and boot whatever-task
stuff as well)
@hmaurer What do you gain over having the source of the project there and just doing boot run
or whatever?
What would that JAR be?
Define "make sense". There are pros and cons 🙂
The cons include deploying all your source etc rather than just a single file (which may make your build process more complex); requiring Boot be installed on all your servers; requiring that your servers can access Maven Central and Clojars at all times; ...
That last one was enough for us to run our own Archiva instance acting as a caching proxy for Clojars (before Clojars got the nice, new, redundant CDN implementation for the repo) and all of those together with a desire to move processes to Docker pushed us to use uberjars instead.
(that said, we haven't actually made the move to Docker yet)
@alexmiller sorry to ping you, but wrt to my last question do you know whether something like (map (comp long char) "foo")
is one of those cases where the compiler doesn't throw an error on boxing? I suspect this may be the case since all the primitive-math version of long
does is call unchecked-long
from clojure.core, which throws an error where as cc's long
does not.
anything you do with seq functions will only use objects
just curious, what does (comp long char)
accomplish that long
doesn’t?
I don’t think it will do anything different
Yeah, I just realized since I used map
to convert from a string I should probably mention I get the same erorr with just (primitive-math/long \a)
whereas (long \a)
doesn't throw a boxing warning.
the boxed warnings will only happen if you’re calling a numeric operation
and map
is not one of those
My clarification was meant to say it happens outside of being passed to a higher order function.
@noisesmith I'm using Zach Tellman's primitive-math library so for that long
you do actually need to cast to a char first
but it is a char already?
long
is handled in RT, so it’s not caught by the warnings in Numbers
@alexmiller thank you. That's exactly what I needed to know.
I think you’re going to end up passing a Character object into RT.longCast(Object)
so if you just use (map long "abc")
with clojure.core/long it just works, but you need (comp long char)
instead because of primitive-math?
@noisesmith exactly
none of these options are going to giving you a sequence of primitive values regardless, so I’m not sure it really matters
yeah- you can’t put a primitive on the stack, so if you are using map it’s boxed, guaranteed
it’s hard to recommend something different, b/c it matters a lot what you’re going to do with the result
(I guess you could make an array transducing context and use a map transducer - or would the map transducer still force the boxing?
:thumbsup:
(although we’ve talked about primtiive transducing options)
if your goal is to produce a collection, you could either use a Java long[] or a primitive vector of longs. In general, the latter tends to force boxing on use anyways (through the vector APIs) so you primarily get only memory savings.
Hmm. Does this "primitive transducing" discussion have any bearing outside of a higher-order context? Before Justin brought that up I assumed I was stuck with boxing for chars, but I'm not sure whether you're suggesting something from the java.lang.Character.
generally, if I’m trying to super optimize all this stuff, I’d use long-array
or maybe int-array
here (depends on whether you just care about ascii or full unicode)
this is a particularly tricky area of Java
Right. I'm not going so far with optimization here to use unboxed sequences (and have found vector-of
to be almost always useless for the reason you mentioned).
fascinating
user=> (doto 'primitive-math require in-ns)
primitive-math
primitive-math=> (long \a)
ClassCastException java.lang.Character cannot be cast to java.lang.Number clojure.lang.RT.uncheckedLongCast (RT.java:1464)
primitive-math=> (long (char \a))
ClassCastException java.lang.Character cannot be cast to java.lang.Number clojure.lang.RT.uncheckedLongCast (RT.java:1464)
primitive-math=> (map (comp long char) "hello")
ClassCastException java.lang.Character cannot be cast to java.lang.Number clojure.lang.RT.uncheckedLongCast (RT.java:1464)
I’m sure I’m doing something silly here
Your first two cases were the behavior I was asking about here. The third one for some reason works for me.
There is also Long/parseLong
, but that only works for numerical strings. Similar to (map #(Character/digit %) "123")
@sophiago it seems like without using clojure.core/long etc. the easiest way to get a number from a char in a string is
=> (Character/codePointAt "foo" 0)
102
but that may be doing something wrong with unicode contexts
actually
=> (Character/codePointAt "☃" 0)
9731
Oh, it doesn't seem to work in higher-order functions, e.g. (map-indexed #(Character/codePointAt %2 %1) "foo")
so I'd need to use loop/recur. Plus I'm not even sure it saves me from boxing wrt to the individual elements.
the boxing would depend on what you do in your consuming function, and I would use (map #(f (Character/codePointAt s %)) (range (count s)))
- if f knows how to use a numeric value unboxed, it would work I bet
primitive-math=> (let [s "hello, world!"] (map #(inc ^int (Character/codePointAt s %)) (range (count s))))
(105 102 109 109 112 45 33 120 112 115 109 101 34)
Thanks. I'll compare. I sort of suspect it's not worth it since I'm just dumping them into a sequence, but will test.
@noisesmith postmortem: worth adding a helper function for, but not significantly faster for any use cases and slower for some. Regardless, thanks for the tip.
If you deploy an uberjar, you can still have a REPL in production of course (start it with the Socket REPL Server options) -- or just run the uberjar with no options (we don't AOT, and we set :main
to clojure.main
so an uberjar can run clojure.main
to get a REPL -- or we can use -m my.namespace
to run the -main
in that namespace).
@seancorfield can you start a repl in a running production system if you didn’t start the uberjar with the socket repl server option?
there’s a namespace to use to start the socket server by hand, and of course you can use clojure.tools.nrepl.server to make an nrepl server instead
@noisesmith can you plug into a running jvm like that? I am not familiar with how those things work
The clojure process can call the functions to start a server. If you really need to monkey your way in, a debug connection could be used to access clojure via interop from the java side and make it do so I guess…
In the past we've had a way to tell a process to start a REPL (and to stop it again) so we can have a REPL open when we need it.
(that has varied depending on the process)
Mostly, for debugging, we'd just restart a process with the socket REPL server JVM options if we needed that. Or just start a standalone REPL while ssh'd into the server, for local interactions.
(running a REPL server in a live, production process comes with all sorts of caveats about security and risk and so on and so forth... we have -- occasionally -- applied a hot fix via the REPL by sending a new function defn
but... "ultimate power" and all that... generally not recommended practice at all 😐 )
This is the power of a lisp machine. 0 downtime deploys are much easier if you can live reload code this way.
aka "extensive downtime deploys when you mess it up" 😉
@U0MDMDYR3 Heh, just send the old defn
back into the running image 🙂
I have no qualms about leaving a localhost nrepl on production servers. It’s invaluable.
@seancorfield we maintain our processes listening on localhost nrepl ports and use ssh to control them. in rare circumstances we apply hotfixes or debugging that are urgent or otherwise problematic. just another component that spins up an nrepl server. There are caveats and we don't do it lightly, but the power has saved my company a lot of money over the last 4 years.
@norman @bja Glad to hear of others using a live localhost REPL for this!
yeah - it requires discretion, but can lead to sorting out weird production issues faster
When we had just a few production nodes, we would hot patch fixes regularly. There’s also nothing like being able to instrument or sample live data. Obviously you need to know your tools well and tread lightly, but there’s nothing like being able to go from bug report to fixed in production in 5 minutes.
@norman doesn’t it become a bit of a nightmare if you apply ad-hoc patches on production?
After the fire is out we will get packages built and deployed, but that could be a day or two later
Now we just have too many production nodes to do this, but our infrastructure for fixes is more sophisticated
Today it could take half a day to get a fix committed, built in CI, approved by QA and deployed by ops
Hand fixing 3 heads takes a couple minutes. Patching 20 is less fun manual. It all depends on the scale