This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-08-07
Channels
- # beginners (95)
- # cider (131)
- # cljdoc (12)
- # cljsjs (2)
- # clojure (209)
- # clojure-dev (1)
- # clojure-italy (3)
- # clojure-nl (10)
- # clojure-russia (37)
- # clojure-spec (19)
- # clojure-uk (182)
- # clojurescript (136)
- # cursive (28)
- # datomic (28)
- # editors (55)
- # figwheel-main (1)
- # fulcro (36)
- # hyperfiddle (12)
- # jobs (1)
- # jobs-discuss (55)
- # luminus (1)
- # mount (1)
- # off-topic (28)
- # onyx (18)
- # reagent (17)
- # ring-swagger (4)
- # rum (14)
- # shadow-cljs (22)
- # spacemacs (15)
- # tools-deps (16)
- # vim (26)
so I finally have time now to do catch up with devops and cloud stuff (even though it’s 2018 now and serverless is the cool kid in town)… what options are there in clojure? There is pallet http://palletops.com/ but seems the project’s dead?
@lemontea Does there need to be anything in Clojure? There are plenty of solid, well-maintained DevOps projects out there that aren't Clojure.
(we tried to use Pallet -- we even worked with the creators of Pallet -- and it just wasn't up to snuff for what we needed: if you aren't doing something that is really mainstream and standard, Pallet doesn't really have an answer... That said, it's a very cool project and was a great concept... it just wasn't really flexible enough or simple enough)
being able to do things end-to-end with clojure will be more satisfying, but ya I expect the big player out there to be better supported
still, since this is just learning I don’t need anything fancy at all - just “work” is plenty good enough
I don't know of anything in Clojure in that space...
...at least, nothing that is still actively maintained.
oh no. 😐 https://twitter.com/dorrismccomics (pardon the off topic link… “oh no” is a great answer to many situations)
for career development going the mainstream route (for things that ain’t your “core competency” or differentiator) is pretty much a no-brainer, but I think I can give it a chance/try…
and https://cemerick.com/2010/11/02/continuous-deployment-of-clojure-web-applications/
Yeah, and that latter is pretty much ancient history in Clojure terms.
And Adam Bard's blog seems like a huge amount of work to deploy those things (and nearly all from forks or ad hoc variants of old, unmaintained projects).
@lemontea if you're into devops, I think Nixos is worth the effort. It's a huge thing to learn, but it's just elegant
I do a lot of devops-type work and would never do it in clojure. Interfacing with the messy details of the operating system is not a strength of the jvm, and the abstractions one wants to use in clojure do not fit. The "reconciliation" process kubernetes uses is much more appropriate, and the ergonomics of go for OS work are highly productive. Different spaces, different tools.
I see. I wonder why jvm is weak at that though. Because it’s designed to be platform independent and so can’t handle each OS’s idiosyncrasies easily?
Yes, that design decision means that there will be a certain amount of implementation detail exposure under the abstraction- "works this way on this platform" and "works that way on that platform."" But even dealing with the platform abstractions themselves- the file system, the network- is just ugly and inefficient. The recent kerfuffle around the removal of sun.misc.Unsafe is a good example- basically every networking library was using it to work around the abstraction (and not just one abstraction, there are 3 or 4 different network IO abstractions in the standard library now). Take that and move up a layer with Clojure, where you have immutable data structures, and infinite seqs- these things just do not align with the very concrete systems world.
In comparison, Go's ergonomics match closely to the linux kernel's, so much so that Google wrote a system call layer in userspace as a security measure in Go. Kubernetes very effectively bridges the gap between declarative and operational. You specify a declarative model of some system thing- how many pods you want running, what memory or io policy you want- and then the Controllers iteratively compare the state of the system against the state of the model and go through state transition mechanisms to get them aligned over time. The gravity and physics of the system are first class considerations.
One more point before calling the horse truly dead- it is not as though the linux kernel is a model of beautiful design, the interfaces to which should be preserved all the way up the stack. Quite the contrary. There are a half dozen different ways to do everything, each with their own unique special cases and historical oddities. But I'd put it this way- in the same way that Clojure is "hosted" on the JVM and the JSVM, Go is "hosted" on the interface exposed by the kernel to userspace.
you’ve got a well beaten horse there :face_with_rolling_eyes:. I do intend to learn Kubernetes though - logically it precedes devops scripting/programming tool like puppet/chef/ansible?
Interesting, I have not used them, but I have worked with the windows apis, which have more design thinking behind them, and can imagine a managed runtime on top being sane. Wrt k8s, it is orthogonal and to some extent competitive with ansible, puppet and co. K8s, combined with a container runtime, is much more like a multi-machine platform/programming abstraction.
People use ansible to deploy kubernetes, and maybe do some system level management with puppet. But a lot of tasks that were also in those tools bailiwick in terms of allocating resources to workloads are better done directly in k8s
*allocating resources and especially managing workload/app lifecycle
are you guys aware of NixOS?
I am, followed it early on when they were just starting, maybe a decade ago, and poke at it from time to time, but haven't used it in anger. Glad to see they are still at it.
I think they're quite mature now 🙂
Is there industry traction? Revenues + support model?
Technically I don't doubt they're solid
Go is pretty nice, the directory structure thing really bugs me though....but it is what it is....i wish i could like Rust.
Yes. One way to do things. One of the ergonomics-at-scale that I really like is that I can read anyone's go and understand what it is doing. With clojure, everyone writes it differently, builds their own semantics and abstractions. As much as that makes it fun to write, the ecosystem suffers, I believe, because reading is impeded.
C++ will be in my life forever though I'm sure because it's the only thing one can reasonably use on pretty much any platform (well that or C, sideshow_bob_shuddering.gif).
I wish there was a more FP-style systems language. I do a lot of video systems development and there's a lot of state, both intentionally and not, and I wonder if an FP style approach may reduce bugs.
although Wowza and Red5 are both Java media servers so maybe Clojure would work for that sort of thing.
might be an interesting experiment since with graal you could interact with ffmpeg libs to do transcoding and so on.....hm
I hope someone can help me explain why I get OutOfMemoryError. I am trying to copy data from a huge table in database A to database B using clojure.jdbc. It blows my heap, and I don't understand why. https://gist.github.com/RokLenarcic/cc1562e376c0d29371a224724def0153
As far as I can see I am only copying 10k rows at a time
After copying about 3M rows I get OutOfMemoryError
doesn't that code try to create a resultset which is huge from the query made at line 6?
the resultset will contain only nils since your :result-set-fn doesn't return anything sensible
is there a reason why you can't just query 10k rows at a time and use LIMIT
and OFFSET
to go through the database?
anyway, not sure if this really helped. I know you are using cursors there but I think you are creating a huge empty resultset
Is there a way to preserve type hints in macroexpansion? I want to do something like this:
(defmacro unwrap-solution [s]
`(.solution ^Solution ~s))
but am getting reflection warnings. Thanks.Well the result-set is empty because it's not the point
the result set handling function is performing inserts
which are the point
@puzzler it requires basically applying the type hint using with-meta
If that’s not enough, I can dig up an example
@roklenarcic yes I know that it's empty. But it still consumes memory. Consider (vec (take 3e6 (repeat nil)))
. That will consume quite a lot of memory without doing anything meaningful
Like I said, I'm not 100% certain if that is the cause of your problems but just giving some ideas
since you are querying data from the database clojure.jdbc tries to return a resultset. Your query will end up returning all the data your query would return, however you are dropping most of that out in your :result-set-fn. This leaves however a nil
per row in your database which is stored in memory of you Clojure app. At least, this is what I think is happening. It's a different version of lazy sequences and keeping on the sequence head
Possibly, but I don't see a way to do it better.
Also 3M element vector of nil shouldn't really take gigabytes of RAM
yes gigabytes of heap gone
Also I think you are not correct about how the result-set-fn works
it is applied to resultset as a whole
:row-fn is applied to each row, but result-set-fn is applied to whole result set
my guess would be nil
that is my main idea what could be going wrong. I'm assuming you've already set -Xmx4g
or similar to your JVM
but I'll check it
if this doesn't help then I would suggest grabbing a profiler and retrieve some real data to see what kind of objects are being gathered
result of that jdbc/query
is nil
I think I understand what's wrong
anyway, profiler will give you unbiased real world data which should give you some hints
my destructuring expression ruins everything
(fn [[keys & values :as rs]]
keeps whole lazy sequence in values
I need to delete that
@alexmiller thanks
@roklenarcic did it solve your problem?
So the function parameter is a lazy sequence and the parameter declaration itself is holding on to the head of it. How do I get around that?
in other words, when you have (fn [result-set] .... )
and result-set
is large lazy sequence, is there even a way to process that without blowing heap?
or if it's easier, imagine it's infinite sequence
but won't result-set
local hold to head of lazy sequence and ruin everything?
I'll try again
I'll let you know
No I think I had (first rs) somewhere down the line
I moved that up into a new local
since if parameter declaration would cause lazy seq to hold the head then lazy sequences would be pretty much useless
well, usually they get passed directly from fn to fn
and things like doseq are a macro
for example https://github.com/clojure/clojure/blob/clojure-1.9.0/src/clj/clojure/core.clj#L2737
if that would hold the head then you would not be able to do:
(doseq [x (map inc (range))] (println x))
What about something like this?
I think partition-all into doseq makes sure that batches get dropped
not really, since first
returns the element
which has no link to sequence itself
I'm running the new version.... gimme 5 min
looks like another failure
Slowed down to a crawl, GC working overtime
I changed the Gist to latest version
just an idea, what if you take the write connection opening within your result set fn
it shouldn't really affect performance that much since you have (I hope?) connection pools in place anyway
looks like I'm going to have to do that
but first I'll see if I can write a fn that consumes a massive sequence of integers
jesus christ... the problem was that running REPL as Debug in IntelliJ prevents locals from being cleared (despite the button that prevents locals clearing not being selected at all). Seems to be a Cursive bug or something.
since for Cursive and IntelliJ to be able to provide local value debugging it needs to hold on to those...
@dominicm Re: string cycling: Thank you for answering. @andy.fingerhut your last solution is exactly what I meant, you are a star )
hi all, quick question. I'm trying to write the simplest of macros: like (comment)
with condition: (include cond? body)
I tried:
(defmacro include [cond? body]
(when cond?
body))
But macroexpand
only prints the result of evaluating the body, not the body itself. e.g.
(include true (+ 1 2))
; => 3
; instead of '(+ 1 2)
That is correct: the when
is executed at macro-expand time, and is not included in the result. Only body
will be returned as the result of the macro, and only when cond?
evaluates to true
Sorry, the body
will be evaluated also
What you're missing are the backtick and tilde operators
and even once you’ve added them, remember that calling a macro will also cause the returned code to be evaluated
Yeah, but I couldn't figure it out. I tried unquoting, but I don't know what to quote, since I only want what's in body, nothing else
ok, first question, do you want the condition to be checked at compile (macro-expand) time or at runtime?
(defmacro include [cond? body]
(when cond?
`~body))
=> #'user/include
(macroexpand '(include true (+ 1 2)))
=> (+ 1 2)
@manutter51 is that any different from
(defmacro include [cond? body]
(when cond?
body))
no quote/unquote?Yes, they are different
without the backtick and tilde, you’re defining code that executes at macroexpand-time, so everything will be evaluated at macroexpand-time, and so expanding the macro will give you 3
With the backtick and tilde, you’re writing a macro that returns an expression that will be evaluated after macroexpand time, so when you expand the macro you get back [EDIT: nope, read the OP wrong, they’re not different in fact.](+ 1 2)
@manutter51 I don't think that's the case
when you unquote from a syntax-quote in a macro, the unquoted code is executed at macroexpansion time
I think if you want macroexpand
to return '(+ 1 2)
and not just (+ 1 2)
, then yes, it should be
`'~body
@bronsa You’re right, I thought the OP was saying they got back 3
when they expanded the macro, but I misread
@bronsa I realise that. The idea is to include that code if an external condition is true, which I still haven't figured what it'll be, probably something off lein project map
Yeah, the problem with macros is that you don’t get any runtime values, you only get compile-time symbols
OK, thanks guys. First I wasn't quoting the code to macroexpand
to begin with. So that's why I always got the eval result.
As for @manutter51 example, I get this:
(defmacro include1 [cond? body]
(when cond?
`~body))
(defmacro include2 [cond? body]
(when cond?
body))
(macroexpand '(include1 true (+ 1 2)))
=> (+ 1 2)
(macroexpand '(include2 true (+ 1 2)))
=> (+ 1 2)
So I can't see the difference between quote-unquote or do nothing.Yes, that was my mistake, I misread your original post
user=> '`[~foo]
(clojure.core/apply clojure.core/vector (clojure.core/seq (clojure.core/concat (clojure.core/list foo))))
Heh, gonna file that last example under “Be careful what you wish for.”
(that actually generates so much code that the bytecode excedes the classfile limits of the jvm)
So, maybe a wider question. I need to do "editions" or "cuts" of my program. i.e. different editions have different features. I'm not quite sure how to do it. We distribute a uberjar, so I was thinking using a macro like the above to include code based on a lein project map value, or something like that.
yeah, you could have different profiles, and inject a var to determine which one to generate
Why does this give a reflection warning:
(let [id-1 3
id-2 1
id-3 2
ids [id-1 id-2 id-3]]
#(compare (.indexOf ids %1) (.indexOf ids %2)))
;; => call to method indexOf on clojure.lang.IPersistentVector can't be resolved (no such method).
But this doesn't?
(let [ids [3 2 1]]
#(compare (.indexOf ids %1) (.indexOf ids %2)))
The only difference is in the values inside the vector, which doesn't seem like it should affect calling indexOf
on the vector.@xiongtx No exception for me:
(let [id-1 3
id-2 1
id-3 2
ids [id-1 id-2 id-3]]
#(compare (.indexOf ids %1) (.indexOf ids %2)))
=> #object[user$eval1506$fn__1507 0x5c10d9e9 "[email protected]"]
it's not an exception, it's a reflection warning
you need to turn those on to see them
Clojure 1.9.0
(ins)user=> (set! *warn-on-reflection* true)
true
(let [id-1 3
id-2 1
id-3 2
ids [id-1 id-2 id-3]]
#(compare (.indexOf ids %1) (.indexOf ids %2)))
Reflection warning, NO_SOURCE_PATH:6:13 - call to method indexOf on clojure.lang.IPersistentVector can't be resolved (no such method).
Reflection warning, NO_SOURCE_PATH:6:31 - call to method indexOf on clojure.lang.IPersistentVector can't be resolved (no such method).
#object[user$eval149$fn__150 0x2072acb2 "[email protected]"]
@xiongtx my hunch is that the method is specifically parameterized on the second arg's type, and in one case the clojure compiler knows that the type is Long, in the other it doesn't carry that info when compiling
the clojure compiler doesn't try to be especially clever
right
But how does the Long
type affect whether the vector is recognized as a subtype of List
or only an IPersistentVector
?
hmm no, that's on the List API and it takes Object
that wouldn't be the thing, I was wrong anyway - I thought maybe the arg type was parameterized but it's not
sometimes methods are not found because the type of some arg isn't matched
I created https://dev.clojure.org/jira/browse/CLJ-2382 Can't seem to edit the issue to fix the code formatting, however.
you should probably specify your clojure version also
(and for bonus, maybe try it on the latest alpha)
ah yeah @alexmiller beat me to it :)
a minimal repro is
user=> #(.indexOf [:a] %1)
#object[user$eval187$fn__188 0x3b0c9195 "[email protected]"]
user=> #(.indexOf [%1] %1)
Reflection warning, NO_SOURCE_PATH:18:2 - call to method indexOf on clojure.lang.IPersistentVector can't be resolved (no such method).
#object[user$eval192$fn__193 0x8c11eee "[email protected]"]