Fork me on GitHub
#beginners
<
2018-01-24
>
michael74005:01:26

does this look idiomatic?

(def hash-maps [{:id 1} {:id 2} {:id 3}])

(defn get_ [key]
  (fn [map] (get map key)))

(map (get_ :id) hash-maps)

michael74005:01:47

can't use partial unless i want to flip flop gets arguments

michael74005:01:06

an idiomatic example of currying, i mean

edwaraco05:01:55

You can use (map :id hash-maps)

michael74005:01:57

it's a toy example. just trying to think about how to curry in clojure

michael74005:01:45

but thank you! i didn't know you could handle hash maps like that.

dadair05:01:58

if the function is only used for that map, you can also just do (map #(get % :id) hash-maps) or (map (fn [m] (get m :id)) hash-maps) (I’m assuming you don’t always use keywords, since if so you could just do (map :id hash-maps) as indicated above)

edwaraco05:01:03

I think curry technique in clojure cannot be used completely.

edwaraco05:01:19

With partial function (Trying to do "Curry") you can do (map (partial :id) hash-maps), but I think it's not an idiomatic example

seancorfield06:01:47

@michael740 Using anonymous functions is idiomatic: #(f % arg2)

d4hines15:01:33

Clojurians seem like perceptive folk, and I was hoping to get some advice from some more experienced devs on how I should structure the data for an app I'm writing. Is this an ok place to solicit such?

manutter5115:01:03

I’d say go for it.

d4hines16:01:20

I work on line-of-business apps for a call-center. Our agents rely heavily on a note-taking app I wrote that goes and gathers all the relevant notes from various sources in our CRM and displays them in a uniform format. They can also add notes by filling out various forms in the app. Some the data generated by the forms is arbitrary text input, and some of it is constrained to predefined options. Submitting the form has side-effects in the CRM that are essentially arbitrary. There are also complex rules on user-note ownership and editing. My current code-base for solving all these problems is getting exponentially worse as the requirements increase. My question is: how do I structure the data for these notes in way that makes my life easy, so that I can write arbitrary rules about the forms that generate them and the side-effects they have in other systems?

d4hines16:01:07

My current thought is to maybe have some sort of tagging system for the notes that embues them with all the atributes the various side-effects will need. I could store all the notes in a Datomic database, and then have other services read each note when it's created, look at the tags, and hand it off to whatever functions handle those tags. That seems like it could get really complicated as well, but I'm not sure how else to handle it.

noisesmith18:01:37

fwiw my mental model wouldn’t accept “the attributes the various side-effects will need” - a side effect shouldn’t be a first class part of the design, it should be an edge as it is an adaptor between your code’s explicit model and something that you can’t know or control. So I would replace that with “attaching tags to each entity so that they can be categorized, dispatched, or filtered by tasks that need to launch various side effects”

noisesmith18:01:35

maybe that’s a pedantic point, but I find it useful to be very strict about never thinking of a side effect operation, or a thing that performs them directly, as being in the middle of my code - it has to be an edge as it interacts with a black box that is guaranteed to fail intermittently

noisesmith18:01:21

but yes, that sounds like a good fit for a system in clojure that uses datomic - creating entities linked to an id representing a specific case number or customer id, with various attached tags that can be used to select the data needed for various operations / api calls / etc.

d4hines22:01:26

Thanks for the feedback. I agree with what you're saying, and by no means want the side-effect code to be in the client application (which is where it is now). I'm going to have to brainstorm a bit to figure out how to express the problems I'm trying to solve in a way that is... solvable. Do you the Datomic forum is a better place for discussion about application architecture?

noisesmith22:01:22

if the questions are datomic-specific, I think so - I’m not a datomic expert but I’ve been developing production clojure apps as my day job for years now

d4hines22:01:22

Cool. I appreciate the willingness to help!

gamecubate16:01:17

Say I have 2 seqs:

(def images ["2.png" "5.png" "0.png" "3.png" "1.png" "4.png"])

(def layers [[[4 5] [5 5] [6 5]]
             [[4.5 5] [5.5 5]]
             [[5 5]]])
and I want to write a fn such that (fn layers images) returns:
[[[4 5   "2.png"] [5 5   "5.png"] [6 5 "0.png"]]
 [[4.5 5 "3.png"] [5.5 5 "1.png"]]
 [[5 5   "4.png"]]]
. How would you go about this? I tried:
(let [i (atom 0)]
  (for [layer layers]
    (for [tile layer]
      (let [img (nth images @i)]
        (reset! i (inc @i))
        (conj tile img)))))
but for handles the sequences starting with the rightmost, hence not giving me the order I wanted, e.g.,
(([4 5   "2.png"] [5 5   "3.png"] [6 5 "1.png"]) 
 ([4.5 5 "5.png"] [5.5 5 "4.png"])
 ([5 5   "0.png"]))
.

gamecubate16:01:50

loop/recur might seem a better choice but am perhaps missing something simpler.

rauh16:01:59

@gamecubate you can just use map with multiple inputs: (mapv conj (apply concat layers) images)

gamecubate16:01:52

My sequence flattened if I do that, though:

[[4 5 "img-2"] [5 5 "img-5"] [6 5 "img-0"] [4.5 5 "img-3"] [5.5 5 "img-1"] [5 5 "img-4"]]
instead of the desired:
[  [[4 5 "img-2"] [5 5 "img-5"] [6 5 "img-0"]]   [[4.5 5 "img-3"] [5.5 5 "img-1"]]   [[5 5 "img-4"]]  ]

gamecubate17:01:10

Preserving the structure with:

(mapv #(mapv conj % images) layers)
almost works:
[[[4 5   "img-2"] [5 5   "img-5"] [6 5 "img-0"]]
 [[4.5 5 "img-2"] [5.5 5 "img-5"]]
 [[5 5   "img-2"]]]
Need to figure out a way then to drop images as I go.

schmee17:01:01

what does the nesting represent?

schmee17:01:36

Clojure is not very good at dealing with mutating data in nested structures

schmee17:01:02

is it possible to convert the data into a vector of maps for instance?

schmee17:01:10

that would be much easier to deal with

rauh17:01:07

@schmee Yeah in that case a stateful walk would likely be easier in a lot of languages.

rauh17:01:26

You can use loop/recur to do it. It's not too bad:

(loop [images (seq images), layers (seq layers), out []]
    (if layers
      (let [layer (first layers)]
        (recur (drop (count layer) images)
               (next layers)
               (conj out (mapv conj layer images))))
      out))

rauh17:01:46

There is a hackier solution that's likely simpler, but involves a stateful iterator

gamecubate17:01:30

@schmee The nesting represents tile coords on a 3-layer board. Tiles have images assigned to them from a shuffled set.

gamecubate17:01:13

Was thinking about doing a refactor in favour of maps indeed. My first take is due tonight so would add this to a later sprint.

schmee17:01:33

ahh, yeah, then a vector of {:x 0 :y 0 :z 0} or something would be much easier to deal with

gamecubate17:01:20

Meanwhile, and thanks a lot @rauh but that looks right. You OTOH made it look too easy.

gamecubate17:01:41

@schmee yes indeed.

gamecubate17:01:00

You’ve convinced me. Thanks.

drewverlee18:01:20

In cases where nesting makes sense. Then i suggest looking at the Specter Library. Which i understand, makes use of an abstraction that others say is similar to lenses.