0

I'm new to Clojure. This question is related to this one but different. Say I have nested map:

(def example
  {:a {:b 2 :data [1 2 3] :something-that-uses-data ?}})

And suppose I want access (-> example :a :b :data) in :something-that-uses-data. Do I have to call it exact same way of if there some macros or keyword (something like (-> this :data))? Can't find anything related for couple hours.

3
  • not sure I understand the question. You might want to use get-in instead of threading macros. Commented Mar 20, 2020 at 18:11
  • The content of a map is not linked to the map. So if you are a ?, you have no way of knowing you are inside a map. You would have to keep track of the root + path, pass it down, decide from there what to do. Commented Mar 20, 2020 at 18:22
  • I saw a get-in solution. It's now what I wanted. Thnx Commented Mar 20, 2020 at 18:29

2 Answers 2

1

I think you are trying to imitate objects in Clojure. Objects have data and functions working on that data packed together in a class, but in functional languages like Clojure, data and functions are separated.

So idiomatic way in Clojure is functional approach, which is just functions working on immutable data. The only OO feature in Clojure are protocols, which are used for polymorphism.

I think what you want in your example is this:


(def example
  {:a {:b 2 :data [1 2 3]}})

(defn something-that-uses-data [{{:keys [data] :a}}]
  (do-work-with data))

You can of course have a map which contains data and functions, but that would be useful only if functions are different and you want to apply those functions in a sequential context. Something like this...but I am only guessing what your context might be.

  (map (fn [[k v]
         (let [data (:data v)
               f (:somthing-that-uses-data v)]
           (f data)))
       collection-of-maps)
Sign up to request clarification or add additional context in comments.

1 Comment

Thnx. I just wanted to make sure it's "impossible" by default.
1

I'm not sure I understand the question. Can you edit to clarify the desired behavior?

There are 3 ways you can access deep data structures in the Tupelo library.

  • Using tupelo.forest to process tree-like data structures. See the docs and be sure to watch the video.

  • You can use tupelo.core/destruct, which is a fancier version of get-in using a template. See the examples here.

  • You can do a depth-first walk of your data structure, keeping track of the parents of each node using walk-with-parents. See the examples and the docs.


Accessing the parents of a nested data element

You can do this using walk-with-parents and walk-with-parents-readonly. Consider this simple nested data structure:

(def data {:a 1 :b {:c 3}} )

We can walk the data structure, remembering the path from the root to each element. When we get to element 3, we have the following path of parent data:

(walk-with-parents data <noop-intc>) =>

:parents => [ {:a 1, :b {:c 3}}   ; the orig map
              [:b {:c 3}]         ; the MapEntry for key :b
              {:c 3}              ; the map where value 3 
              [:c 3] ]            ; the MapEntry with value 3
:data => 3

So the interceptor will be called with 2 args:

  • a parents path vector of 4 elements
  • the data item itself

For the -readonly variant the interceptor function can do validation & throw an exception if a problem is detected. For the non-readonly variant, the return value replaces the data element. Each interceptor is a 2-element map that looks like:

{:enter (fn [parents data] ...)
 :leave (fn [parents data] ...) }

2 Comments

(get-in) requires to write down all the path to a given key in hash-map again. And I was wondering if an element of hash-map «knows» it's parent with some default function or macros.
An element in a hash-map does not know its parent. The magic of immutable data structures that are cheaply modifiable (as it were) does not permit cycles. However, a visitor can certainly remember the parent as it visits the child. That is one of the things you get from zippers (see clojure.zip).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.