Fork me on GitHub
#clojure
<
2018-06-08
>
robert-stuttaford06:06:18

is there an identity for transducers? something suitable for this situation: (sequence (or (make-xform) identity) coll)

reborg06:06:10

(map identity)?

robert-stuttaford07:06:54

yeah, that’s what i did. was wondering if there was a blessed way

reborg07:06:40

Needed this a couple of times myself, so here's an additional option:

(def xf-identity
  (fn [rf]
    (fn
      ([] (rf))
      ([res] (rf res))
      ([res in] (rf res in))
      ([res in & ins] (rf res (list* in ins))))))

user=> (sequence (comp (map inc) xf-identity) (range 10))
(1 2 3 4 5 6 7 8 9 10)
user=> (sequence 
         (comp (map #(apply + %&)) 
                xf-identity) (range 10) (range 10))
(0 2 4 6 8 10 12 14 16 18)

reborg07:06:13

Vaguely faster as expected:

(let [items (range 10000)]
  (quick-bench
    (dorun
      (sequence (map identity) items))))
;; 914.020710 µs

(let [items (range 10000)]
  (quick-bench
    (dorun
      (sequence xf-identity items))))
;; 892.697959 µs

bronsa08:06:53

identity itself is a valid transducer and is THE identity transducer

👍 4
reborg08:06:57

doesn't handle the mutliple seqs, but yeah, useful

bronsa08:06:49

how could it handle multiple seqs?

bronsa08:06:22

I’m not sure what that means

reborg08:06:03

there's no obvious identity semantic for multiple seqs, I just gave one option above, but it's 1 convention that can be adopted

bronsa08:06:56

identity as a transducer is equivalent to

(defn xf-identity [rf] (fn ([] (rf)) ([res] (rf res)) ([res in] (rf res in)) ([res in & ins] (apply rf res in ins)))

bronsa09:06:54

which is trivial to prove as that inner fn is equivalent to rf , collapsing the whole thing to (defn xf-identity [rf] rf)

bronsa09:06:27

so it does have “support” for multiple inputs

bronsa09:06:04

what your xf-identity is doing is just packing multiple arguments into a list which I think is a bad idea

bronsa09:06:36

your next transducer in the pipeline will take either an a or a list of as through the same arity depending on the arity invoked by xf-identity

bronsa09:06:46

as opposed to either an a or multiple as in different arities

reborg09:06:16

@bronsa what do you expect from (sequence xf-identity (range 10) (range 10)), that you should not use it?

bronsa09:06:36

that xf-identity is equivalent to map identity for the 1-input case and to map list for the n-input case

bronsa09:06:26

flipping behaviour based on arity is bad IMO

reborg09:06:14

so is xf-identity=(map list) good for (sequence xf-identity (range 10) (range 10)) ?

bronsa09:06:37

I don’t think xf-identity as you wrote it is a good transducer, so it being “good” or “bad” for a particular use case doesn’t make much sense to me

bronsa09:06:14

I wouldn’t call map list identity

bronsa09:06:01

and I definitely wouldn’t flip between 2 different functions depending on the number of input arguments

bronsa09:06:22

user=> (sequence (comp (map list) identity) (range 10) (range 10))
((0 0) (1 1) (2 2) (3 3) (4 4) (5 5) (6 6) (7 7) (8 8) (9 9))
btw

bronsa09:06:32

identity can be used in multi-input contexts

bronsa09:06:43

and has predictable and well defined behaviour

bronsa09:06:09

so I don’t see why it would be desiderable to complect identity and (map list) in a single transducer

reborg10:06:52

I don't have a specific use case in mind and given the power of identity I would agree with you and not suggest to use a custom xf-identity as a general case. So @robert-stuttaford should consider (or (make-xform) identity) as the first choice. Thanks @bronsa for the always useful insights.

bronsa10:06:07

fun one: try evaluating (sequence identity [1] [1]) (which obviously is non-sensical)

bronsa10:06:26

ArityException Wrong number of args (3) passed to:   clojure.lang.AFn.throwArity (AFn.java:430)

bronsa10:06:45

passed to ??? simple_smile

reborg10:06:58

that's the TransformerIterator itself I guess

bronsa10:06:32

user=> (.getSimpleName clojure.lang.TransformerIterator$1)
""

lilactown16:06:52

I’m using clojure.walk to transform a tree of maps. is there a way to tell walk to ignore the value at a key?

bronsa16:06:14

if you’re using clojure.walk/walk you can control the recursion and you can do whatever you want by writing the right inner or outer function, if you’re using prewalk or postwalk then no

lilactown16:06:06

I just default to using prewalk. gonna have to figure this out now I guess 😅

bbrinck16:06:35

@lilactown Do you mean that for a certain value, you want to keep the old value of the key?

bbrinck16:06:47

“old value for the key” I mean

bbrinck16:06:38

Hm, I think this is possible. let me try something

bbrinck17:06:02

@lilactown would something like this work?

(def m {:a 1 :b ""})
user=> (walk/prewalk (fn [x]
  #_=>                 (if (map-entry? x)
  #_=>                   (let [[k v] x]
  #_=>                     (if (= :b k) x [k (inc v)]))
  #_=>                   x))
  #_=>               m)
{:a 2, :b ""}

lilactown17:06:56

nah the problem is that the previous value is also a tree

lilactown17:06:09

so prewalk recurses down into it when I don’t want to

bbrinck17:06:32

oh, i see, you want to stop everything at the top level when you see a key

lilactown17:06:08

I figured out that what I actually wanted was for it to ONLY traverse down into a single key

lilactown17:06:20

ignore trees at other keys

lilactown17:06:45

I think I figured it out

(defn- analyze-tree [tree]
  (println (first tree))
  (if (= (first tree) :children)
    (walk/walk
     analyze
     identity
     (make-node tree))
    (make-node tree)))

(defn analyze [tree]
  (analyze-tree (make-node tree)))

lilactown17:06:25

actually I don’t think I need the walk in analyze even

lilactown17:06:13

yeah just (analyze-tree (make-node tree)) 😄

lilactown17:06:25

nvm that didn’t work lol

zilti17:06:46

Has anyone ever attempted to pack an uberjar with javapackager? I'm trying it right now, but all I ever get is "Exception: java.lang.Exception: Error: Modules are not allowed in srcfiles: [target/videocapture.jar]."

xiongtx18:06:51

Getting some unexpected behavior when trying to capture output from clojure.tools.logging:

(deftest log-test
  (is (re-find #"foo" (with-out-str
                        (clojure.tools.logging/info "foo")))))
gives output:
11:25:04.447 [main] INFO <ns> - foo

expected: (re-find #"foo" (with-out-str (clojure.tools.logging/info "foo")))
  actual: (not (re-find #"foo" ""))
It seems that the output is being printed to *out* despite the use of with-out-str.

noisesmith18:06:03

it is probably ignoring *out* which is what with-out-str redefines, and using system.out directly

noisesmith19:06:39

@xiongtx the more I consider it, tools.logging is wrapping java loggers which wouldn't know about or use *out* at all

xiongtx22:06:04

You're right. I used the following to instead of with-out-str and it worked:

(defmacro with-system-out-str
  [& body]
  `(let [out# System/out
         baos# (ByteArrayOutputStream.)]
     (System/setOut (PrintStream. baos#))
     ~@body
     (.flush System/out)
     (System/setOut out#)
     (str baos#)))

noisesmith22:06:51

glad I could help

noisesmith22:06:02

you could also do this with logging config, though that seems fine enough

noisesmith19:06:21

*out* itself wraps System/out which a logger would use directly unless you configured it to do something else

benzap19:06:36

Anyone familiar with the gotchas of AOT compilation of clojure code? I'm experiencing an error something akin to to java.lang.ClassCastException: Foo cannot be cast to IFoo, where IFoo is a protocol that Foo implements. Anyone know why this would cause an error in AOT compilation, but not in regular operation?

noisesmith20:06:44

this can happen when you reload the file defining IFoo but not the file defining Foo

noisesmith20:06:01

protocols / records are a bit fragile for iterative repl based stuff

the2bears19:06:05

Hmmm... since we're on the topic of logging. Given a Storm topology that's using Log4J (default) through tools.logging with no additional properties set... I assume it's writing to the file system. Seems like a crazy question, but is there a chance it's not async IO?

the2bears19:06:14

Vague description not withstanding 🙂

the2bears19:06:57

@benzap the advice I've always seen has been to avoid AOT.

benzap19:06:37

I'd like to, but i'm trying to see if I can get my project to native compile using graalvm, first step is to get it AoT 🙂

👍 4
benzap19:06:13

I'm wondering if it has anything to do with the Foo record obtaining more than it's defined structure of values

benzap19:06:55

ie. (defrecord Foo [x y]) (def x (->Foo 1 2)) (class x) ;; Foo (class (assoc x :z 3)) ;; Foo This appears to be Foo still, but doesn't the inclusion of the additional key-value cause it to convert to a hashmap?

hiredman19:06:40

if you dissoc one of the defined keys like :x it will become a map though

the2bears19:06:40

I think it only converts once a required key is removed.

hiredman19:06:25

@benzap re: aot that kind of issue is very common if you have correct namespace dependencies

hiredman19:06:08

e.g. you import a type created using defrecord, but don't require the clojure namespace where it is defined, or something similar

hiredman19:06:38

you may also want to make sure you don't have stale class files sitting around

hiredman19:06:30

(aot compilation is the worst, I doubt the native code from graalvm will offset that)

benzap19:06:34

not sure i'm following, you're supposed to import the record type?

hiredman19:06:57

you aren't, but people do, and it causes similar kinds of weird errors sometimes

benzap19:06:07

ah interesting

hiredman19:06:23

is the interface a definterface?

benzap19:06:35

no, it's just a standard defprotocol

hiredman19:06:59

are the defrecord and the protocol in the same namespace?

benzap19:06:37

no, I have them separated out, with the record and extension in foo.impl.bar, and the protocol in foo.bar

hiredman19:06:42

if not, how are you importing/requiring the protocol in the namespace where the defrecord is defined?

benzap20:06:01

so the protocol is defined in fif.stack-machine, and the implementation is fif.impl.stack-machine

benzap20:06:31

a class exception in seen the moment the protocol touches the record

hiredman20:06:34

when was the last time you cleared out the aot generated class files?

hiredman20:06:59

and how are you aot compiling?

benzap20:06:59

probably never 🙂

hiredman20:06:50

you may have some weirdness there, the place you would need to delete them from will depend on whatever tooling you are using

benzap20:06:37

i'm using lein uberjar, i'm trying to follow along to this: https://github.com/borkdude/cljtree-graalvm

benzap20:06:01

i'll try doing a clean

hiredman20:06:49

are you sure you are actually aot compiling?

benzap20:06:06

yeah, i've included the :uberjar profile with :aot :all

hiredman20:06:15

I don't see :gen-class in any of the namespaces

benzap20:06:32

hmm, I assumed :aot :all ignored that?

hiredman20:06:09

if I recall that just tells lein to call clojure.core/compile on every namespace it finds in source

hiredman20:06:30

do you have class files being generated in target/ ?

benzap20:06:39

But wouldn't that mean I should be running into fewer issues?

benzap20:06:16

yeah, there's several *.class files in target/classes/fif

hiredman20:06:42

how many though? there should be at least one for each namespace

benzap20:06:55

it just fails to generate fif.core line 24

hiredman20:06:17

so aot compilation is failing there, not actually trying to run the program?

hiredman20:06:37

I bet you have a file somewhere in src that tries to run your code, and :all is loading that

benzap20:06:51

it fails when I run lein uberjar. I should have been more clear

hiredman20:06:53

because you shouldn't be actually running stuff at compile time

hiredman20:06:49

oh of course, right, and that isn't a def, so you are calling the protocol method while building

hiredman20:06:06

lein's aot :all definitely will break that

benzap20:06:23

That is correct, the fif.core compilation step fails on the class exception between the record Stackmachine and the protocol IStackmachine

hiredman20:06:29

because it calls compile in sort of arbitrary order, and may ever try to reload-all

benzap20:06:30

It's unclear why

hiredman20:06:11

so what is happening is the file that defines the protocol is getting reloaded after the record defining file is loaded

hiredman20:06:48

adding the :gen-class stuff may fix it, because you will get stable classfiles written to disk

benzap20:06:51

Is this C programming? Where do I put the #include?

seancorfield20:06:59

https://github.com/benzap/fif/blob/master/src/fif/core.cljc#L18-L26 <-- isn't this def going to cause all that code to run at load time?

hiredman20:06:14

there is a reason I always tell people not to aot

hiredman20:06:05

you will eventually run in to a weird crazy error that will suck the life out of you

hiredman20:06:46

it may also fix it if you don't :aot :all and just pass in your main namespace there

benzap20:06:57

@seancorfield the default-stack is a stack-machine instantiated at compile time

seancorfield20:06:40

So "yes" is the answer to my question 🙂 ... which seems ... suspect.

benzap20:06:22

I feel like if I can't get :aot :all to work, it might not be feasible to generate a native-image. Still not clear on whether what i've wrote can even be native 🙂

hiredman20:06:22

aot compilation is transitive, so if you load all you namespaces from your main namespace, or any namespace loaded by you main namespace, and so on, then it will be aot compiled

hiredman20:06:13

:aot :all is just a lein shortcut, which doesn't know anything about namespace dependencies in your project, so it may compile them in the wrong order, and recompile them after they have already been compiled

benzap20:06:49

So would I be able to get something to the similar effect if I use :gen-class everywhere, and do an :aot [fif.core] in the leiningen profile?

benzap20:06:52

Also, given that it's a leiningen shortcut, is there a better tool for doing this sort of AOT compilation, and is it entirely necessary

hiredman20:06:26

I dunno if it is necessary for whatever you are doing with nativeimage, it usually is not necessary for people not doing that

hiredman20:06:09

there potentially could be a better tool, if lein used tools.namespace to get an idea of the namespace dependencies and compiled them order

benzap20:06:01

Alright, tried it with just fif-core, and included a :gen-class and additionally a :main to fif.core. Seemed to work

benzap20:06:28

just need to try native-image

benzap20:06:16

I forgot to mention that I had also included direct-linking, but I would assume this would cause runtime issues?

seancorfield20:06:45

It's fine if you're not rebinding things dynamically at runtime.

seancorfield20:06:15

I'm pretty sure we have direct linking enabled in production...

seancorfield20:06:32

Yup,

base_jvm_opts="-XX:-OmitStackTraceInFastThrow -Dclojure.compiler.direct-linking=true"
for all JARs we run in production (we do not AOT compile tho')

👍 4
hiredman20:06:10

(let [namespaces
      (clojure.tools.namespace.dependency/topo-sort
       (:clojure.tools.namespace.track/deps
        (clojure.tools.namespace.file/add-files
         {}
         (clojure.tools.namespace.find/find-sources-in-dir
          "src/"))))]
  (doseq [namespace namespaces]
    (compile namespace)))
might be the more correct version of lein's :aot :all

benzap20:06:49

Unrelated to clojure, but I ran out of memory using native-image.

benzap20:06:09

apparently 4gb isn't enough?!

gklijs21:06:29

Could it be some kind of recursion, maybe triggered by a macro. Just a wild idea, I don't have any experience with native image.

hiredman21:06:15

nativeimage is running on the bytecode, by which time all the macros have been expanded away

Philip Hale22:06:19

i read an issue a couple of weeks ago where someone got a native image to compile, but only after several hours and 20gb+ of RAM!

benzap02:06:49

Haha, I guess I lucked out then. I think the highest it went for me was around ~5gb. My VM was only set at 4gb, so I had to bump it up a bit

benzap02:06:56

It's actually kind of eerie how easy it was to build a native-image. It's working all kinds of magic in there...

hiredman21:06:59

I suspect youare effectively compiling your project + the jdk, which means you are compiling a huge project

mikerod21:06:27

> there is a reason I always tell people not to aot I think there are times when it is somewhat necessary to AOT. it is best if you don’t have to do it, but if you have a compelling reason, you have to fight through it.

mikerod21:06:56

(1) faster startup time may matter (2) don’t have to ship the raw source code clj files in final artifact (3) I think you have to for Android or something? (4) exposing statically loaded classes for Java (or similar) consumers (I guess (4) could be done by writing a Java class that you compile that JITs the rest of the clj underlying)

hiredman21:06:37

android is a whole other kettle of fish

hiredman21:06:50

similar to the naitveimage thing, anroid isn't a jvm, but has a tool that operates on jvm bytecode to generate bytecode for android (or native code for art or whatever)

👍 4
benzap21:06:07

Wow, it worked. It used about 4.6Gb at its peak when compiling

hiredman21:06:41

the better solution of course would be a compiler that targets the android bytecode/runtime de jour

👍 4
benzap21:06:13

I've worked with clojure-android

hiredman21:06:29

@mikerod it is a spectrum, and the further that slider gets from no aot to aot the more pain there is, with diminishing increments of functionality

benzap21:06:15

You're absolutely right, it would get compiled into a separate bytecode developed by google. I believe art is the successor to the previous version, which performed a lot more AoT

mikerod21:06:20

I think the struggle of clj aot is less of a struggle than building cljs with :advanced optimizations though

mikerod21:06:43

so if you can get through cljs advanced opts, tackling clj aot problems should be a breeze 😛

johnj21:06:24

:advanced is obligatory in a browser 😉

noisesmith21:06:31

what they have in common is that if you have them turned on during dev it's a series of small things that are fixable, but if you turn them on in a mature project you get a storm of failures

👍 8
noisesmith21:06:14

but aot is not strictly needed like advanced compilation - there's always a way around it

mikerod21:06:52

> but aot is not strictly needed like advanced compilation - there’s always a way around it I think it may be “strictly” needed if you need to squeeze every ounce of startup time perf you can out of a large app?

noisesmith21:06:18

the time savings to effort is pretty small though

mikerod21:06:22

Alternatively, if you are forbidden to give consumers are jar full of your clj source files (perhaps rarer?, but legal-stuff at a company perhaps)

mikerod21:06:50

(obviously, people can always decompile the bytecode, but it’s pretty obfuscated)

mikerod21:06:55

@hiredman yeah, I’m aware of that one too.

mikerod21:06:17

which is a less obfuscated way to do it.

benzap21:06:01

Or you could use native-image to generate a single-executable 🙂

hiredman21:06:17

just saying, if anyone has a jar with some aot compiled clojure in and want to take a peek

mikerod21:06:22

I don’t know the legal-side discussions of this, so won’t pretend to. I believe in intellij and/or eclipse it has presented me with warnings about ensuring that it is “legal” to decompile the bytecode before it performs the action though

mikerod21:06:50

so I was thinking, perhaps decompiling things is a different category than giving people jars with sources in them directly

benzap21:06:04

It isn't illegal to debug a program by looking at the decompiled code.

hiredman21:06:10

I would be shocked if nativeimage ever catches on

benzap21:06:12

Downloading a car on the other hand. Don't do it

dpsutton21:06:28

i wonder if its a reminder to don't look at things that you shouldn't

mikerod21:06:29

anyways, doesn’t really matter. I’m just contemplating and thinking of reasons why AOT can still be a necessary feature at times

dpsutton21:06:39

like how kernel developers will never look at the released windows 95 source

dpsutton21:06:44

clean room development and all that jazz

genekim21:06:01

Hello, all! I’m a little baffled by a problem I’m facing using Clojure future — I’m using it to fire off a TCP message to HostedGraphite (it’s a hosted Grafana service). (PS: I love the service, although it can get spendy w/lots of metrics… http://hostedgraphite.com/) I’m sending a message to HostedGraphite to graph / collect when certain Ring endpoints are hit, using a Future so that the main thread doesn’t have to wait for the TCP message send to complete. I’m posting the little function I wrote below. It works great… messages are sent and received successfully about 40 times… then it looks like the futures stop firing, because println messages stop, TCP messages stop being sent/received. Am I using futures incorrectly? I never dereference the future return value, but I didn’t think that mattered? (i.e., I suspect I’m exhausting some resource, like threads or something associated with futures??) Many thanks in advance for any pointers, or feedback of any kind! PS: I can’t believe how much fun I’m having programming again, because of Clojure. Thanks so much to this entire community! 🙂

(ns trello-workflow.hosted-graphite
  (:require [clj-tcp.client :as tcp]
            [clojure.spec.alpha :as s]
            [clojure.core.async :as async]))

(def hg-key "xxx")

(defn- gen-key [s]
  (let [full-metric (format "%s.%s" "trello-workflow" s)]
    full-metric))

(defn send-metric
  " input: metric-name val "
  [metric-name n]
  {:pre [(s/valid? string? metric-name)
         (s/valid? int? n)]}
  (let [hg-payload (format "%s.%s %d\n"
                           hg-key
                           (gen-key metric-name)
                           n)]
    (future
      (let [h (tcp/client "" 2003 {})]
        ;(println hg-payload)
        (println (format "send-metric: %s %d" metric-name n))
        (do
          (tcp/write! h (.getBytes hg-payload))
          (tcp/close-all h))))))

genekim21:06:38

….just trying to think this through.. I’m using clj-tcp.client, which is asynchronous… Inside the future, I create the clj-tcp.client, send the message, and close the socket, which if I understand correctly, kills the thread… And since no one derefs the future value, everything with the future should be gone, eligible for GC, etc… Is my understanding correct? Thx!!!

hiredman21:06:52

there is a lot to unpack there

hiredman21:06:52

closing a socket doesn't kill a thread, and futures use a threadpool so once the future is finished running, the threadpool will be returned to the pool, not killed

hiredman21:06:52

it doesn't matter if you deref the future or not, it matters if you keep a reference to the future

hiredman21:06:43

but, garbage generated in the future will be eligible for garbage collection when there are no more references to it, regardless of if the future is still running or not

noisesmith21:06:59

the one gotcha about not derefing a future is that futures leave exceptions for when you access them, so not derefing a future can mean an exception is never seen

genekim21:06:55

Thx so much @hiredman @noisesmith ... sorry for mangling terminology. I’m baffled by why the code block inside the future stops running after, say, 30 times... Based on what you said, threads should complete and go back to thread pool, there are no derefs... So, what could be going wrong? Thx!!

hiredman21:06:18

why wouldn't it stop?

genekim22:06:04

I want it to run every time a Ring handler is run. I put it inside future so that these calls are run in parallel, to let rest of Ring handler code run. I’d expect it to be able to run to completion every time, not stop after 30 times. :)

hiredman22:06:37

is it throwing and exception?

noisesmith22:06:54

you wouldn't know since the future is never dereffed, and there's no try/catch

noisesmith22:06:28

justin.smith@C02RW05WFVH6: ~$ clj
Clojure 1.9.0
(ins)user=> (def f (future (/ 1 0)))
#'user/f
(ins)user=> (System/exit 0)
justin.smith@C02RW05WFVH6: ~$ 
we all know that blew up, but there's no evidence

genekim22:06:29

Oh... I’ll check tonight! Thx!

genekim22:06:34

@noisesmith Sorry. I had to jump in car to pick up my kids. That was meant to be pronounced, “Ooooooh....” Uncaught exception!!! That could explain it! Thx for the suggestion. I can’t wait to try it out when I get home in a couple of hours. Want to take a guess at what exception is being thrown? :). I’m drawing a blank! :) I’ll let you know!

genekim22:06:48

@noisesmith if threads have uncaught exceptions, that would explain behavior I’m seeing, right? Eventually, no threads left, defer’ed code never run, yes?

noisesmith22:06:33

it won't use up threads, it just means you never see the error messages

genekim22:06:33

Hmm... drats... well, more illumination will come when I deref those futures! :) Oddly excited to see what i find! :)

noisesmith22:06:32

another option is (future (try .... (catch Exception e (println e))))

noisesmith22:06:40

then you don't need to worry about the deref

seancorfield22:06:39

If you feel like using Timbre for logging, it has a logged-future macro that behaves just like future except it also logs any exceptions (using color-coded stacktraces! 🙂 )

👍 4
flefik23:06:49

Is it considered best/poor practice to spec private methods?

seancorfield00:06:49

@cfeckardt In general, you get the most benefit from spec'ing functions that are at the boundaries of your systems -- so private functions wouldn't fall under that.

seancorfield00:06:32

That said, if you have a private function that has some critical behavior you want to verify with generative testing then spec might be worthwhile.

seancorfield00:06:14

There's also a school of thought that thinks private functions are a waste of time and using namespaces to organize what is your "API" and what is your implementation is a better way to go.

flefik10:06:23

thanks, that's what i thought

seancorfield00:06:49

@cfeckardt In general, you get the most benefit from spec'ing functions that are at the boundaries of your systems -- so private functions wouldn't fall under that.