Fork me on GitHub
#clojure
<
2019-02-22
>
acim103:02:10

If I have an arbitary number of functions I want to run just for effects, is there a handy function for that? So rather than

(do
  (f1)
  (f2)
  (f3))
just (dofn f1 f2 f3)

slipset16:02:45

On a phone, so not tested, but (apply reduce (fn [_ f] (f)) f1 f2 f3)

slipset16:02:58

Should probably work.

didibus04:03:54

You can use run! or doseq for that.

didibus04:03:42

For example, you can do:

(run! #(%) [f1 f2 f3])
or
(doseq [f [f1 f2 f3]]
  (f))

mars0i03:02:03

@mkeller02 look at run! and juxt.

acim103:02:55

Will do @mars0i -- thank you

mars0i03:02:12

With run! you'd have to do something like this: (run! (fn [f] (f)) [(fn [] (println "yo")) (fn [] (println "ya"))]). juxt is probably better for what you want.

👍 5
gypsydave511:02:40

So... anyone seen this before?

Exception in thread "main" java.lang.ClassCastException: cm_metrics.main$_main$fn__8935 cannot be cast to java.base/java.lang.Thread
	at cm_metrics.main$_main.invokeStatic(main.clj:30)
	at cm_metrics.main$_main.doInvoke(main.clj:21)
	at clojure.lang.RestFn.invoke(RestFn.java:397)
	at clojure.lang.AFn.applyToHelper(AFn.java:152)
	at clojure.lang.RestFn.applyTo(RestFn.java:132)
	at cm_metrics.main.main(Unknown Source)

gypsydave511:02:17

it's from an uberjar

gypsydave511:02:07

happens when the -main function calls (Runtime/getRuntime) (I think)

sekao13:02:53

@gypsydave5 i dont know what your code looks like but i’m guessing you’re trying to add a shutdown hook? if so, the addShutdownHook method wants a Thread object not a function: (Thread. (fn [] ...))

gypsydave513:02:15

You're obviously a mind reader 😄

mbjarland14:02:22

I'm trying to get a clojure cli tool I wrote a while back to compile and run under java 6. So far I've downgraded leiningen and the clojure version and I'm now in the process of trying to find a permutation of the dependencies which would actually compile under java 6 (currently playing with tools.cli) . Just figured I would ping this channel to see if this is even worth it...i.e. is this doomed to fail from start or is there a chance this might actually work in the end?

mbjarland14:02:41

@alexmiller I'm trying to get the tool to run in a controlled server environment which only has java 6

bja14:02:13

Has anyone used a recent core.memoize with a cache backend out of process (like leveldb or redis where you need to serialize the objects)? Since I last used it, it seems to make heavier use of custom delay objects and I wondered if anyone else has encountered issues

Alex Miller (Clojure team)14:02:59

@mbjarland that's what I was asking. I don't think it's hopeless (but might not be fun)

mbjarland14:02:55

@alexmiller ok thanks! ...yeah, not fun which is why I figured I would ask first before banging my head against the wall

g16:02:24

hey everyone, this might be more of a java question but i’m looking to store a hex number, for example 1ff as a byte-array

g16:02:36

namely, 0x00 0x01 0xFF

g16:02:10

casting to and from bigintegers results in truncating the 1, and i’m not quite sure how to get around that

g16:02:12

any ideas?

g16:02:12

i guess the super hacky route would be to mod the number of hex characters against 8 and supplement zeroes..

Alex Miller (Clojure team)16:02:52

you might look at java.util.BitSet

Alex Miller (Clojure team)16:02:33

not sure if it's exactly what you want as an api but prob good code to build on

g17:02:04

cool, i’ll have a look - thanks

hiredman17:02:35

0x00 0x01 0xFF isn't a "hex number" it is 3 bytes, so when you try to store it as 1 byte it is truncated to 0xff

hiredman17:02:57

3 bytes is kind of an odd size to work with, usually it will be some power of two, 2 or 4 would fit, and match the sizes of some primitives on the jvm (short and int)

hiredman17:02:41

are you sure this isn't just a signedness thing tripping you up?

noisesmith17:02:50

if you want a byte array with the contents describing some Long / Int / whatever you can use ByteBuffer https://docs.oracle.com/javase/7/docs/api/java/nio/ByteBuffer.html#putInt(int)

hiredman17:02:42

bigintegers will add an extra byte to track the sign sometimes, where if you are mostly concerned with bytes but sometimes with numbers you often want to assume unsigned and not have the extra byte

noisesmith17:02:31

(cmd)user=> (def bb (ByteBuffer/allocate 16))
#'user/bb
(ins)user=> (seq (.array bb))
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
(ins)user=> (.putLong bb 42)
#object[java.nio.HeapByteBuffer 0x640f11a1 "java.nio.HeapByteBuffer[pos=8 lim=16 cap=16]"]
(cmd)user=> (seq (.array bb))
(0 0 0 0 0 0 0 42 0 0 0 0 0 0 0 0)
(ins)user=> (.putLong bb Long/MAX_VALUE)
#object[java.nio.HeapByteBuffer 0x640f11a1 "java.nio.HeapByteBuffer[pos=16 lim=16 cap=16]"]
(cmd)user=> (seq (.array bb))
(0 0 0 0 0 0 0 42 127 -1 -1 -1 -1 -1 -1 -1)

noisesmith17:02:45

this is the most straightforward way I've found for converting between the internal representation of primitive types and the actual values (for every putFoo method there's a getFoo method)

g17:02:53

i was using clojure’s byte-array

noisesmith17:02:47

so what was your actual task? turn a byte array into a number?

g17:02:07

no, i was trying to store a series of bits that may or may not fit neatly into bytes in a byte array

g17:02:21

so, right, it wasn’t really a ‘hex number’, i was using hex to specify a series of bits

noisesmith17:02:30

OK - you can write bits (or any other type of course...) one by one with ByteBuffer, then read out whatever types you like

hiredman17:02:46

putShort on a bytebuffer is almost certainly what you want

5
g17:02:45

yeah, i hadn’t thought about that

g17:02:53

i’ll dig into it and see what i can do. thanks guys, appreciate the help

butterguns19:02:18

Why isn't group-by "transducable"? Is it because it needs to scan the entire coll?

hiredman19:02:00

group-by is itself a reduce, not a transformation of a step function

hiredman19:02:19

you can do something similar as a transformation of a step function if you have input sorted by your grouping key and a transducer version of partition-by

Alex Miller (Clojure team)19:02:26

it can't produce any intermediate results, so not really any point

butterguns19:02:40

OK, so my group-by is in the middle of a long chain of transformations

butterguns19:02:03

my only option, it seems, is to have 2 transducers

noisesmith19:02:13

I could imagine a hypothetical case where a groups-by function might be useful, and that could be a transducer (reductions : reduce :: groups-by : group-by)

noisesmith19:02:15

reductions gives a lazy seq of all accumulator values for a given reducing function and input, so groups-by could give all the intermediate groups as you consume inputs - but there's few places that would be useful, if any

Alex Miller (Clojure team)19:02:20

have you considered what it means to have a group-by in the middle of a long chain of transformations?

hiredman19:02:14

https://github.com/aphyr/tesser has a group-by operation with slightly different semantics that is very interesting

hiredman19:02:02

it basically splits the step function to separate reduction steps for each group

butterguns19:02:04

@alexmiller My feeling is that it's not a single sequence of transformations. Rather, two, unrelated map-reduce piplines

butterguns19:02:39

@hiredman Thanks. My understanding of how transducers work is still very basic, so I'll have to read more about this

hiredman19:02:49

tesser has a slightly different model of folds then transducers does, but is very useful for inspiration, and speaking of map/reduce tesser can run the folds as hadoop jobs

hiredman20:02:22

(defn groupy-by-transducer [key-fun]
  (fn [f]
    (fn
      ([] (f))
      ([x] (into {} (map (fn [[k v]] [k (f v)])) x))
      ([accum value]
       (let [k (key-fun value)]
         (assoc accum k (f (get accum k (f)) value)))))))

(transduce
 (comp
  (groupy-by-transducer :type)
  (map :mass))
 +
 {}
 [{:name :electron, :type :lepton, :mass 0.51}
  {:name :muon,     :type :lepton, :mass 105.65}
  {:name :up,       :type :quark,  :mass 1.5}
  {:name :down,     :type :quark,  :mass 3.5}])

;;=> {:lepton 106.16000000000001, :quark 5.0}

hiredman20:02:49

that kind of works, but I think it might run afoul of the internal mutations that some transducers do

mpdairy21:02:38

I'm trying to add metadata to a function, but it's annoying because it erases the function name in the print-out:

binja.util> (fn hey ([x] x))
#function[binja.util/eval9096/hey--9097]
binja.util> (with-meta (fn hey ([x] x)) {:x "x"})
#function[clojure.lang.AFunction/1]

mpdairy21:02:49

does anyone know how I can add meta data without erasing the name?

hiredman21:02:44

I recommend not putting metadata on functions because they have reference equality but value metadata

mpdairy21:02:11

can't it be done "under the hood" somehow?

noisesmith21:02:00

you can implement IFn / IMeta and have any toString you like via reify, deftype, whatever

noisesmith21:02:37

or fork clojure, but implementing IFn and IMeta is a lot less work

hiredman21:02:50

what is printed there is the jvm type of the object

hiredman21:02:31

adding metadata to a function results in wrapping it with an instance of another function with that metadata

hiredman21:02:29

which is part of why (= x (with-meta x {})) which holds true for other types with-meta works on fails for functions

mpdairy21:02:17

hmm well that's disappointing. maybe i can figure out how to store the metadata inside the function and fetch it somehow when I need it

mpdairy21:02:48

it won't be very "meta" then

noisesmith21:02:15

cmd)user=> (def f (reify clojure.lang.IFn (invoke [this] :a) (applyTo [this _] :b) Object (toString [this] "f")))
#'user/f
(ins)user=> (def g (with-meta f {:foo :bar}))
#'user/g
(ins)user=> (meta g)
{:foo :bar}
(ins)user=> (meta f)
{:line 53, :column 8}
(ins)user=> (f)
:a
(ins)user=> (g)
:a
(ins)user=> [f, g]
[#object[user$reify__233 0x36676c1a "f"] #object[user$reify__233 0x5b408dc3 "f"]]

noisesmith21:02:33

(reify is already IMeta so no need to implement)

mpdairy22:02:33

thanks @noisesmith! But I mostly need it for stack trace errors so it needs to show up as a function, not just a reify

hiredman22:02:48

the original function will show up in the stacktrace

hiredman22:02:57

because the wrapper just calls it

hiredman22:02:04

but, again, metadata on functions is kind of silly, metadata is more or less extra data you want to attach to an object without changing the equality of that object

hiredman22:02:16

but I doubt you are comparing equality on a functions

hiredman22:02:30

so use a map

mpdairy22:02:39

b.util > (def f (reify clojure.lang.IFn (invoke [this] :a) (applyTo [this _] :b) Object (toString [this] "f")))
#'b.util/f
b.util> (f 3)
Execution error (AbstractMethodError) at b.util/eval9131 (form-init14739800243932352027.clj:2461).
Receiver class b.util$reify__9129 does not define or inherit an implementation of the resolved method abstract invoke(Ljava/lang/Object;)Ljava/lang/Object; of interface clojure.lang.IFn.

hiredman22:02:52

{:my other-data :f some-function}

noisesmith22:02:58

you need to define the arity you call

noisesmith22:02:10

you only defined the 0 arg applyto invoke

hiredman22:02:35

just don't put metadata on functions

mpdairy22:02:05

well i'm storing the type information of the args and return value for a function, which I can check with a spec, so I just need some way to get that out of a function. i could hide it in the function so if you call it with the magic incantation it returns the type

mpdairy22:02:34

but the only arity I consistently have available is zero args, so I could have that return the type information, but that will result in sloppy errors

mpdairy22:02:24

Oooh I see what you were saying @hireman. My function names do show up in the stack trace, just not when i print out the function. that'll work!