Fork me on GitHub
#beginners
<
2018-12-16
>
anantpaatra00:12:42

Say you need a data model for contacts in a quick application you're building. Where would you go to find a decent, complete, battle driven data model for that?

christian.gonzalez01:12:48

Wouldn’t a hash-map work well for looking up contacts? Not sure what the use case is here.

anantpaatra02:12:10

I may be using the wrong term there. What I mean is more like this:

[{:id int
  :name string
  :nickname [string & optional strings]
  :addresses [{:street,...} {:street...}]
  :phone [phone-1 phone-2 phone-3]
  :father opt-id
...}]

anantpaatra03:12:27

Like, what is expected of a complete contact. Hard to explain since I have very little experience with databases and data in general. Maybe people probably set this things up in databases, so I should probably be looking for "database contact model" or something like this.

christian.gonzalez03:12:16

You could definitely do that! Keep in mind whether you want to do a relational model or a nosql model, which might lead to you to actually create a database to index records rather than storing them in memory. The example you showed seems solid. Think about how you'll query the records and what your use case is most importantly.

anantpaatra21:12:39

Thank you 🙂

quieterkali05:12:20

hey folks, can i consider this (doall (map @profiles connections-ids)) under side effect of @profiles?

lucio11:12:47

When I need to perform side effects I personally use doseq

jayzawrotny05:12:54

What is the intended result?

andy.fingerhut05:12:59

Daouda also posted the same question to #clojure channel, where I have sent a few responses.

jayzawrotny05:12:34

Ah ok, thanks for the heads up. 🙂

jaihindh.reddy12:12:22

What's the operation that removes a thing from a set?

pcbalodi12:12:04

thre is disj

nikola.kasev13:12:57

I have a list of maps like this ({:type "G", :location [2 1], :hp 300} {:type "G", :location [5 5], :hp 250}) and I would like to apply a function to the :hp key for an element where the :location is [2 1] for example. I'd like to have the resulting list. Is there an idiomatic way to do this "select for update" logic?

cbowdon13:12:47

I’m not sure if there’s a better idiom but (map #(if (= [2 1] (:location %)) (update % :hp f) %) data) would be clear enough IMO

alexmiller14:12:37

By the way you phrase the question, I would ask whether you should be using a map indexed by location as your data structure, rather than a list

nikola.kasev21:12:16

@ what do you mean?

cbowdon22:12:59

I think Alex is saying if your data structure was {[2 1] {:type “G” :hp 300}} instead then the operation would be trivial and more efficient.

cbowdon22:12:39

e.g. (update data [2 1] #(update % :hp f))

nikola.kasev21:12:00

I see, that's an idea I can explore

farhad.hsni18:12:49

Hi. Why repl is a big deal for clojurians? Is there any difference with other repls?

dpsutton18:12:04

I used to program in C# for my job. Almost the only way to interact with your code is to press f5 and run the entire application or set up unit tests. There was a program called LinqPad that was a quick way to evaluate and interact with code and scripts and it was amazing. You can write a little code and exercise it and see how it behaves rather than setting up a unit test and 4 mocked objects injected in. Python and ruby both have repls as well but they are different a little. I think stu halloway put it best in that a lisp's repl allows your tools to run in your code rather than the opposite where the code runs in your tools.

farhad.hsni18:12:57

Does Clojure have a package manager/build tool like cargo of rust or mixof Elixir? Sorry my questions are dumb.

dpsutton18:12:58

you most probably want lein: https://leiningen.org/

dpsutton18:12:04

but there's also deps.edn a newer take on package management. It is unabashedly not a build tool though. https://clojure.org/guides/deps_and_cli

lvbarbosa18:12:06

Can someone explain to me the reason why derefs will trigger transaction retries? I am not sure, but as I understood it, the STM system will try to guarantee that the ref values seen during transactions are the same as they were at the beginning of the transaction. Am I correct?

lvbarbosa18:12:00

I am confused because I don't understand the reason for this. What if I want the latest value in the ref, and not the one given at the beginning of the transaction?

andy.fingerhut20:12:01

In what way is a transaction worthy of the name 'transaction' if it computes a new state based upon a mix of different starting states?

andy.fingerhut20:12:35

I am not saying there is no such thing -- if there is such a system that calls a thing like that a transaction, I would be interested in learning about it.

andy.fingerhut20:12:06

I also do not know for certain whether deref's always trigger a transaction retry inside of a dosync, or only in some cases. I would guess it is not always, but perhaps only if the STM implementation detects that the ref has been updated by a different transaction, since the current transaction began.

andy.fingerhut20:12:51

And note that deref is the same operation used for uncoordinated dereferencing of Clojure atoms, promises, futures, etc., not only STM refs, so what I say above is only intended to refer to refs.

andy.fingerhut20:12:12

since there are no transactions at all for any of those other kinds of objects on which a deref can be performed.

lvbarbosa21:12:36

I see. I liked your first statement, it gave me a good point of view of why we need a "static snapshot of the world" inside of the transaction

lvbarbosa21:12:05

Now I understand the reason for the ref-history mechanism

lvbarbosa21:12:34

Yes, deref will trigger a transaction retry only if the value of the ref was changed since the beginning of the transaction.

lvbarbosa21:12:45

Thank you, sir!

nikola.kasev19:12:29

I'm implementing an algorithm with many recur places and complex data structures. I'd like to be able to trace and see how the execution flows. What would be a good way to instrument the code to dump tracing messages and the contents of the data to the console or file?

eggsyntax00:12:51

You can get a fair way with println 🙂 - There are lots of useful versions of tracing functions, often called spy. One that you automatically have on hand is clojure.tools.logging/spy. - Many clj/s editors like emacs/CIDER include debuggers. - If you need a really powerful approach, take a look at Sayid (although I’d try lighter-weight solutions first): https://github.com/clojure-emacs/sayid

tkjone20:12:57

I want to transform:

(def data   [{:id {4 67} :person_id {12 74}}])

; or

(def data-2 [{:id {4 67} :person_id {12 74}} {:id {12 83} :person_id {14 89}}])
to
((4 67 12 74))

((4 67 12 74) (12 83 14 89))
My current solution is
(map 
  (fn [m] 
    (reduce (fn [acc [k v]] (concat acc (first v))) '() m)) 
  data-2)
This does not feel as "nice" as I imagine it could be. Any suggestions for alternatives. > assumption: the values of each k/v pair in data and data-2 will only ever have 1 kv pair in them.

lennart.buit20:12:11

Not off the shelf solution, but you can call vals on a map to get the … vals

lennart.buit20:12:37

oh wait, its maps in maps

lennart.buit20:12:59

why is that, don’t you want to have like maps of keys -> lists/sets/vecs?

christian.gonzalez20:12:19

You could do this... (->> data (map vals) (map #(map vec %)) (map flatten))

christian.gonzalez20:12:34

Should really clean up that map map though

christian.gonzalez20:12:59

it's a terrible solution

tkjone20:12:04

What would you recommend as an alternative?

lilactown20:12:03

what the heck is 4 as a key? 😛 totally non-descript

lennart.buit20:12:07

{:id [4 67] :person-id [12 74]} or {:id #{4 67} :person-id #{12 74}} or {:id '(4 67) :person-id '(12 74)}

dpsutton20:12:24

You seem to not care that they are key value pairs ie one is associated with another. So don't use an associative data structure

lennart.buit20:12:00

(thats vecs/sets/lists respectively)

christian.gonzalez20:12:13

i wasn't sure if the id maps were actually being used for lookups, otherwise the first probably makes the most sense

tkjone20:12:41

{4 67} would represent a before and after value.

lennart.buit20:12:08

is it actually a mapping, thats the question you should ask yourself ^^

tkjone20:12:36

In my mind, yes. But I might be too close to this at this point 😜

tkjone20:12:17

So all instances of 4 will become 67. Thats why I initially thought map could be a good option.

lennart.buit20:12:47

well, thats a bit conflicting with your desire to flatten them to lists

christian.gonzalez20:12:57

Are you trying to maintain a history of IDs for these records?

lennart.buit20:12:18

maybe I am just misunderstanding

tkjone20:12:54

Imagine a db value is updated from 4 -> 67 and you need to remember this. That is what I have in mind.

tkjone20:12:55

The need to flatten them comes when I have to transform them to something a sql query can use.

lennart.buit20:12:19

right makes sense

christian.gonzalez20:12:56

It would be complicated to update those maps multiple times like if {4 67} turned into {5 4 4 67}

christian.gonzalez20:12:30

Maybe just maintain current ID and set/vector of past IDs? Then it would be easy to collect these, if this is the only option for maintaining history of a record

lennart.buit20:12:35

yeah, you could maintain history in a vec instead right by treating it as a fifo

lilactown20:12:11

user=> (def data [{:id {:old 4 :new 67} :person-id {:old 12 :new 74}}])
user=> (->> data (map vals) (flatten)
  #_=>      (map :old))
(4 12)
user=> (->> data (map vals) (flatten)
  #_=>      (map :new))
(67 74)
user=> (interleave 
  #_=>    (->> data (map vals) (flatten)
  #_=>       (map :old)) 
  #_=>    (->> data (map vals) (flatten)
  #_=>       (map :new)))
(4 67 12 74)

lilactown20:12:44

probably a cleverer way to do it, but it certainly looks clearer to me what’s happening if I were to read it a week later

lennart.buit20:12:36

bikeshedding: would prefer :current and :previous

lennart.buit20:12:23

:new and :old sound like future and past, :current and :previous more like current and past.

tkjone20:12:13

I like that alternative @lilactown.

lilactown20:12:18

lol I figure @tkjone can put his own spin on the names according to his domain. but the point is: names are good!

lennart.buit20:12:44

yah, not trying to offend

tkjone20:12:29

haha indeed.

didibus20:12:33

But now you can't keep more then a single prior id

lennart.buit20:12:48

ofcourse you can, you make :previous a vec or a list

didibus20:12:32

You can find just as good a name without this no. Just call it {:ids-over-time [2 45 67]}

didibus20:12:20

Hum, ya, maybe that's clearer to split out the current and the prior ones. But it seems harder to work with. Depending what you need to do

lennart.buit20:12:03

as long as things are consistently collections or consistently scalars ^^

didibus20:12:07

But I do like the suggestion of say {:current-id 45 :prior-ids [2 34 67]} in terms of readability. Though it also brings some question. Like the code must now maintain the invariants of coordinating the two. Making sure to conj to prior and update current when a new id is added. And its unclear if prior-ids include current or not.

tkjone20:12:34

Catch to the above: there will never be multiple prior-ids. Its only going to be something like 1 -> 15*

didibus20:12:33

Eh, if this was persistent data, like the model for a document store. I would design for future extension in mind, and so I'd make it a vector just in case in the future we need further history.

nikola.kasev20:12:50

I'm getting a NullPointerException clojure.core/name (core.clj:1546) in a function that turns a keyword into a vector. Any ideas why?

didibus20:12:09

Clearly something is null and passed to name

nikola.kasev21:12:37

Got it, I'm using a library that returns that...

didibus21:12:17

@tkjone Anyway, no right answer here. I think using keys like current and prior does make it more readable, so if you really don't need more then one prior, it seems a pretty good choice.

tkjone21:12:51

Thanks all!

didibus21:12:23

@nikola Are you sure the input keyword sometimes can't be nil?

nikola.kasev21:12:57

@didibus it's a result of a call to an external library function

didibus21:12:33

Ya, I'd add a check, in between.

didibus21:12:13

Also, just test your keyword to vector function. What does it do if you give it nil? Does it throw that error?

didibus21:12:46

To be sure its not a bug inside your function

jaihindh.reddy22:12:49

I have a log entry as a map that contains a :timestamp which is a java.time.OffsetDateTime. How should I print it to an edn file? pr-str gives me :timestamp #object[java.time.OffsetDateTime 0x67573db5 "2018-12-05T00:15:30.151200+05:30"]

jaihindh.reddy22:12:06

I want it to show with #inst

jaihindh.reddy22:12:35

What's the difference b/w print-method and print-dup? Which should be implemented when?

jaihindh.reddy22:12:49

This worked beautifully:

(defmethod print-method java.time.OffsetDateTime
  [dt out]
  (.write out (str "#inst \"" (.toString dt) "\"")))
Thanks @jkr.sw!

jaihindh.reddy22:12:24

I keep forgetting that read and print are extensible via multimethods.