I need some function which among other stuff would define a new global symbol. So that I could use it like this:
(define (func-prototype symbol value comment)
(define symbol value) ; this should somehow be reformulated
(format "~a=~a !~a\n" symbol value comment))
(define string-list (map func-prototype
'((s1 1 "first value")
(s2 20 "second value")
(s3 300 "third value))))
and be able to get the following results:
> string-list
'("s1=1 !first value\n"
"s2=20 !second value\n"
"s3=300 !third value\n")
> s1
1
> s2
20
> s3
300
Can this be implemented as a function or it is possible to do that only with the help of macros? Could you please suggest any possible implementations or at least give some hints/references that might be helpful?
I'd rethink the general approach, making it simpler. My suggestion: define a global hash table and inside the function add bindings to it, for example:
(define value-map (make-hash))
(define (func-prototype symbol value comment)
(hash-set! value-map symbol value)
(format "~a=~a !~a\n" symbol value comment))
Use it like this:
(define string-list
(map (lambda (lst)
(apply func-prototype lst))
'((s1 1 "first value")
(s2 20 "second value")
(s3 300 "third value"))))
string-list
=> '("s1=1 !first value\n"
"s2=20 !second value\n"
"s3=300 !third value\n")
And wherever you need to refer to one of the symbols in the hash table, do this:
(define (get key)
(hash-ref value-map key))
(get 's1)
=> 1
(get 's2)
=> 20
(get 's3)
=> 300
In general it is not possible to accomplish what you are trying to accomplish in the way you described. Your only hope would be to write stuff out to a file and then load that file into an interactive session. But even then.
In scheme you can't introduce top-level names, such as your desired s1, s2, and s3, except at the top-level. To do so, you could define a macro as:
>(define-syntax define-foo
(syntax-rules ()
((_ name value)
(define name value))))
>(define-foo s1 1)
<undefined>
> s1
1
If you try to use that macro in a function, it is no dice because the body of a function must end with an expression and any definition forms, like what the above macro would expand into, become local variables. That is:
(define (func-prototype name value comment)
(define-foo name value)
name)
>(func-prototype 's1 1 "com")
1
> s1
<error>
One approach that you could take that would work if your string-list is a constant would be as such:
> (define-syntax declare-variables
(syntax-rules ()
((_ (name value comment) ...)
(begin
(define name value)
...))))
> (declare-variables (s1 1 "com") (s2 20 "com") (s3 300 "com"))
> s1
1
This gets it done (I've ignored using 'comment') but, as I said, requires a compile time string-list.
One possibility you might think would work, but wouldn't, would be to use eval as:
(eval '(define s1 1) (environment ...))
but 'eval' only works for expressions, not declarations. Which gets me back to 'load' as a possibility.
First, consider whether you really want to do this, or whether a different solution (like a hash table) would work as well.
You can do this with reflection and dynamic evaluation using the eval procedure.
;; define-variable-with-value! : symbol any -> void
(define (define-variable-with-value! name value)
(eval `(define ,name (quote ,value))))
The quote is important; otherwise you risk reinterpreting a value as an expression to evaluate. To see the difference, consider the example
(define-variable-with-value! 'x (list 'error "kaboom"))
Related
During the execution of my code I get the following errors in the different Scheme implementations:
Racket:
application: not a procedure;
expected a procedure that can be applied to arguments
given: '(1 2 3)
arguments...:
Ikarus:
Unhandled exception
Condition components:
1. &assertion
2. &who: apply
3. &message: "not a procedure"
4. &irritants: ((1 2 3))
Chicken:
Error: call of non-procedure: (1 2 3)
Gambit:
*** ERROR IN (console)#2.1 -- Operator is not a PROCEDURE
((1 2 3) 4)
MIT Scheme:
;The object (1 2 3) is not applicable.
;To continue, call RESTART with an option number:
; (RESTART 2) => Specify a procedure to use in its place.
; (RESTART 1) => Return to read-eval-print level 1.
Chez Scheme:
Exception: attempt to apply non-procedure (1 2 3)
Type (debug) to enter the debugger.
Guile:
ERROR: In procedure (1 2 3):
ERROR: Wrong type to apply: (1 2 3)
Chibi:
ERROR in final-resumer: non procedure application: (1 2 3)
Why is it happening
Scheme procedure/function calls look like this:
(operator operand ...)
Both operator and operands can be variables like test, and + that evaluates to different values. For a procedure call to work it has to be a procedure. From the error message it seems likely that test is not a procedure but the list (1 2 3).
All parts of a form can also be expressions so something like ((proc1 4) 5) is valid syntax and it is expected that the call (proc1 4) returns a procedure that is then called with 5 as it's sole argument.
Common mistakes that produces these errors.
Trying to group expressions or create a block
(if (< a b)
((proc1)
(proc2))
#f)
When the predicate/test is true Scheme assumes will try to evaluate both (proc1) and (proc2) then it will call the result of (proc1) because of the parentheses. To create a block in Scheme you use begin:
(if (< a b)
(begin
(proc1)
(proc2))
#f)
In this (proc1) is called just for effect and the result of teh form will be the result of the last expression (proc2).
Shadowing procedures
(define (test list)
(list (cdr list) (car list)))
Here the parameter is called list which makes the procedure list unavailable for the duration of the call. One variable can only be either a procedure or a different value in Scheme and the closest binding is the one that you get in both operator and operand position. This would be a typical mistake made by common-lispers since in CL they can use list as an argument without messing with the function list.
wrapping variables in cond
(define test #t) ; this might be result of a procedure
(cond
((< 5 4) result1)
((test) result2)
(else result3))
While besides the predicate expression (< 5 4) (test) looks correct since it is a value that is checked for thurthness it has more in common with the else term and whould be written like this:
(cond
((< 5 4) result1)
(test result2)
(else result3))
A procedure that should return a procedure doesn't always
Since Scheme doesn't enforce return type your procedure can return a procedure in one situation and a non procedure value in another.
(define (test v)
(if (> v 4)
(lambda (g) (* v g))
'(1 2 3)))
((test 5) 10) ; ==> 50
((test 4) 10) ; ERROR! application: not a procedure
Undefined values like #<void>, #!void, #<undef>, and #<unspecified>
These are usually values returned by mutating forms like set!, set-car!, set-cdr!, define.
(define (test x)
((set! f x) 5))
(test (lambda (x) (* x x)))
The result of this code is undetermined since set! can return any value and I know some scheme implementations like MIT Scheme actually return the bound value or the original value and the result would be 25 or 10, but in many implementations you get a constant value like #<void> and since it is not a procedure you get the same error. Relying on one implementations method of using under specification makes gives you non portable code.
Passing arguments in wrong order
Imagine you have a fucntion like this:
(define (double v f)
(f (f v)))
(double 10 (lambda (v) (* v v))) ; ==> 10000
If you by error swapped the arguments:
(double (lambda (v) (* v v)) 10) ; ERROR: 10 is not a procedure
In higher order functions such as fold and map not passing the arguments in the correct order will produce a similar error.
Trying to apply as in Algol derived languages
In algol languages, like JavaScript and C++, when trying to apply fun with argument arg it looks like:
fun(arg)
This gets interpreted as two separate expressions in Scheme:
fun ; ==> valuates to a procedure object
(arg) ; ==> call arg with no arguments
The correct way to apply fun with arg as argument is:
(fun arg)
Superfluous parentheses
This is the general "catch all" other errors. Code like ((+ 4 5)) will not work in Scheme since each set of parentheses in this expression is a procedure call. You simply cannot add as many as you like and thus you need to keep it (+ 4 5).
Why allow these errors to happen?
Expressions in operator position and allow to call variables as library functions gives expressive powers to the language. These are features you will love having when you have become used to it.
Here is an example of abs:
(define (abs x)
((if (< x 0) - values) x))
This switched between doing (- x) and (values x) (identity that returns its argument) and as you can see it calls the result of an expression. Here is an example of copy-list using cps:
(define (copy-list lst)
(define (helper lst k)
(if (null? lst)
(k '())
(helper (cdr lst)
(lambda (res) (k (cons (car lst) res))))))
(helper lst values))
Notice that k is a variable that we pass a function and that it is called as a function. If we passed anything else than a fucntion there you would get the same error.
Is this unique to Scheme?
Not at all. All languages with one namespace that can pass functions as arguments will have similar challenges. Below is some JavaScript code with similar issues:
function double (f, v) {
return f(f(v));
}
double(v => v * v, 10); // ==> 10000
double(10, v => v * v);
; TypeError: f is not a function
; at double (repl:2:10)
// similar to having extra parentheses
function test (v) {
return v;
}
test(5)(6); // == TypeError: test(...) is not a function
// But it works if it's designed to return a function:
function test2 (v) {
return v2 => v2 + v;
}
test2(5)(6); // ==> 11
I'm trying to return a string value when I read this CSV file that contains cities and city attributes. Here is what I have so far:
(defn city [name]
(with-open [rdr (reader)]
(doseq [line (drop 1 (line-seq rdr))]
(def x2 line)
(def y (string/split x2 #","))
(if (= name (y 0))
(println line)
))))
(city "Toronto")
=> Toronto,43.666667,-79.416667,Canada,CA,Ontario,admin,5213000,3934421
I can get it to print out the row, but how would I go about getting the function to return the row instead, if that makes sense?
With how you have the code setup currently, you can't. doseq is meant to carry out side effects; it doesn't return anything. Rarely do you ever want to use doseq, and rarely should you ever use def inside of function definitions.
You want to find the first line where (= name (y 0)) is true. There's a few ways of approaching that. A basic way would be using loop and just stopping it once you find the line. I think using map or for to loop over the line-seq, then grabbing the first result would work out well here though:
(defn city [name]
(with-open [rdr (reader)]
(first
(for [line (drop 1 (line-seq rdr)) ; Same syntax here as with doseq
:let [y (string/split line #",")] ; Use let instead of def for local definitions
:when (= name (y 0))] ; Only add to the list ":when (= name (y 0))"
line))))
for is like Python's generator expression (if you're familiar with Python). It is not like a normal imperative for loop like in Java. The for will return a list of lines for which (= name (y 0)) was true. Because presumably there's only one such valid line in the file though, we only want one result, so we pass the list to first to get the first valid line found.
And note that for is lazy. This does not iterate the entire file before passing off to first. first requests the first element before for has even iterated, and no more iteration is done once a matching line is found.
The println function is meant for side-effects, and always returns nil. Adjust your function to return line as the last item after the if:
(if (= name (y 0))
line)
If you haven't seen it yet, look at
Brave Clojure (free & book)
Getting Clojure (book)
Clojure Cheatsheet
Here is a better organized version of the code. Your project dependencies will need to look like:
:dependencies [
[org.clojure/clojure "1.10.1"]
[prismatic/schema "1.1.12"]
[tupelo "0.9.168"]
]
and the code can then look like:
(ns tst.demo.core
(:use tupelo.core tupelo.test)
(:require
[clojure.string :as str]
[clojure.java.io :as io]
[tupelo.string :as ts]))
(def city-data
" city,lat,lon,country,ccode,province,unk1,unk2,unk3
Toronto,43.666667,-79.416667,Canada,CA,Ontario,admin,5213000,3934421
Chicago,40.666667,-99.416667,USA,US,dummy,admin,5213000,3934421
")
(defn city->fields [city-str]
(str/split city-str #","))
(defn city [name]
(with-open [rdr (io/reader (ts/string->stream city-data))]
(let [lines (mapv str/trim (line-seq rdr))
hdrs-line (first lines)
city-lines (rest lines)
cities-fields (mapv city->fields city-lines)
city-match (first (filterv #(= name (first %)) cities-fields))]
; debug printouts
(spyx hdrs-line)
(spyx-pretty city-lines)
(spyx city-match)
city-match))) ; <= return value
(dotest
(println "Result: " (city "Toronto"))
)
with result:
-------------------------------
Clojure 1.10.1 Java 13
-------------------------------
Testing tst.demo.core
hdrs-line => "city,lat,lon,country,ccode,province,unk1,unk2,unk3"
city-lines =>
("Toronto,43.666667,-79.416667,Canada,CA,Ontario,admin,5213000,3934421"
"Chicago,40.666667,-99.416667,USA,US,dummy,admin,5213000,3934421"
"")
city-match => ["Toronto" "43.666667" "-79.416667" "Canada" "CA" "Ontario" "admin" "5213000" "3934421"]
Result: [Toronto 43.666667 -79.416667 Canada CA Ontario admin 5213000 3934421]
I am going through the book Web Development with Clojure and it tells me to pass the handler (defined bellow) as a Var object instead of as the function itself, since the function can then change dynamically (this is what wrap-reload does).
The book says:
"Note that we have to create a var from the handler in order for this middleware
to work. This is necessary to ensure that the Var object containing the current
handler function is returned. If we used the handler instead then the app would
only see the original value of the function and changes would not be reflected."
I don't really understand what this means, are vars similar to c pointers?
(ns ring-app.core
(:require [ring.adapter.jetty :as jetty]
[ring.util.response :as response]
[ring.middleware.reload :refer [wrap-reload]]))
(defn handler [request]
(response/response
(str "<html>/<body> your IP is: " (:remote-addr request)
"</body></html>")))
(defn wrap-nocache [handler]
(fn [request]
(-> request
handler
(assoc-in [:headers "Pragma"] "no-cache"))))
Here is the handler call:
(defn -main []
(jetty/run-jetty
(wrap-reload (wrap-nocache (var handler)))
{:port 3001
:join? false}))
Yes, a Var in Clojure is similar to a C pointer. This is poorly documented.
Suppose you create a function fred as follows:
(defn fred [x] (+ x 1))
There are actually 3 things here. Firstly, fred is a symbol. There is a difference between a symbol fred (no quotes) and the keyword :fred (marked by the leading : char) and the string "fred" (marked by a double-quote at both ends). To Clojure, each of them is composed of 4 characters; i.e. neither the colon of the keyword nor the double-quotes of the string are included in their length or composition:
> (name 'fred)
"fred"
> (name :fred)
"fred"
> (name "fred")
"fred"
The only difference is how they are interpreted. A string is meant to represent user data of any sort. A keyword is meant to represent control information for the program, in a readable form (as opposed to "magic numbers" like 1=left, 2=right, we just use keywords :left and :right.
A symbol is meant to point to things, just like in Java or C. If we say
(let [x 1
y (+ x 1) ]
(println y))
;=> 2
then x points to the value 1, y points to the value 2, and we see the result printed.
the (def ...) form introduces an invisible third element, the Var. So if we say
(def wilma 3)
we now have 3 objects to consider. wilma is a symbol, which points to a Var, which in turn points to the value 3. When our program encounters the symbol wilma, it is evaluated to find the Var. Likewise, the Var is evaluated to yield the value 3. So it is like a 2-level indirection of pointers in C. Since both the symbol and the Var are "auto-evaluated", this happens automatically and invisibly and you don't have to think about the Var (indeed, most people aren't really aware the invisible middle step even exists).
For our function fred above, a similar situation exists, except the Var points to the anonymous function (fn [x] (+ x 1)) instead of the value 3 like with wilma.
We can "short-circuit" the auto-evaluation of the Var like:
> (var wilma)
#'clj.core/wilma
or
> #'wilma
#'clj.core/wilma
where the reader macro #' (pound-quote) is a shorthand way of calling the (var ...) special form. Keep in mind that a special form like var is a compiler built-in like if or def, and is not the same as a regular function. The var special form returns the Var object attached to the symbol wilma. The clojure REPL prints the Var object using the same shorthand, so both results look the same.
Once we have the Var object, auto-evaluation is disabled:
> (println (var wilma))
#'clj.core/wilma
If we want to get to the value that wilma points to, we need to use var-get:
> (var-get (var wilma))
3
> (var-get #'wilma)
3
The same thing works for fred:
> (var-get #'fred)
#object[clj.core$fred 0x599adf07 "clj.core$fred#599adf07"]
> (var-get (var fred))
#object[clj.core$fred 0x599adf07 "clj.core$fred#599adf07"]
where the #object[clj.core$fred ...] stuff is Clojure's way of representing a function object as a string.
With regard to the web server, it can tell via the var? function or otherwise if the supplied value is the handler function or the var which points to the handler function.
If you type something like:
(jetty/run-jetty handler)
the double auto-evaluation will yield the handler function object, which is passed to run-jetty. If, instead, you type:
(jetty/run-jetty (var handler))
then the Var which points to the handler function object will be passed to run-jetty. Then, run-jetty will have to use an if statement or equivalent to determine what it has received, and call (var-get ...) if it has received a Var instead of a function. Thus, each time through (var-get ...) will return the object to which the Var currently points. So, the Var acts like a global pointer in C, or a global "reference" variable in Java.
If you pass a function object to run-jetty, it saves a "local pointer" to the function object and there is no way for the outside world to change what the local pointer refers to.
You can find more details here:
http://clojure.org/reference/evaluation
http://clojure.org/reference/vars
Update
As OlegTheCat has pointed out, Clojure has yet another trick up its sleeve regarding Var objects that point to Clojure functions. Consider a simple function:
(defn add-3 [x] (+ x 3))
; `add-3` is a global symbol that points to
; a Var object, that points to
; a function object.
(dotest
(let [add-3-fn add-3 ; a local pointer to the fn object
add-3-var (var add-3)] ; a local pointer to the Var object
(is= 42 (add-3 39)) ; double deref from global symbol to fn object
(is= 42 (add-3-fn 39)) ; single deref from local symbol to fn object
(is= 42 (add-3-var 39))) ; use the Var object as a function
; => SILENT deref to fn object
If we treat a Var object as a function, Clojure will SILENTLY deref it into the function object, then invoke that function object with the supplied args. So we see that all three of add-3, add-3-fn and add-3-var will work. This is what is occurring in Jetty. It never realizes that you have given it a Var object instead of a function, but Clojure magically patches up that mismatch without telling you.
Sidebar: Please note this only works since our "jetty" is actually
the Clojure wrapper code ring.adapter.jetty, and not the actual Java
webserver Jetty. If you tried to depend on this trick with an
actual Java function instead of a Clojure wrapper, it would fail. Indeed, you must use a Clojure wrapper like proxy in order to pass a Clojure function to Java code.
You have no such guardian angel to save you if you use the Var object as anything other than a function:
(let [wilma-long wilma ; a local pointer to the long object
wilma-var (var wilma)] ; a local pointer to the Var object
(is (int? wilma-long)) ; it is a Long integer object
(is (var? wilma-var)) ; it is a Var object
(is= 4 (inc wilma)) ; double deref from global symbol to Long object
(is= 4 (inc wilma-long)) ; single deref from local symbol to Long object
(throws? (inc wilma-var)))) ; Var object used as arg => WILL NOT deref to Long object
So, if you are expecting a function and someone gives you a Var object that points to a function, then you are OK since Clojure silently fixes the problem. If you are expecting anything other than a function and someone gives you a Var object that points to that thing, then you are on your own.
Consider this helper function:
(defn unvar
"When passed a clojure var-object, returns the referenced value (via deref/var-get);
else returns arg unchanged. Idempotent to multiple calls."
[value-or-var]
(if (var? value-or-var)
(deref value-or-var) ; or var-get
value-or-var))
Now you can safely use the thing you were given:
(is= 42 (+ 39 (unvar wilma))
(+ 39 (unvar wilma-long))
(+ 39 (unvar wilma-var)))
Appendix
Notice that there are three dualities that can confuse the issue:
Both var-get and deref do the same thing with a Clojure Var
The reader macro #'xxx is translated into (var xxx)
The reader macro #xxx is translated into (deref xxx)
So we have (confusingly!) many ways of doing the same thing:
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(def wilma 3)
; `wilma` is a global symbol that points to
; a Var object, that points to
; a java.lang.Long object of value `3`
(dotest
(is= java.lang.Long (type wilma))
(is= 3 (var-get (var wilma)))
(is= 3 (var-get #'wilma))
; `deref` and `var-get` are interchangable
(is= 3 (deref (var wilma)))
(is= 3 (deref #'wilma))
; the reader macro `#xxx` is a shortcut that translates to `(deref xxx)`
(is= 3 #(var wilma))
(is= 3 ##'wilma)) ; Don't do this - it's an abuse of reader macros.
Another note
The (def ...) special form returns the clojure.lang.Var object it creates. Normally, a (def ...) form is used only at the top level in a Clojure source file (or at the REPL), so the return value is silently discarded. However, a reference to the created Var object can also be captured:
(let [p (def five 5)
q (var five)]
(is= clojure.lang.Var
(type p)
(type q))
(is= 6
(inc five)
(inc (var-get p))
(inc (deref q)))
(is (identical? p q)))
Here we create a global Var five pointing to the number 5. The return value of the def form is captured in the local value p. We use the (var ...) special form to get a reference q pointing to the same Var object.
The first test shows that p and q are both of type clojure.lang.Var. The middle test shows three ways of accessing the value 5. As expected, all retrieve the value 5 which is incremented to yield 6. The last test verifies that p and q both point to the same Java object (i.e. there is only one clojure.lang.Var object that points to the integer 5).
It is even possible for a Var to point to another Var instead of a data value:
(def p (def five 5)) ; please don't ever do this
While it works, I cannot think of a legitimate reason for ever doing this.
There are a couple of good answers already. Just wanted to add this caveat:
(defn f [] 10)
(defn g [] (f))
(g) ;;=> 10
(defn f [] 11)
;; -Dclojure.compiler.direct-linking=true
(g) ;;=> 10
;; -Dclojure.compiler.direct-linking=false
(g) ;;=> 11
So, when direct linking is on, the indirection via a var is replaced with a direct static invocation. Similar to the situation with the handler, but then with every var invocation, unless you explicitly refer to a var, like:
(defn g [] (#'f))
Hopefully this small example will get you on track:
> (defn your-handler [x] x)
#'your-handler
> (defn wrap-inc [f]
(fn [x]
(inc (f x))))
> #'wrap-inc
> (def your-app-with-var (wrap-inc #'your-handler))
#'your-app-with-var
> (def your-app-without-var (wrap-inc your-handler))
#'your-app-without-var
> (your-app-with-var 1)
2
> (your-app-without-var 1)
2
> (defn your-handler [x] 10)
#'your-handler
> (your-app-with-var 1)
11
> (your-app-without-var 1)
2
The intuition for this is when you use a var when creating your handler you are actually passing a "container" with some value, content of which can be changed in future by defining var with the same name. When you don't use var (like in your-app-without-var) you are passing a current value of this "container", which cannot be redefined in any way.
I am still very beginner in LISP and hope that you all could give me some suggestions on how to solve the following problem.
(CG-USER(1):defstruct Test()
(TestValue 10)
(TestChild 'none)
)
TEST
CG-USER(2): (defun testvalue(item)
(slot-value item 'TESTCHILD))
TESTVALUE
CG-USER(3):(setf TestObject(make-Test :TestChild '(TestObject2 B C)))
#S(TEST :NIL NIL :TESTVALUE 10 :TESTCHILD (TESTOBJECT2 B C))
CG-USER(4): (setf TestObject2(make-Test :TestChild '(D E F)))
#S(TEST :NIL NIL :TESTVALUE 10 :TESTCHILD (D E F))
CG-USER(5): (setf aaa (car (testvalue TestObject)))
TESTOBJECT2
CG-USER(6): (testvalue aaa)
Error: The slot TESTCHILD is missing from the object TESTOBJECT2 of class #<BUILT-IN-CLASS SYMBOL> during
operation SLOT-VALUE
[condition type: PROGRAM-ERROR]
The following is my code. As you can see, I am trying to read the details in the object TestObject2 through the function testvalue . My main purpose is to able to determine the name of the object (TestObject2) from the another object first (in this case, TestObject) as I do not know the name of the name TestObject2 yet.
But however, once I managed to successfully retrieve the name TestObject2 , when I try to access the elements inside the object (TestObject2) , it no longer identify the variable holding TestObject2 (in this case, "aaa") as an object. Thus, it could not read the testvalue of it, resulting in the error.
I am very beginner at LISP and I could not figure out on how to solve this. It would be really great if anyone could provide a sample solution for this. Thanks.
P/S: Also, may I ask what is the NIL NIL in this line? And any way to remove it?
#S(TEST :NIL NIL :TESTVALUE 10 :TESTCHILD (D E F))
Second question:
(defstruct Test()
(TestValue 10)
(TestChild 'none))
Let's format above slightly different:
(defstruct Test ; the structure has a name `TEST`
() ; the first slot is named `NIL`
(TestValue 10) ; the second slot is named `TESTVALUE`
(TestChild 'none)) ; the third slot is named `TESTCHILD`
So, it makes sense to remove the first slot... ;-) and we get:
(defstruct Test ; the structure has a name `TEST`
(TestValue 10) ; the first slot is named `TESTVALUE`
(TestChild 'none)) ; the second slot is named `TESTCHILD`
First question:
TESTOBJECT2 is a symbol. If the symbol has a value, you can retrieve it with the function SYMBOL-VALUE.
(symbol-value 'testobject2)
Generally you want to slim your code a bit:
CL-USER 42 > (defstruct test
(value 10)
(child 'none))
TEST
In above we don't need the test prefix. DEFSTRUCT already creates accessors with TEST- as the prefix.
CL-USER 43 > (setf test-object (make-test :child '(test-object2 b c)))
#S(TEST :VALUE 10 :CHILD (TEST-OBJECT2 B C))
Note that in your example and in above code, the :child is not a structure. It is just a list of three symbols.
CL-USER 44 > (setf test-object2 (make-test :child '(d e f)))
#S(TEST :VALUE 10 :CHILD (D E F))
Again, the child of above is a list of three symbols.
CL-USER 45 > (setf aaa (car (test-child test-object)))
TEST-OBJECT2
Above: The first of that list is the symbol TEST-OBJECT2.
CL-USER 46 > (test-value (symbol-value aaa))
10
Above: we can retrieve the symbol value of the symbol TEST-OBJECT2, which is the value of the variable AAA.
Suppose I have some function which returns a struct:
(struct layer (points lines areas))
(define (build-new-layer height)
...
(layer list-a list-b list-c))
I want to keep track of the last returned result something like:
(define top-side (build-new-layer 0)) ; store the first result
...
(set! top-side (build-new-layer 0.5)) ; throw away the first result and store the new one
However, for that particular code I get the error:
set!: assignment disallowed;
cannot modify a constant
constant: top-side
Please, tell me what would be the right way to do what I want
What language are you using? it seems it's a matter of configuration, because in principle what you're doing should work. Go to the "choose language" window (Ctrl+L in Windows), click on "show details" and see if one of the options of the language currently in use disallows redefinition of variables. Alternatively, try using a different language.
Depending on where exactly you're going to use the stored result (I can't tell from the code in the question), you could pass it around as function parameters, in such a way that using a global variable is no longer necessary. This might be a better idea, relying on global state and mutation (the set! operation) is discouraged in Scheme.
If you always want to keep around the last layer, then you might prefer setting the last-layer every time one is built. Like this.
(define last-layer #f)
(define build-new-layer
(let ((save-layer #f))
(lambda (height)
(let ((new-layer (layer list-a ...)))
(set! last-layer save-layer)
(set! save-layer new-layer)
new-layer))))
Note: if the real problem is the 'constant-ness' of last-layer then build yourself a little abstraction as:
(define-values (last-layer-get last-layer-set!)
(begin
(define last-layer-access
(let ((last-layer #f))
(lambda (type . layer)
(case type
((get) last-layer)
((set) (set! last-layer (car layer)))))))
(values (lambda () (last-layer-access 'get))
(lambda (layer) (last-layer-access 'set layer))))