Fork me on GitHub
#beginners
<
2018-12-05
>
mario.cordova.86203:12:23

Does there exist a mapping function where I can pass through a variable to the next iteration?

mario.cordova.86203:12:45

Sort of like an accumulator in reduce

trailcapital03:12:29

can you give an example of what you mean @mario.cordova.862

mario.cordova.86203:12:39

Lets say I have a collection of maps ({:a "hello" :b true} {:a nil :b false} {:a "world" :b true} {:a nil :b true}) I want to map over this collection starting from this first one. And say "If the value of :a is not nil then set the "accumulator" to "hello". Then on the next iteration I want to be able to use that accumulator to set that :a in the second map to "hello".

mario.cordova.86203:12:02

Hope that makes sense. I am using reductions at the moment to get a similar effect but can't get it working

mario.cordova.86203:12:32

Id like to be able to change the value of the accumulator in the mapping function and have it persist on to the next iteration.

mario.cordova.86204:12:30

Got it working with reductions ๐Ÿ˜…

gayathrik05:12:09

@mario.cordova.862 can you post the code over here ๐Ÿ™‚ Just learnt about "partial" from a discussion here.

mario.cordova.86205:12:32

(reductions (fn [prev-map line]
                    {:date (line :date)
                     :time (line :time)
                     :verb (line :verb)
                     :guard (or (line :guard) (prev-map :guard))}) {} (sort-input))

gayathrik05:12:45

hopefully will get reductions based on the problem posted here and your solution

sreekumar.av08:12:03

Hi Folks, Can I use Clojure for writing a library that can easily (read without glue code) be used by an Android app?

cbowdon09:12:56

I think you can generate classes that can be consumed from Java. Not sure if there is a performance penalty though.

anna.railton10:12:01

Hi everyone, wonder if someone could help me out with this. โ€ข I've got a sequence of integers which I cycle through infinitely โ€ข I calculate the accumulated list of these, e.g. [1 2 3 4] -> [1 3 6 10] in accumulate-lazy โ€ข I work through this lazy accumulated list and return the first duplicate I find. The problem Works fine for when first-duplicate only has to look at small amounts of accumulated list, get StackOverflow error for longer input:

user=> (first-duplicate (accumulate-lazy inf-freq-list))
(first-duplicate (accumulate-lazy inf-freq-list))

StackOverflowError   clojure.lang.PersistentHashMap$BitmapIndexedNode.index (PersistentHashMap.java:677)

user=> (first-duplicate (take 10000 (accumulate-lazy inf-freq-list)))
(first-duplicate (take 10000 (accumulate-lazy inf-freq-list)))

StackOverflowError   clojure.lang.RT.nthFrom (RT.java:912)
What have I screwed up?

anna.railton10:12:50

Actual breaking output:

clojure
(def freq-list [-14 15 9 19 18 14 14 -18 15 4 -18 -20 -2 17 16 -7 -3 5 1-5 -11 -1 -6 -20 1 1 4 18 5 -20 -10 18 5 -4 -5 -18 9 6 1 -19 13 10 -22 -11 -14 -17 -10 -1 -13 6 -17 9 -11 -6 3 -14 4 -14 15 7 15 -1 -4 9 10 6 -9 8 3 10 -14 8 22 19 -15 10 -6 9 -4 2 11 -15 14 11 -14 2 7 9 -22 -6 -3 -21 10 5 -20 19 -3 -13 5 -16 12 10 -12 -4 12 2 -1 -4 -8 -6 -3 -8 -8 7 -10 -20 -12 -6 -17 2 20 7 -17 -14 -8 3 11 15 -4 -10 -16 -3 -19 -6 -3 -9 -16 -7 -16 -12 11 -13 -6 13 4 -7 -3 5 12 4 -19 -19 7 1 -7 -12 -7 -5 -17 18 3 10 15 -12 -8 17 9 19 4 16 -11 10 -6 -7 6 6 19 3 8 -2 12 19 -17 7 -2 -10 -16 -11 -1 -2 -17 -7 22 3 8 11 -12 -4 3 19 -16 18 -12 -17 -8 5 -11 7 -12 -23 -2 14 8 9 3 29 16 -15 -2 -12 24 14 2 4 -2 18 -9 -18 8 -9 4 7 19 7 10 -9 -14 -6 5 17 -14 7 2 -1 11 18 -2 -6 19 -15 -18 -5 12 21 2 2 -5 10 8 -10 1 -17 6 21 18 -17 7 -5 43 -4 50 -4 9 16 -12 9 10 -21 3 25 -9 18 17 -19 5 16 -8 -17 16 21 -16 6 12 -9 19 -7 4 -8 7 18 17 -7 11 21 -6 4 -17 -19 18 13 -17 2 4 -8 9 22 2 5 13 6 3 -28 -17 -17 -6 19 -16 -19 11 -7 6 -17 3 -4 -7 -6 10 17 -7 -4 2 8 -7 -25 15 -10 -18 -10 -10 -11 -11 14 14 -3 21 10 -11 6 11 21 -25 -9 -14 2 16 -27 4 -7 6 -11 29 21 21 6 -10 14 17 6 -13 32 -23 17 -1 12 -6 -11 27 -5 19 -2 -21 -42 -21 19 -64 20 -38 42 -23 1 -29 26 -4 57 6 -1 31 107 -9 5 19 -12 20 -1 30 1 2 -47 -38 5 13 121 69 -60 57143 -13 -3 13 15 1 -19 -11 2 -14 4 15 8 -7 16 3 -5 -16 17 -18 5 -7 17 11 12 -2 16 -17 6 12 -9 -16 -5 9 18 15 -19 16 16 -9 5 -18 19 -17 -18 9 11 7 -3 -7 -6 3 -17 -4 6 4 2 10 -13 -21 10 -17 15 1 -10 -13 20 -14 21 13 16 5 4 19 9 12 4 -10 14 -6 17 -9 -14 3 16 6 13 6 -15 14 11 -1 8 -4 11 5 -7 -19 -2 14 18 -14 2 18 16 -3 16 7 1 -12 -5 -18 8 11 -7 -8 -19 14 -10 -7 4 9 -18 -2 19 21 9 1 -9 4 -3 4 -11 -17 10 -2 6 -20 3 15 4 19 19 -14 3 1 4 12 11 -8 12 18 -14 2 -4 19 -1 4 15 13 16 7 -12 10 -13 -3 -17 19 -16 5 -20 -15 3 18 4 3 3 14 -12 8 11 7 10 13 -7 5 -19 12 -3 18 -9 7 1 18 17 -6 1 -15 10 -5 -10 -6 18 16 6 5 -10 -8 -5 7 -1 -4 17 1 4 -8 1 11 -13 -18 -14 5 3 19 12 8 -3 5 -24 3 -10 -20 -1 10 -19 6 8 -3 -10 15 -8 14 -17 -17 4 1 -12 13 -11 4 12 -25 -1 -8 14 -23 -21 17 -20 2 -5 -1 12 -10 3 17 4 -15 20 -1 9 23 -20 -22 -12 -10 -15 11 -35 -12 -14 16 -10 -19 26 12 16 -8 -15 19 3 -12 -16 10 2 -1 13 -7 2 12 9 -1 2 -21 3 -1 13 7 2 3 18 -32 4 43 19 -3 -5 20 -2 -2 17 61 2 -5 23 -3 8 -12 55 12 12 6 20 -6 7 15 3 11 8 -4 -9 -19 -13 -4 6 -10 -9 -5 -4 11 5 15 -14 2 6 -22 2 18 -11 -2 -4 -16 -5 12 4 20 8 18 -6 15 17 -11 16 -13 -12 1 -9 3 16 4 19 9 -8 -7 -12 11 21 -11 -16 8 1 25 15 -9 -42 12 -18 -6 -10 -8 -5 -23 -20 14 12 23 120 19 -43 9 -1 -1 -33 6 43 -103 -187 9 3 -82 -20 -7 125 72 613 56596 -18 -19 12 -11 -19 1 19 -18 -6 -3 16 -2 -10 -9 -13 -15 16 5 -15 7 -8 -17 7 -16 14 14 -15 4 -15 -10 -9 12 -15 5 -1 13 -15 -3 -19 1 -6 -19 -19 23 -25 -28 -10 -3 -21 -13 -14 11 18 -13 1 -16 14 15 5 -7 10 2 -14 10 11 11 -6 -7 -4 14 -12 -6 -3 -15 -10 -13 -17 3 2 8 18 -4 -11 2 -12 13 -9 14 17 12 1 -12 -14 6 5 -13 20 -13 -22 3 15 14 17 7 -22 -22 -14 -2 -20 15 -3 -8 13 40 72 -21 25 -16 11 22 22 25 16 15 15 9 12 -10 9 11 -1 10 -16 17 8 -7 -17 4 -11 20 10 7 6 -7 8 -19 23 7 -1 -2 17 -9 -3 2 -10 -10 -23 17 7 10 6 14 12 -1 3 -7 -3 16 -11 17 -7 -1 -3 -12 -114806])

jumar10:12:52

@ I think your problem is actualy in the first-duplicate function. Clojure doesn't optimize tail recursion so you'll get StackOverflowError after thousands of iterations. Using loop-recur should solve it - something like:

(defn accumulate-lazy
  "Takes in a lazy sequence and lazily produces the accumulated output."
  ([coll] (accumulate-lazy 0 coll))
  ([sum coll]
   (when-let [[x & xs] (seq coll)]
     (let [new-sum (+ sum x)]
       (cons new-sum (lazy-seq (accumulate-lazy new-sum xs)))))))

(defn first-duplicate
  "Returns the first duplicate in a sequence"
  ([coll] (first-duplicate coll #{}))
  ([coll accumulator]
   (loop [[x & xs] (seq coll)
          acc accumulator]
     (when x
       (if (contains? acc x)
         x
         (recur xs (conj acc x)))))))

(first-duplicate (accumulate-lazy (cycle [-14 1 10 4])))
;; => -13
(first-duplicate (accumulate-lazy (cycle (range 10000))))
;; => 49995000

anna.railton10:12:10

Awesome, thanks so much @. Is a good rule of thumb "if you need thousands of recursions, use loop-recur instead of direct recursion"?

jaihindh.reddy10:12:35

BTW Anna, instead of your accumulate-lazy, you can just say (reductions + 0 (cycle [-14 1 10 4]))

jaihindh.reddy10:12:14

reductions is just like reduce except instead of the final state it returns a sequence of states at each step

jumar10:12:23

This almost looks like the 4clojure exercise ๐Ÿ™‚ http://www.4clojure.com/problem/solutions/60

jaihindh.reddy10:12:00

Also, loop and recur are pretty low level, and you can use higher level constructs here. For instance, your first-duplicate can be implemented as (reduce (fn [seen? v] (if (seen? v) (reduced v) (conj seen? v))) #{} coll)

jaihindh.reddy10:12:06

Putting those two together, it becomes

(->> (cycle input)
     (reductions + 0)
     (reduce (fn [seen? v]
               (if (seen? v)
                 (reduced v)
                 (conj seen? v)))
             #{}))

anna.railton10:12:40

Thanks for much for this guys, got a fair amount to study in here!

anna.railton11:12:05

You would also not believe how long it took to come up with that damn accumulate function, then there's a one liner with reductions :exploding_head:

dpsutton12:12:34

You can do a self tail call with recur. People mentioned loop and recur but you can also recur "against" invoking the same function

iain.tatch_clojure.sl11:12:23

Hi, I'm sure this is really simple but I can't quite work out how to do it. I want to take a vector and have each item in it become the key of a hashmap with the value being an incrementing counter. so I start with ["foo" "bar" "xyzzy"] and I'd like the output to be {"foo" 1 "bar" 2 "xyzzy" 3}

iain.tatch_clojure.sl11:12:05

Every idea I have ends up with an atom to store the value as it increments but I'm sure there's a nicer way to do that

hybas312:12:00

@iain.tatch_clojure.sl (let [ks ["foo" "bar" "xyzzy"]] (zipmap ks (range 1 (inc (count ks)))))

iain.tatch_clojure.sl12:12:49

ahhhh zipmap might be the function that I failed to find in the docs

iain.tatch_clojure.sl12:12:12

in fact (zipmap ks (range)) is good enough for my use-case

hybas312:12:16

Actually you don't need to count the vector of keys, you can use laziness because zipmap will stop as soon as one of it's arguments is exhausted. i.e. (rest (range)) instead of (range 1 (inc count ks)).

denisyagovkin12:12:04

ะŸั€ะธะฒะตั‚.

nikola.kasev12:12:27

in order to group by items in a list I do (group-by (fn [[x y]] (str x ":" y)) [[1 2] [1 2] [3 4] [5 6] [1 2] [5 6] [5 7]]). Is there a better way to achieve this?

nikola.kasev12:12:46

I'm interested in occurrences of more than once, so I am planning to then filter out groups with count = 1

nikola.kasev12:12:12

so I only want [1 2] and [5 6]

gon13:12:12

frequencies too can be useful

gon13:12:27

(frequencies [[1 2] [1 2] [3 4] [5 6] [1 2] [5 6] [5 7]])
{[1 2] 3, [3 4] 1, [5 6] 2, [5 7] 1}

nikola.kasev13:12:11

of course, thanks @gon

nikola.kasev13:12:55

I've just discovered identity, so I can rewrite the anonymous function to #(identity %)

jaihindh.reddy13:12:10

#(identity %) is the same as (fn [x] x) is the same as identity

nikola.kasev13:12:18

how can I create a matrix from a list [[1 2] [1 3] [1 4]] and a range from 1 to 3, where I sum a value from the range with the first item in a pair?

jumar14:12:10

You could use sth. like this:

(defn to-matrix [row matrix-range] (for [dx matrix-range [x y] row] [(+ x dx) y]))

(to-matrix [[1 2] [1 3] [1 4]] (range 0 4))
;;=> ([1 2] [1 3] [1 4] [2 2] [2 3] [2 4] [3 2] [3 3] [3 4] [4 2] ...)

nikola.kasev13:12:41

the result should be [[1 2] [1 3] [1 4] [2 2] [2 3] [2 4] [3 2] [3 3] [3 4] [4 2] [4 3] [4 4]]

denisyagovkin14:12:22

ะšั‚ะพ ะผะพะถะตั‚ ะฟะพ garden ะฟะพะดัะบะฐะทะฐั‚ัŒ ?

grant87214:12:58

if i am using reify, how do i specify a paramaterized type, like Map<String,String> ?

alexmiller14:12:08

ignore the types :)

alexmiller14:12:14

itโ€™s just a Map

alexmiller14:12:33

the JVM actually erases the generic types anyways - those are a Java thing, not a JVM thing

alexmiller14:12:53

and actually Clojure maps implement Map, so you can just use a Clojure map

saikyun14:12:20

maybe the question is better suited here: is there a way to get lein repl working in an offline environment? I'm having a hard time getting it to load the reps locally, instead of trying to connect to maven

grant87214:12:45

so.... lets say for example, im passing a callback to a java function, which is supposed to implement Handler<AsyncResult>, i can just ignore the AsyncResult bit?

mateus.pimentel.w15:12:05

There is some video showing how is the workflow people usually have when working with the best tools for clojure?

mateus.pimentel.w15:12:13

like, showing how they use repl, do tests, etc

quieterkali15:12:16

hey guys, what does coll means in clojure documentation?

quieterkali15:12:26

thank you @shaun-mahood ๐Ÿ™‚

quieterkali15:12:48

now that you said, make sense hahaha

quieterkali15:12:29

another one question

quieterkali15:12:26

from https://clojure.org/reference/data_structures#Collections, they said: All of the Clojure collections are immutable and persistent.

quieterkali15:12:53

to be immutable, it doesn't have to be persistent first?

quieterkali16:12:25

what does they mean by persistent here?

shaun-mahood16:12:45

@quieterkali https://github.com/bbatsov/clojure-style-guide#naming is a good reference for things like coll (scroll down a bit and you will see coll listed), as well as the Clojure Do's and Don'ts on https://stuartsierra.com/, and https://leanpub.com/elementsofclojure

arne-clojurians16:12:03

is anybody doing the advent of code in clojure?

jumar16:12:45

@ there's #adventofcode channel

arne-clojurians16:12:39

I'm somehow receiving a StackOverflowError with today's puzzle and i'm not sure why

shaun-mahood16:12:09

@quieterkali http://www.hypirion.com/musings/understanding-persistent-vector-pt-1 is a pretty good detailed explanation - lots of detail so hopefully it covers what you are wondering. The naive way to do immutability without persistent data structures would likely be to create a full copy every time, and it seems to me like immutable data structures for practical work didn't really take off until Rich implemented them persistently (based on work by Chris Okasaki and Phil Bagwell if I remember correctly)

quieterkali16:12:37

@shaun-mahood thanks for those links. taking a look to them right now ๐Ÿ™‚

shaun-mahood16:12:16

@quieterkali: You don't need much of an understanding of them to use Clojure successfully, so definitely don't think it's required reading - but it's nice to know where to look if you are interested.

quieterkali16:12:51

i understand what you mean. But i really want to get a good understanding of the ecosystem, that will make my life easier in the future hehehe

noisesmith16:12:50

The simplest example of a persistent data structure is a linked list. You can make a new list with a new element at the front, by pushing a new element to the front of an existing list. If the lists are immutable, this is safe and you don't need to copy the first list - they can share most of their structure and co-exist.

noisesmith16:12:19

vectors are fascinating, but not nearly as simple an illustration of what persistence does

quieterkali17:12:48

thanks @noisesmith ๐Ÿ™‚

andy.fingerhut17:12:49

Part of Rich Hickey's goal in writing Clojure's immutable data structure implementations were: both the original version before the "update" operation, and the "updated" version, are both available after every operation. Both the old and new continue to have the same performance characteristics for reading and updating that the original does (usually O(log n) with base 32 log for the constant factor).

andy.fingerhut17:12:53

There are other immutable data structures where the old version is no longer accessible after an "update" operation is performed (I think -- not 100% sure about that statement), or where the old version has worse performance for accessing it.

d.ian.b17:12:54

how can I transform nil to "" without if's?

d.ian.b17:12:56

I'm searching a key in a map, if it's nil, give me ""

noisesmith17:12:30

@d.ian.b do you expect strings for the non-nil results?

noisesmith17:12:48

returns "" for nil, the string for any string

d.ian.b17:12:06

aaw, I forgot that

d.ian.b17:12:42

It was exactly what I was searching for haha

noisesmith18:12:01

if some values aren't strings, this usage of replace can be adapted

user=> (replace {nil ""} [:a nil :b])
[:a "" :b]

noisesmith18:12:39

(there's many places in my own code where it would have been improved by using replace instead of map...)

d.ian.b18:12:13

yeah, it seems very useful

noisesmith18:12:22

another useful function for nil handling is fnil which makes a new function with a hard-coded substitute for nil

noisesmith18:12:35

or, more comprehensively, substitutions plural

user=> (map (fnil + 1 2) [1 nil 2 nil 3] [2 nil nil 3 nil])
(3 3 4 4 5)

d.ian.b18:12:46

And a request that gives me a nil or a map?

d.ian.b18:12:54

to give me a {}

noisesmith18:12:34

there's a few ways to do it, but (replace {nil {}} coll} if you have a coll with multiple results

noisesmith18:12:02

or (into {} c) to get {} for nil and the same map back for a map

d.ian.b18:12:38

Thanks ๐Ÿ˜ƒ

d.ian.b18:12:49

I was reading the clojure cookbook but I forgot

noisesmith18:12:18

also, or works with nil - so (or c {})

d.ian.b18:12:01

nil's are some beats to treat, anything goes wrong and you'll have a damn NPE

alexmiller19:12:35

actually, Clojure is aggressively polymorphic wrt nils and usually itโ€™s not wrong to lean on them

alexmiller19:12:47

Java interop is one general exception to that

alexmiller19:12:28

and I would say that taking care to avoid having maps with nil values (and instead just omit the values) is a worthy goal that makes everything easier down the line

kyselyradek21:12:12

Is there a convention for naming constants? (like earmuffs for dynamic vars)

shaun-mahood21:12:29

https://github.com/bbatsov/clojure-style-guide#dont-flag-constants Not sure if this is always followed, but I've not seen anything in the wild that would make me think otherwise

noisesmith21:12:43

@Radek I think this is in the link above, but the assumption in clojure is that everything is a constant with very few exceptions

kyselyradek21:12:12

Yeah, I've heard that one before, but is this really enough?

kyselyradek21:12:30

I have a tendency to at least use all-caps

noisesmith21:12:36

if I put constants in all-caps, the majority of my vars would be all-caps, it would be extremely distracting

noisesmith21:12:52

unless by constants you mean something other than "top level definitions that aren't mutable"

kyselyradek21:12:42

Yeah, I actually mean those top level ones

noisesmith21:12:50

right, that's what I'm saying. All the functions we define are constants - they aren't mutable. All the data literals. That covers everything except singleton mutable containers, which are extremely rare in my experience.

noisesmith21:12:50

If you make an exception for functions, I still don't think naming things that aren't functions in all caps is useful

kyselyradek21:12:50

Eeh, I guess you're right. While the use of all-caps constant is ugly here (mixing local var and global constant) @(http/websocket-client ws-url WS-OPTIONS) I feel like it makes sense here :chanId (channel CHANNELS-MAP) But I guess I'll rather sacrifice the second example to the favour of the first one (which is really ugly)

kyselyradek21:12:08

But it'll take some time to get used to ๐Ÿ˜„

kyselyradek21:12:35

Anyway, thanks again ๐Ÿ™‚ It's refreshing to see a community who is actually helpful to the language beginners ๐Ÿ™‚

shaun-mahood21:12:37

I tend to have very few top-level defs - it's much more likely they will be passed in as an argument or come from a let binding. It also lets me to treat my functions as independent from the context my application is using, making things like unit tests, generative tests, and using them from the REPL much more straightforward.

kyselyradek21:12:02

Also very good point. I have literally two of these defs within a namespace which are reused in multiple functions where it really wouldn't make sense to pass them as arguments, but I also keep a separate file (namespace) for global constants across the app which are in the simply required and passed as arguments.

kyselyradek21:12:33

But I'll also try to keep them as local as possible for the reasons you mentioned, thanks for them ๐Ÿ™‚

shaun-mahood21:12:54

One of the things that hurt me early on was setting everything up for development - I'd get everything working great and then end up having no idea how to deploy things. If you want to keep the same style you could try using something like https://github.com/juxt/aero to define those constants. It would probably make it a bit easier to separate your development, testing, and production environments and still give you a central place to keep things.

kyselyradek21:12:03

This is something for the future, I'll definitely star the repo, however the original post was more about the kind of constants you write per namespace to unify the namespace's API towards the core (e.g. you're writing interfaces to multiple exchanges and you want to reference the socket channel names with some unified name within your whole program (e.g. :orderbook) but one exchange uses string "book" and the other one "ob" so you provide mappings for those in the exchange interface namespaces)

kyselyradek21:12:31

But I'll definitely look into Aero for runtime configs

shaun-mahood22:12:56

Ahh that makes sense - I haven't done any real work with channels like that.

kyselyradek22:12:21

Let me give you an example, maybe a constant isn't really necessary

kyselyradek22:12:03

Let's say I have interfaces to multiple exchanges, each via a Websocket. I'm going to send a message that defines information about what channel I want to receive. So I'm going to have multiple namespaces, each for one exchange. Now I want a unified API towards the app, so each namespace is going to have subscribe-message function that returns the message. e.g.

(defn subscribe-message [symbol channel]
  (let [message {:chanId (channel channels-map)}]
    ...))

kyselyradek22:12:17

Because I want the unified API, I want to pass channel arg as something general (e.g. :orderbook) to these functions for multiple exchanges and the idea is the (channel channels-map) converts it into something that exchange understands (like "ob" etc)

kyselyradek22:12:15

Obviously the channels-map here is the constant.

kyselyradek22:12:25

So I don't think this namespace-specific mapping is something you want to pass as an argument, is it?

kyselyradek22:12:18

Now let's say for now only this one function needs the mapping. I can see moving it directly to let, however I feel a strong possibility of the mapping becoming important for other functions in the future. Where would you draw line between moving it inside vs leaving it outside as a namespace constant?

shaun-mahood22:12:00

One of the naming conventions I like (though not everyone does) is using -> in my function names to specify that kind of thing - so you might define your constant as a function like defn ->channel and have that function do your conversions. You might use convert-channel-name, or make-channel, or whatever naming you prefer, can be consistent with similar things in your codebase, and communicates the meaning of what you are trying to do.

kyselyradek22:12:29

I'm wondering now what would be a difference if that mapping was defn... because technically now it's a map and map is used as a function

shaun-mahood22:12:27

Yeah - even if you use the literal map as part of the function and call it the same way, for some situations it can add a lot of flexibility - you can add in multiple arguments or an optional arguments map, handle more complex conditionals, error handling, or even just throwing some print statements for troubleshooting.

shaun-mahood22:12:05

The thing that jumps to mind immediately (though not sure if this is possible or helpful for you) is that you could have a single-arity version that takes just the channel argument, and a two-arity version that takes channel exchange - then your conversion can be specific to the exchange if needed. You could also log an error message when there's some input you don't expect (if that's a problem for you).

kyselyradek22:12:39

I also though about multi-arity but I don't think that'd help this particular case

kyselyradek22:12:33

In fact I'm pretty fine with the namespace-level def in this case, I asked more out of curiosity, to see how more experience Clojurian would handle this

kyselyradek22:12:45

And you definitely gave me a lot to think about, thank you for that! ๐Ÿ™‚

shaun-mahood22:12:00

Being able to use maps as functions is fantastic, glad it is working for you. Keeping it simple like that is often a very good choice.

kyselyradek22:12:39

Definitely. I didn't even fully realise it until recently when I heard Rich highlight this in one of his talks. Also, in past toying with the language I used the (key map) order rather than (map key) because the latter doesn't work for defrecords, but I'm definitely going to embrace this more

noisesmith22:12:19

this is a favorite trick of mine

(ins)user=> (defrecord Foo [k] clojure.lang.IFn (invoke [this k] (get this k)) (invoke [this k default] (get this k default)))
user.Foo
(cmd)user=> ((->Foo :a) :k)
:a
(ins)user=> ((->Foo :a) :l :OK)
:OK

noisesmith22:12:56

should also implement applyTo, but that's a start

kyselyradek22:12:07

Gonna steal! ๐Ÿ˜‚ ๐Ÿ”ฅ

noisesmith22:12:38

complete version - worth making a macro for these methods

(cmd)user=> (defrecord Foo [k] clojure.lang.IFn (invoke [this k] (get this k)) (invoke [this k default] (get this k default)) (applyTo [this args] (apply get this args)))
user.Foo
(cmd)user=> (apply (->Foo :a) [:k])
:a

alexmiller21:12:07

generally I try to first avoid defโ€™ing values, then usually do nothing special naming-wise if I do

alexmiller21:12:25

but I have seen people use + + around constants

alexmiller21:12:05

generally, I find the all-caps style from Java irritating to both write and read so would unrecommend that

kyselyradek21:12:28

Yes, I already converted to the regular naming after few messages in the thread below my original post

lennart.buit21:12:26

(pet peeve in editors: I hate that defed values are coloured white in Cursive/IntelliJ, which is the same as unapplied functions, would love to see difference between you are using a definition here and you are passing around a function)

lennart.buit21:12:29

but yeah, how would cursive knowโ€ฆ

noisesmith21:12:38

but your top level functions are defs

noisesmith21:12:55

how would clojure know? how would the vm know?

lennart.buit21:12:22

yeah, they canโ€™t