Fork me on GitHub
#beginners
<
2016-12-15
>
agile_geek07:12:49

@gdeer81 I tend to start with the simplest abstraction first. If only for the reason that I can then refactor fast and it's easier to grok. So I'd start with maps until I understood the use cases better.

agile_geek07:12:34

I have same approach around maps vs records and multimethods vs protocols

agile_geek07:12:22

although I'm not entirely sure multimethods are 'simpler' than protocols I think they're more explicit (in my mind).

roelofw16:12:06

What can I do to get rid of these warnings :

WARNING: reader-conditional already refers to: #'clojure.core/reader-conditional in namespace: clojure.tools.reader.impl.utils, being replaced by: #'clojure.tools.reader.impl.utils/reader-conditional
WARNING: tagged-literal already refers to: #'clojure.core/tagged-literal in namespace: clojure.tools.reader.impl.utils, being replaced by: #'clojure.tools.reader.impl.utils/tagged-literal

seancorfield16:12:25

Sounds like you’re pulling in an old version of tools.reader somewhere.

seancorfield16:12:54

(probably through a transitive dependency)

seancorfield16:12:46

If you’re using Leiningen, lein :deps tree should show you all the libraries being pulled in and their versions. That will show you what’s pulling in tools.reader and what version it is.

roelofw16:12:48

@seancorfield then I see this output :

C:\Users\rwobb\Desktop\clojure\paintings2>lein :deps tree
':deps' is not a task. See 'lein help'.

Did you mean this?
         deps
 

roelofw16:12:23

and lein deps tree gives no output at all

st16:12:27

lein deps :tree

roelofw16:12:44

oke, cloverage it too blame : [cloverage "1.0.9"] -> [org.clojure/tools.reader "1.0.0-beta3"]

roelofw16:12:17

and a lot of other problems 😞

roelofw16:12:13

I deleted cloverage and the warnings are gone

roelofw16:12:42

@st @seancorfield is it safe to follow the rest of the recommendations

roelofw16:12:19

I saw that chesire schould be excluded and that would be a problem with clj-http and json input from a external api

roelofw17:12:36

What is a good plugin for validating that you use clojure well ? I know that kibit and eastwood are two choices

agile_geek17:12:50

Kibit and Eastwood are good for different things. Eastwood for stylistic things and issues like your dependency problems above and kibit suggests alternative fns you may not have thought of.

agile_geek17:12:30

Although Eastwood makes some fn suggestions Kibit is more comprehensive

roelofw17:12:49

oke, then I will use kibit on my first project

roelofw17:12:42

what went wrong here :

C:\Users\rwobb\Desktop\clojure\paintings2>lein deps
Retrieving lein-kibit/lein-kibit/0.1.3/lein-kibit-0.1.3.pom from clojars
Retrieving jonase/kibit/0.1.3/kibit-0.1.3.pom from clojars
Retrieving org/clojure/core.logic/0.8.11/core.logic-0.8.11.pom from central
Retrieving org/clojure/core.logic/0.8.11/core.logic-0.8.11.jar from central
Retrieving jonase/kibit/0.1.3/kibit-0.1.3.jar from clojars
Retrieving lein-kibit/lein-kibit/0.1.3/lein-kibit-0.1.3.jar from clojars
C:\Users\rwobb\Desktop\clojure\paintings2>lein kibit
'kibit' is not a task. See 'lein help'.
 

roelofw17:12:52

kibit seems to be downloaded and installed

tokenshift17:12:15

Have you added it to :plugins in your project.clj or ~/.lein/profiles.clj?

roelofw18:12:21

to my project.clj file

roelofw18:12:46

I always use that one

roelofw18:12:08

I never use ~/lein/profiles.clj

agile_geek18:12:54

Needs to be in :plugins and not : dependencies

roelofw18:12:36

I see now two recommendations :

At C:\Users\rwobb\Desktop\clojure\paintings2\src\clj\paintings2\api_get.clj:14:
Consider using:
  (client/get
    (str "" id)
    {:as :json,
     :query-params
     {:key (env :key),
      :format "json",
      :type "schilderij",
      :toppieces "True"}})
instead of:
  (-> (str "" id)
   (client/get
     {:as :json,
      :query-params
      {:key (env :key),
       :format "json",
       :type "schilderij",
       :toppieces "True"}}))

At C:\Users\rwobb\Desktop\clojure\paintings2\src\clj\paintings2\api_get.clj:50:
Consider using:
  (client/get
    (str "" id "/tiles")
    {:as :json, :query-params {:format "json", :key (env :key)}})
instead of:
  (-> (str "" id "/tiles")
   (client/get
     {:as :json, :query-params {:format "json", :key (env :key)}}))

 

roelofw18:12:14

but I wonder if the first one does not break my code ?

tokenshift18:12:44

The two options in the first one are equivalent.

tokenshift18:12:13

(-> (foo) (bar)) expands to (bar (foo))

tokenshift18:12:07

Basically, -> takes the result of the first thing, and “threads” it in as the first argument to the second thing.

roelofw18:12:24

yep. that is why I use it

roelofw18:12:47

I find it more readable then just using parentheses

roelofw18:12:58

@agile_geek so is it better to change it of let it what it is

josh_tackett19:12:21

Anyone know a way to measure the size of a ref? or atom?

agile_geek19:12:52

@roelofw Up to you. Kibit is just saying that for just two expressions it's just as clear to nest them in parens. For more than 2 the thread first macro is probably a better choice

agile_geek19:12:57

@josh_tackett a ref or an atom doesn't really have a size. The data structure referenced by a ref or an atom may have. If that data structure is a collection you can use count on the dereferenced atom or ref: (count @my-atom)

agile_geek19:12:15

@josh_tackett however it's rare to want the number of elements in a collection as this is usually something done in imperative languages to process all elements in a for loop. We usually use map to process a collection (or reduce if the result has to be an accumulation)

josh_tackett19:12:36

@agile_geek So the issue is that this ref is growing in memory and I need to find out why, and also how to garbage collect properly. Need to see the size print out to know that...

agile_geek19:12:32

Must be a massive data structure if it's consuming that much memory. The garbage collector will clean up any data structure no longer referenced so while you have a data structure in a ref or an atom it will never be GCed. For this to be a problem you are probably doing something a bit strange. It's typical to maintain all requiredstate if required in an atom, for example, in a Clojurescript application and it would not normally consume much memory. Something is fundamentally wrong if you're running out of memory. Are you eagerly fetching a large amount of data from a file or database?

kyle_schmidt19:12:41

Just wondering and I know this may be a very general question but just wondering if there is a typical way in clojure to represent state? i.e. do I typically want one map that contains all of my data or is it common to have more than 1 stateful collection that I operate on?

agile_geek19:12:08

@kyle_schmidt the short answer is try and avoid keeping state at all. For example, in a CRUD web app you can just process data through a stack of function calls without storing it anywhere but in the database.

roelofw19:12:49

Can I do that also in for example a accounting website. I thought I need somewhere the amount of every account so I can do checks on it. Or do I every time calculate that again ?

agile_geek19:12:08

If absolutely necessary (usually only on client side UI using Clojurescript) you would typically store a map of maps and vectors in a single atom to represent all state in one place

roelofw19:12:44

I think on logging in or out you need some sort of state , or am I wrong ?

agile_geek19:12:43

You don't need state in your application code. You would need to store the state in some persistent data store (a database or even a file)

kyle_schmidt19:12:51

@agile_geek Thanks! That makes sense!

donaldball19:12:17

It it common practice to have a single clojure atom with your system state, which may itself mediate access to external persistent storage

kyle_schmidt19:12:32

@roelofw You can also use JWT with an expiration time that the client would send to the server to verify that the REST request is valid

kyle_schmidt19:12:10

and you wouldn't need to store login server side

roelofw19:12:43

oke, and how can I then deal with checks that accounts may not be negative. Calculate every time the amount of a financial account ?

agile_geek19:12:52

@kyle_schmidt if you want to maintain being logged in between browser sessions that's a good solution. Although I typically force a login on every session depending on app

agile_geek19:12:17

@roelofw or store the total

roelofw19:12:24

oke, so everty time something changed , change also the total in the database

agile_geek19:12:32

Although storing the total in a multi tenanted environment may be problematic and lead to deadlocks etc

roelofw19:12:48

so a account will have a name, a desription and a total

agile_geek19:12:04

Well presumably the account data is in database anyway right?

roelofw19:12:38

it will have a name, a id , a description and if needed a total

agile_geek19:12:00

And transactions as children of an account?

roelofw19:12:14

I think so

roelofw19:12:58

this is very therotical at this moment, because im not building such at the moment

roelofw19:12:33

but who knows in the future if I know and have more expericience with clojure

agile_geek19:12:20

So if you always have to fetch the txns and /or you know there aren't many per account you could just total them in the process of transforming them for display.

agile_geek19:12:56

But if you aren't always reading all txns per account you'll need to store the total

roelofw19:12:30

txns = transactions

kyle_schmidt19:12:48

and what if there are many txns? Would you store those in an atom and write them all to the database after a period of time to reduce i/o?

roelofw20:12:07

I think at the end of the year there will be some 500 transactions on all accounts

kyle_schmidt20:12:47

many txns across many accounts**

dpsutton20:12:09

0,1,infinity

agile_geek20:12:52

@kyle_schmidt depends on the database. If it's relational and you can't bulk update you might as well update directly. These aren't really Clojure questions more architecture. For instance if you had to load millions of txns then I might read them, write them to an asynchronous messaging system and have another service or another process in same app read from the message queue to write in bulk. If ot was really big data maybe use a streaming framework like Onyx

kyle_schmidt20:12:17

gotcha. sorry, didn't mean to stray off topic

agile_geek20:12:43

I use atoms sparingly and usually for small amounts of data in a specific use case that only needs in memory transient storage...and even then I might use an in memory database