Call to defmacro with a quoted symbol - elisp

I have this data structure (basically):
(setq ssm-list '(tasklist
((id . "10525295")
(name . "Inbox")
(sort_order . "0"))))
This works for getting the name:
(defun ssm-list-get-prop (list prop)
(cdr (assoc prop (car (cdr list)))))
(ssm-list-get-prop slack-one-list 'name)
What'd I like is to create a macro that will create a defun with the name ssm-list-name (or ssm-list-id) as there are actually a lot more properties in the list.
So I tried this:
(defmacro ssm-list-prop-defun (field)
`(defun ,(intern (concat "ssm-list-" field))
(one-list)
(cdr (assoc ,field (car (cdr one-list))))))
(ssm-list-prop-defun 'name)
(ssm-list-prop-defun 'id)
But the last two calls failed miserably with (wrong-type-argument characterp quote) I tried putting symbol-name in the macro but that didn't help.
Is there a way to accomplish this?

You're very close, minor edits gets you a working solution. The problem is that you're mixing symbols and strings. This will work:
(defmacro ssm-list-prop-defun (field)
;; note that concat operates on strings
`(defun ,(intern (concat "ssm-list-" field))
(one-list)
;; note that you need a symbol here, so quote the
;; result of the call to intern
;; and, if you're always using symbols,
;; might as well use assq
(cdr (assq ',(intern field) (car (cdr one-list))))))
;; pass in a string
(ssm-list-prop-defun "name")
And here's the variant that uses a symbol:
;; variant that works off a symbol
(defmacro ssm-list-prop-defun (field)
`(defun ,(intern (concat "ssm-list-" (symbol-name field)))
(one-list)
(cdr (assq ',field (car (cdr one-list))))))
(ssm-list-prop-defun name)

I think you want to read about defstruct in the cl package: (info "(cl) Structures") (or http://www.gnu.org/software/emacs/manual/html_node/cl/Structures.html#Structures)

Related

How to change content of quoted value in Guile

I have an symbol that evaluates to (quote ("all")). I would like to append "tests" to the end of the list, and get (quote ("all" "tests")) but I didn't find how to :
(define make-flags ''("all"))
(append make-flags '("tests")) ; Resolves to (quote ("all") "tests")
I suppose I would have to remove the quote by evaluate the make-flags twice and re-quote it, but I didn't find how to.
Yes, you'd need to remove the quote first. Try this:
(define make-flags ''("all"))
`'(,(append (cadr make-flags) '("tests")))
=> ''("all" "tests")
It works because make-flags is just a list of this form: (quote (quote ("all"))) and we can navigate it in the usual way with car and cdr.
The second you evaluate ''("all") you get the list (quote ("all")) and it is not a quoted list at all. This is a list of to elements, the symbol quote and the list ("all"). If you want to add an element to the second element you do it by recreating the outer list and replacing the second with the new list with the added element:
(define (add-second-element ele lst)
`(,(car lst) (,#(cadr lst) ,ele) ,#(cddr lst)))
(add-second-element 'goofy '((donald dolly) (mickey) (chip dale)))
; ==> (donald (mickey goofy) (chip dale))
(add-second-element "tests" ''("all"))
; ==> (quote ("all" "tests"))
If you're not too familiar with quasiquote it's possible to do it without since quasiquote is just fancy syntax sugar for cons and append:
(define (add-second-element-2 ele lst)
(cons (car lst) (cons (append (cadr lst) (list ele)) (cddr lst))))
(add-second-element-2 'goofy '((donald dolly) (mickey) (chip dale)))
; ==> (donald (mickey goofy) (chip dale))
Of course if the first element is always quote and there are only two elements these can easily be simplified in both versions.

Why won't this Scheme function compile?

I have this scheme function that I'm supposed to run lists of integers on but I have no idea what the error means. The error states: "if: expected a question and two answers, but found 4 parts in: (if(null? list) '() (cons (+ 1 (car list)) (f(cdr list)))). What is missing from this function and what on earth does '() do? Thanks! I've never used Scheme before.
(define (f list)
(if (null? list)
’()
(cons (+ 1 (car list)) (f (cdr list)))))
The character you are using to quote the empty list is the right quotation mark ’, U+2019. Your code works fine if you use the ascii apostrophe ', U+0027
(define (f list)
(if (null? list)
'()
(cons (+ 1 (car list)) (f (cdr list)))))
You used the wrong quote (maybe a copy paste error?).
Use ' not ’.

Is it possible to reimplement "apply" in Scheme?

If I encounter a primitive procedure, do I always use the underlying scheme apply?
Assuming I do so, how would I re-implement apply for the scheme interpreter to interpret itself?
(define apply-1
(lambda (proc args)
(cond ((primitive? proc)
(apply proc args)) <-- How would I reimplement this
((eq? (car proc) 'closure)
(eval (cadr (cadr proc))
(bind (car (cdr proc)) args (caddr proc))))
(else error))))
Primitive-apply is the glue between how a primitive in your interpreter is implemented with the underlying implementation. Using hosts apply to apply primitives that are indeed procedures in the host system is a trick. You cannot make a host apply but you can make a interpreter primitive-apply differently that does less or supports other ways to package primitives. Eg.
;; define representations for primitives
(define prim-cons (list 'cons)) ; system unique
(define prim-car (list 'car))
...
;; define primitive?
(define (primitive? proc)
(or (eq? proc prim-cons)
(eq? proc prim-car)
...))
;; define primitive apply
(define (primitive-apply proc args)
(cond ((eq? proc prim-cons) args)
((eq? proc prim-car) (caar args))
...))
;; boot environment
(define primitive-environment
(list (cons #t prim-true)
(cons #f prim-false)
(cons '() prim-null)
(cons 'cons prim-cons)
(cond 'car prim-car)
...))
The fact is using apply is just a simplification since the actual primitive procedure is the resolved object. It doesn't always have to be like that. Imagine we try to optimize it a little:
;; define representations for primitives
(define prim-cons (list 'cons)) ; system unique
(define prim-car (list 'car))
;; make a list of primitives and their implementation
(define primitives
(list (cons prim-cons values)
(cons prim-car caar)))
;; define primitive?
(define (primitive? proc)
(assq proc primitives))
;; make apply-primitive
(define (apply-primitive proc args)
((cdr (primitive? proc)) args))
Still lot of boilerplate.. Why not move the whole primitive-list into the environment.
;; make tags
(define *primitive* (list 'primitive))
(define *constant* (list 'constant))
;; make a list of primitives and their implementation
(define boot-env
(list (list* 'cons *primitive* values)
(list* 'cons *primitive* caar)
...
(list* #f *constant* #f)
(list* #t *constant* #t)))
;; verify type
(define (is-type? x type)
(and (pair? proc)
(eq? (car proc) type)))
;; define primitive?
(define (primitive? proc)
(is-type proc *primitive*))
(define (constant? x)
(is-type x *constant*))
;; make apply-primitive
(define (apply-primitive proc args)
((cdr proc) args))
Now. For compound procedures we just have similar tag. eval itself become very small since you can even have *special-form* in your environment that does something similar making your eval just a case analysis between the types of values you eval and not special cases.
One of my thought about apply was that I wanted my apply to be the one called when you call the procedure apply from the interpreter. You can make use of apply, but apply actually needs to be handled by apply as well. You'll meet the same weird thing when you try to apply eval too.

Setting alist with dynamically evaluated variable name in Guile Scheme

Currently, I have the following alist:
(define globals '((objects test)))
The name of its variable is stored in another alist:
(define test '((loc globals) (other properties)))
I would like to easily retrieve the objects list in globals. I first tried this code.
(assoc 'objects
(cadr (assoc 'loc
test)))
However, that spit out an error:
ERROR: In procedure assoc: Wrong type argument in position 2 (expecting association list): globals
I searched and found this question, so I tried using eval.
(assoc 'objects
(eval '(cadr (assoc 'loc
test))
(interaction-environment)))
However, that spit out the same error as above! Does anyone know how to call assoc with the right argument?
EDIT (2014-10-27 21:27 EST): Thank you for all of the solutions. Unfortunately, the submitted examples will likely not work on the full code:
(define-syntax object
(syntax-rules ()
((_ name prop prop* ...)
(begin
(define name '(prop prop* ...))
(let* ((parent (cadr (assoc 'loc name)))
(objref (cdr (assoc 'objects parent))))
(set! parent
(assoc-set! parent
'objects
(append objref '(name)))))))))
(object my-object
(loc globals)
(name "Harry")
(desc "My Object"))
Try this:
(define globals '((objects test)))
(define test (list (list 'loc globals) '(other properties)))
; alternatively: (define test (list `(loc ,globals) '(other properties)))
(assoc 'objects
(cadr (assoc 'loc test)))
=> '(objects test)
In this case, we don't want to create a list of symbols such as this:
'(loc globals)
What we want, is a list whose second element is another list called globals:
(list 'loc globals)
Alternatively (as pointed by Chris in the comments) we can use quasiquoting to make explicit that we do want an actual value in the list, not that we forgot to quote an item:
`(loc ,globals)
UPDATE
This is a different problem, you should mention the whole context from the beginning. The assoc-set! procedure is still missing from the question, but you can try this to see if it's what you need:
(define-syntax object
(syntax-rules ()
((_ name prop prop* ...)
(begin
(define name '(prop prop* ...))
(let* ((parent (eval (cadr (assoc 'loc name)))) ; use Guile's eval
(objref (cdr (assoc 'objects parent))))
(set! parent
(assoc-set! parent
'objects
(append objref '(name)))))))))

go through an sxml file in schme

im trying to load a sxml file... i manage to do that in scheme. now i want to go through it using recursion and located items that i want. my code is like this,
(define file (read(open-input-file "test1.sxml")))
(define myfunc
(lambda (S)
(if (eq? "foo" (car S))
(display "found\n")
(display "not found\n")
)
(if (null? (cdr S))
(display "\n")
(myfunc(cdr S)))))
but it seems that it goes through only the first line of the sxml file. how can i make it go through all the file until the end?
1) You need to search through all of the sublists of the structure. Your code right now only looks at the top-most elements.
2) You usually don't want to have multiple statements in a row (like your two if statements)
3) You probably want to look for symbols, not strings, in your SXML file. Regardless, you must use equal? to compare strings.
Thus, your function becomes
(define myfunc
(lambda (S)
(cond
((null? S) #f)
((pair? (car S)) (or (myfunc (car S)) (myfunc (cdr S)))) ; search on sublists
((equal? "foo" (car S)) #t) ; if found, return true
(else (myfunc (cdr S))))))

Resources