-1

I am working with refs and I need some help.

I have here 2 bank accounts with respective :operations

    (ref :name "bank"
         :accounts 
        {12345678 (ref {:name "joey" 
                       :account-number 12345678 
                       :operations (ref {:desc "DESC1" :amount 100 :date "2017-01-10"]})
                                   (ref {:desc "DESC2" :amount 200 :date "2017-01-11"]})
                                   (ref {:desc "DESC3" :amount 300 :date "2017-01-12"]})})
        {87654321 (ref {:name "paul" 
                       :account-number 12345678 
                       :operations (ref {:desc "DESC1" :amount 50 :date "2017-01-10"]})
                                   (ref {:desc "DESC2" :amount 10 :date "2017-01-11"]})
                                   (ref {:desc "DESC3" :amount 30 :date "2017-01-12"]})})
})

I need get all :operations from all accounts to build a collection like this:

[{:desc "DESC1" :amount 100 :date "2017-01-10"]}
 {:desc "DESC2" :amount 200 :date "2017-01-11"]}
 {:desc "DESC3" :amount 300 :date "2017-01-12"]}
 {:desc "DESC1" :amount 50 :date "2017-01-10"]}
 {:desc "DESC2" :amount 10 :date "2017-01-11"]}
 {:desc "DESC3" :amount 30 :date "2017-01-12"]}]

Don't need be the same its just an idea, I'm trying with map and deref but still stuck.

  • I have the felling that `(ref :name "bank")` is not valid, right? – Edwin Dalorzo Jan 14 '17 at 21:28
  • What have you tried, and were are you stuck? Incidentally, your code doesn't even *read* ("unmatched delimiter"), much less *work* in any capacity. – Nathan Davis Jan 15 '17 at 02:22
  • Two things: please tell me this isn't actual bank code. :P And tell us more about why your data looks like this. Triple-nested refs has a code smell. – jmargolisvt Jan 15 '17 at 04:52

1 Answers1

1

You have a number of problems with your code which you will need to sort out. While clojure has great support for parallelism/concurrency, you need to first get the basics right. Dealing with multiple threads of activity is hard and trying to do that while also trying to work out how basic data structures and core functions work will make it almost impossible.

  1. Your ref function is not valid clojure. The docs for ref state

ref function Usage: (ref x) (ref x & options) Creates and returns a Ref with an initial value of x and zero or more options (in any order):

:meta metadata-map

:validator validate-fn

:min-history (default 0) :max-history (default 10)

If metadata-map is supplied, it will become the metadata on the ref. validate-fn must be nil or a side-effect-free fn of one argument, which will be passed the intended new state on any state change. If the new state is unacceptable, the validate-fn should return false or throw an exception. validate-fn will be called on transaction commit, when all refs have their final values.

Normally refs accumulate history dynamically as needed to deal with read demands. If you know in advance you will need history you can set :min-history to ensure it will be available when first needed (instead of after a read fault). History is limited, and the limit can be set with :max-history. Added in Clojure version 1.0

The two keywords you have at the start look like you may be trying to define metadata for the ref. You need to use the :meta option when defining the ref (this is a guess on my part as to what your real intention is and could be totally incorrect).

  • The mandatory argument to ref is the initial value. This needs to be a valid clojure 'structure' or a function which returns one. In your case, you look like you want a map.

    1. I don't think you want nested refs. While technically, I think you can define them, this is almost certainly not what you really want. Have a read of refs and transactions to get an idea of whyy you probably don't want to do this and the impact it would have on performing STM. A general rule of thumb is to isolate the values you need to ensure are managed. You want fine grained access control and you want to avoid nested access control as it will make matters overly complex and your access control will become course grained. Consider your bank account operations. You don't want to lock all bank accounts when only operating on one or two of them - you just want to lock/control updates on the accounts involved. What you probably want is either an array (vector) of refs or perhaps a normal hash-map where one of the keys is the account and the value is the ref which is in turn a map with the values you need to ensure are updated in a transaction (other options would include a semaphore or critical region approach whee the ref is a simple flag/lock which determines if the transaction can continue or has to be wound back and tried again.

    2. I think you may want to think about your data structure a little more. Play around with hash maps a bit and experiment with nested structures. Play with the core functions on those basic and nested data structures (map, filter, reduce, loop/recur, sequences and laziness etc.. Get the basic abstraction correct and then look at how you would need to change it to ensure consistency in your data i.e. how to ensure your transactions are suitably atomic, especially when multiple accounts are involved. Your current nested structure is not valid - for example, look at the :operations value. What value is that? Is it a vector of transaction refs? How does clojure know that is a vector? How will your app need to extract that data? What is the most frequent operation - extract it all together or extract individual transactions? Do they need to be in date order? How easy is it to get a transaction for a specific date? Will this scale once you have hundreds of accounts with thousands of transactions? etc etc. Get your abstraction correct and the easy things will still be easy. Get it wrong and the easy things will be hard and the hard things will likely be even harder.

Tim X
  • 4,158
  • 1
  • 20
  • 26