3

I need to define mutually-dependent vars. By this I mean that one var contains e.g. a vector with another var and vice versa. This is illustrated by the following code:

(declare a b)
(def a [1 b])
(def b [a 2])

But after loading this code I get this:

test=> (first a)
1
test=> (second a)
#<Unbound Unbound: #'test/b>
test=> (first b)
[1 #<Unbound Unbound: #'test/b>]
test=> (second b)
2

clearly, thats not how it should work. I understand that printing such structure will give stack overflow, but I don't need to print it. How should I do it?

Vladimir Matveev
  • 120,085
  • 34
  • 287
  • 296

3 Answers3

3

You can do the following:

(declare a b)
(def a [1 #'b])
(def b [#'a 2])

@(a 1)
=> [#'user/a 2]

Note that #' is a reader macro for referring to a var.

I'm still not quite sure why you want to do this though..... trying to make vars mutually dependent like this seems like a pretty bad code smell to me. It's likely that whatever you are trying to do would actually be best solved by a different approach.

EDIT

With the extra comment stating that the problem is related to having different types of entities referring to each other, I think a better approach is a map with keywords, e.g.

(def my-model
  {:a 
      {:name "Entity A" 
       :references [:b]}
   :b 
      {:name "Entity B"
       :references [:a]}}
mikera
  • 105,238
  • 25
  • 256
  • 415
  • 1
    Yes, I thought I should do something with vars. Thanks. What I want to do is the following. I need to describe a (potentially) many-to-many relationship between some kind of types of some entities. These types and their relationship are constant. I want to define them as vars having references to each other, so if I want to get a type which is referenced by another type I could do something simple like `(first (:references src-type))`. – Vladimir Matveev Feb 17 '12 at 10:36
  • But you're probably right, maybe I should use global map of types and make references to be simple keywords. What do you think, will it be better approach? – Vladimir Matveev Feb 17 '12 at 10:42
  • 1
    Yes, I think a big map with keywords will work best. It's all just data - you might want to make copies of it, serialize it or create different versions for test cases etc., which will be easy with maps but get tricky quite quickly if you try to use vars..... – mikera Feb 17 '12 at 10:51
  • Thanks again, I think I'll stick with this approach. – Vladimir Matveev Feb 17 '12 at 12:51
1

First, this smells very much like an XY problem.

Second, mutually referential data structures cannot be created without mutating state. If that's the data structure you need (and you probably don't), then use clojure's very well designed approach to state. For example:

user=> (set! *print-level* 2)  ; this is necessary to stop the infinite print recursion
2
user=> (def a (atom [1]))
#'user/a
user=> (def b (atom [a 2]))
#'user/b
user=> (swap! a conj b)
[1 #<Atom@19ed00d1: #>]
user=> @a
[1 #<Atom@19ed00d1: #>]
user=> @b
[#<Atom@5c6c2308: #> 2]
Alex Taggart
  • 7,805
  • 28
  • 31
0

Lazy evaluation might help:

user=> (declare a b)
#'user/b
user=> (def a [1 (lazy-seq b)])
#'user/a
user=> (def b [(lazy-seq a) 2])
#'user/b
user=> (first a)
1
user=> (second b)
2
user=> (second a) ;stack overflow
user=> (first b) ;stack overflow
user=> (take 42 (second a)) ;still overflow as it's infinitely deep
user=> (take 42 (first b)) ;still overflow as it's infinitely deep

Hope it helps, though I can't see how it's going to be useful.

jbear
  • 363
  • 2
  • 7