For a structure and its instance defined as follows:
(struct dog (name breed age))
(define mydog (dog "lassie" "collie" 5))
(example from https://learnxinyminutes.com/docs/racket/)
The usual way to get a structure field is as follows:
(dog-name mydog) ; => "lassie"
Can there be a macro so that we can perform above using following dot notation:
(mydog.name) ; => "lassie"
Or, parentheses may not be needed:
mydog.name ; => "lassie"
Macro for dot notation is used on this page: http://www.greghendershott.com/fear-of-macros/pattern-matching.html#%28part._hash..refs%29 but I am not able to use it for above purpose. I also saw some details of structure macro on this page: Is struct a macro in Racket?
Edit: as suggested in comments and as mentioned on https://docs.racket-lang.org/reference/reader.html?q=read-cdot#%28part._parse-cdot%29, I tried read-cdot, but it does not work:
#lang racket
(read-cdot #t)
(struct dog (name breed age))
(define mydog (dog "lassie" "collie" 5))
(dog-name mydog) ; works
mydog.dog-name ; does not work;
The error is:
mydog.dog-name: unbound identifier in module in: mydog.dog-name
Related
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.
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.
guys,
look at the code first:
(defmacro map-remove- [v w]
`(dosync
(ref-set ~v (dissoc #~v (keyword ~w)))))
(defmacro set-remove- [v w]
`(dosync
(ref-set ~v (disj #~v ~w))))
(defmacro clean- [v]
`(dosync
(ref-set ~v (empty #~v))))
They work fine now , but I want write a more general macro to combine "map-remove-" and "set-remove-" in one. according my C/Java experience I chose "case" but obviously the case can't use in macro defination cause "The test-constants are not evaluated. They must be compile-time literals", the following code won't work:
(defmacro [x]
(case (type x) ;;;;;;;;;;; This will never work!directly reach to default clause
....))
anybody has any suggestion? very appreciate.
You can use the functions map? and set? to test whether a value is a map or a set respectively.
I'm not sure I have enough perspective on what you're trying to do here though - I've substituted a macro instead of a function, because I can't see the necessity for a macro in the sample you've given.
;; Create a function to remove in a different way when given a map/set and a string
(defn remove- [v w]
(dosync
(cond
(map? #v)
(ref-set v (dissoc #v (keyword w)))
(set? #v)
(ref-set v (disj #v w)))))
;; Set up some mutable refs
(def m (ref {:a 1 :b 2}))
(def s (ref #{"a" "b" "c"}))
;; Remove from the refs
(remove- m "b") => {:a 1}
(remove- s "b") => #{"a" "c"}
On a side note - are you sure you need to use refs? I know that coming from a C/Java background, mutability is the default, but I've never actually had to use it in Clojure so far. Clojure places a lot of emphasis on immutability, and most things can be done (often very elegantly) just by using functions on immutable values.
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).