I'm tring to define syntax class matching a parameter which is procedure.
I know how to match identifiers, expressions, another syntax classes.
This is my sample:
(define-syntax-class model-property
#:description "a model property"
#:attributes (name datatype guard)
(pattern name:id
#:with datatype #`null
#:with guard #'(lambda (value) value)
)
(pattern [name:id #:datatype [datatype:id #:not-null] #:guard guard:expr])
)
And I'd like to replace #:guard guard:expr with something like #:guard guard:procedure
I experimented with
(define-syntax-class model-property-guard
#:description "a property guard"
(pattern guard:expr
#:fail-when (procedure? #'guard)
"property guard should be procedure."))
Is it possible? How?
Macros run at compile-time, before the program is executed. You can’t, at compile-time, know what sort of value an expression will produce—the information simply doesn’t exist. (You could theoretically check such a thing in a language with a static type system, but #lang racket is dynamically typed.)
One thing you can do is put a contract on an expression so that it raises a runtime error if the contract isn’t matched. The expr/c syntax class is provided for this purpose. You use it like this:
(begin-for-syntax
(define-syntax-class model-property-guard
#:description "a property guard"
(pattern (~var guard (expr/c #'procedure?))
#:with c #'guard.c)))
(define-syntax (m stx)
(syntax-parse stx
[(_ guard:model-property-guard)
#'guard.c]))
Using the above definitions, writing (m add1) will successfully produce #<procedure:add1>, while writing (m 1) will fail at runtime with a contract violation:
m: contract violation
expected: procedure?
given: 1
in: procedure?
Note that the expansion must use guard.c in the expansion! The c attribute contains a modified expression that attaches a contract to the value, and using guard directly would merely pass the expression through unchanged, without the contract attached.
For some more examples of expr/c in action, see Contracts on Macro Sub-expressions.
Related
can someone point me to how I can write this in syntax-parse/case?
[(list e ...) #`(list #,(f #'e) ...)]
basically I'd like each element in the list to be processed individually by f in unsyntax. I don't think the above is the right syntax?
You can use unsyntax-splicing (which can be abbreviated as #,#) to embed result of list returning expression as individual elements of outer list. Then you can use map procedure to apply f over all elements of list returned by (syntax->list #'(e ...)) expression. In the end it will look like this:
#`(list #,#(map f (syntax->list #'(e ...))))
I'm trying to create a macro that makes a Clojure deftypeand which requires type hints to be generated. I currently have some test code:
(defmacro test-macro [n]
(let [obj-sym (gensym "obj")
p0 (with-meta 'p0 {:tag java.lang.Object})
p1 (with-meta 'p1 {:tag java.lang.Integer/TYPE})
r0 (with-meta 'remove {:tag java.lang.Boolean/TYPE})
r1 (with-meta 'remove {:tag java.lang.Object})]
`(deftype ~n [~obj-sym]
java.util.List
(~r0 [_ ~p0] (.remove ~obj-sym ~p0))
(~r1 [_ ~p1] (.remove ~obj-sym ~p1)))))
When it returns:
(test-macro test-it)
;clojure.lang.Compiler$CompilerException: java.lang.IllegalArgumentException: Must hint overloaded method: remove, ...
As a guide, it should produce something equivalent to:
(clojure.core/deftype ThisWorks [obj-5546]
java.util.List
(#^"boolean" remove [_ ^java.lang.Object p0-object] (.remove obj-5546 p0-object))
(^{:tag "java.lang.Object"} remove [_ ^int p0-int] (.remove obj-5546 p0-int)))
It looks like I am type hinting the wrong thing, or the meta data isn't being passed. Other than a fix for the immediate problem, bonus points if you can help out with the more general "meta" problem: how to debug a macro which manipulates metadata as macroexpand isn't very useful here..
Thanks
The :tag metadata is supposed to be a symbol, not a class object (since, after all, a symbol is all you can type as code in a non-macro situation: I can't embed the Class object int itself in my code!). So rather than Integer/TYPE, you just want 'int, and similarly for all your other typehints.
I have created a macro which creates a named dispatcher with 3 associates functions get-dispatcher, set-dispatcher and call-dispatcher to work with the dispatcher (they get a dispatching function, add one or call one). It all works just fine! However, now I want to automate the related functions names creation, thus I am putting all these internals of the macro into a let which defines that simple construction function. Note that in the code below only the get- function's name is constructed with that automation. The set- and call- ones name creation still has that manual smell.
(defmacro create-dispatcher [name]
;creates a set of dispatching functions tagged
`(do
;define dispatcher
(def ~(symbol name) ~(atom {}))
(let
[name-w-prefix (fn [x] (~(symbol (str x "-" name))))]
; -- define getter
(defn (name-w-prefix "get")
"get-dispatcher [tag]: get a dispatcher fn by tag"
(~'[] (println "no tag is provided for '" ~(str name) "' dispatcher"))
(~'[tag]
(do
(println "dispatcher '" ~(str name) "' called with '" ~'tag "' tag")
; return the tagged dispatcher
( (keyword ~'tag) #~(symbol name) )))
)
; -- define caller
(defn ~(symbol (str "call-" name))
"get-dispatcher [tag & args]: call a dispatcher fn by tag and apply to the args"
~'[tag & args]
(apply (~(symbol (str "get-" name)) ~'tag) ~'args)
)
; -- define setter
(defn ~(symbol (str "set-" name))
~'[tag fn]
"add-dispatcher [tag fn]: add a dispatcher fn associated with the tag"
(swap! ~(symbol name) assoc (keyword ~'tag) ~'fn)
)
)
; -- report
(println "created dispatcher set for '" ~(str name) "' ok!")
))
However, there is a problem. The name-w-prefix in the let statement binding causes errors. How can I fix that?
(also any advices on improvement are welcome since I am a newb and that is almost the first thing that I wrote in Clojure)
All symbols in a macro are resolved in the current namespace and expected to evaluate to a var. You could quote the name-w-prefix symbol, but that would risk colliding with symbols passed in to the macro during macro expansion. So, Clojure provides a special syntax for use in syntax-quoted forms for generating symbols - just append a # to the end of the symbol and Clojure will treat that as a quoted, auto-generated symbol. So in this case, replace occurrences of name-w-prefix with name-w-prefix# and you should be good to go.
Taking a step back and looking at what your overall goal is, I think you should move the name-w-prefix definition outside the syntax quotes and then use syntax-escape to call it. Otherwise, you'll get still more errors because defn requires a symbol, so once expanded the macro must produce a defn form that has a symbol as its second item. Something along the lines of:
(defmacro create-dispatcher [name]
(let [name-w-prefix #(symbol (str % "-" name))]
`(do
(def ~(symbol name) (atom {}))
(defn ~(name-w-prefix "get")
([] (println "no tag provided"))
([tag#] (println "called with tag" tag#))))))
Note that I've changed ~'[tag] to [tag#] in the defn body in accordance with what I was talking about above.
The macro, transform!, as defined below seems to work for => (transform! ["foo" 1 2 3]). The purpose is to take in a list, with the first element being a string that represents a function in the namespace. Then wrapping everything into swap!.
The problem is that transform! doesn't work for => (transform! coll), where (def coll ["foo" 1 2 3]). I am getting this mystery exception:
#<UnsupportedOperationException java.lang.UnsupportedOperationException: nth not supported on this type: Symbol>
The function:
(defmacro transform!
" Takes string input and update data with corresponding command function.
"
[[f & args]] ;; note double brackets
`(swap! *image* ~(ns-resolve *ns* (symbol f)) ~#args))
I find it strange that it works for one case and not the other.
Macros work at compile-time and operate on code, not on runtime data. In the case of (transform! coll), the macro is being passed a single, unevaluated argument: the symbol coll.
You don't actually need a macro; a regular function will suffice:
(defn transform! [[f & args]]
(apply swap! *image* (resolve (symbol f)) args)))
Resolving vars at runtime could be considered a code smell, so think about whether you really need to do it.
You're passing a symbol to the macro, namely coll. It will try to pull that symbol apart according to the destructuring statement [f & args], which won't be possible of course.
You can also use (resolve symbol) instead of (ns-resolve *ns* symbol).
I am currently developing a small CMS using the wonderful Enlive as templating engine. Enlive has a macro called at that takes a node (a map) specifying the HTML snippet and an arbitrary number of tuples each consisting of a selector (a vector) and a transformation (a closure).
(at a-node
[:a :selector] a-transformation
[:another :selector] another-transformation
...)
Now I want to generate the tuples depending upon incoming data/context. I have tried a lot of different things without success. For example
(let [this (repository/u "http://example.com/ACMECorp")
statements (repository/find-by-subject this)
context {:depth 1}]
`(at (snippet-for 'this 'context)
[root] (set-attr :about (str 'this))
~#(loop [rules []
st statements]
(if-not (seq st)
rules
(recur (conj rules
`[:> (attr= :property ~(str (repository/predicate (first st))))]
`(content (renderit ~(repository/object (first st)) 'context)))
(rest st))))))
Any help is highly appreciated.
-Jochen
Clojure is a Lisp, so you can always fallback to building the code you'd want as a list, and call eval on it. I'm not 100% sure about the code you gave, but I'd guess you just want to enclose your whole syntax-quote in an eval call.
(let [this (repository/u "http://example.com/ACMECorp")
statements (repository/find-by-subject this)
context {:depth 1}]
(eval `(at (snippet-for 'this 'context)
[root] (set-attr :about (str 'this))
~#(loop [rules []
st statements]
(if-not (seq st)
rules
(recur (conj rules
`[:> (attr= :property ~(str (repository/predicate (first st))))]
`(content (renderit ~(repository/object (first st)) 'context)))
(rest st)))))))
Not sure if they are interchangeable, but take a look at the at* function. Seems to me that your problem is at being a macro.
EDIT: They're not. Call it like this:
(at* a-node
[[:a :selector] a-transformation
[:another :selector] another-transformation
...])