I'm trying to write a clojure macro that will be used to generate multiple Java classes at compile time. I've found that I can add annotations to a class when I invoke gen-class outside of a macro. However, when I try to use gen-class inside a macro, the compiled class doesn't have annotations.
I boiled my problem down to this example:
(gen-class
:name ^{Deprecated true} Test1
:prefix Test1-
:methods [[^{Deprecated true} getValue [] Integer]])
(defn Test1-getValue [] 42)
(defmacro create-test-class [name x]
(let [prefix (str name "-")]
`(do
(gen-class
:name ~(with-meta name {Deprecated true})
:prefix ~(symbol prefix)
:methods [[~(with-meta 'getValue {Deprecated true}) [] Integer]])
(defn ~(symbol (str prefix "getValue")) [] ~x))))
(create-test-class Test2 56)
When I compile this file, it creates a Test1.class and Test2.class - I inspect both with Eclipse, and find that Test1 has both class-level and method-level #Deprecated annotations, but Test2.class that has no annotations. When I use macroexpand, it looks as though my Test2.class should be annotated:
user=> (set! *print-meta* true)
true
user=> (macroexpand '(create-test-class Test2 56))
(do (clojure.core/gen-class :name ^{java.lang.Deprecated true} Test2 :prefix Test2- :methods [[^{java.lang.Deprecated true} getValue [] java.lang.Integer]]) (user/defn Test2-getValue [] 56))
What am I doing wrong here?
Meikel Brandmeyer answered the question here:
https://groups.google.com/forum/#!topic/clojure/Ee1bVwcUT-c
"quote the annotation in the macro. (with-meta name `{Deprecated true}). Note the backtick."
Here is the working macro:
(defmacro create-test-class [name x]
(let [prefix (str name "-")]
`(do
(gen-class
:name ~(with-meta name `{Deprecated true})
:prefix ~(symbol prefix)
:methods [[~(with-meta 'getValue `{Deprecated true}) [] Integer]])
(defn ~(symbol (str prefix "getValue")) [] ~x))))
Related
I have defined two macros:
(defmacro property [name type]
`(setv ^(of Optional ~type) ~name None))
(defmacro data-type [vname &rest propertys]
`(with-decorator dataclass
(defclass ~vname []
~propertys)))
When called as:
(data-type my-test-type
(property name str)
(property unitprice float)
(property qty_on_hand int)
(property test int))
and expanded and translated into python it produces the following:
#dataclass
class my_test_type:
name: Optional[str] = None
unitprice: Optional[float] = None
qty_on_hand: Optional[int] = None
test: Optional[int] = None
[None, None, None, None]
Writing it without the nested macros still yeilds a list of one None:
(data-type my-test-type
(setv ^(of Optional str) name None
^(of Optional float) unitprice None
^(of Optional int) qty_on_hand None
^(of Optional int) test None))
#dataclass
class my_test_type:
name: Optional[str] = None
unitprice: Optional[float] = None
qty_on_hand: Optional[int] = None
test: Optional[int] = None
[None]
Where is this list of [None, None, None, None] coming from? While the list of none won't break anything it's still a little jarring and I wish I knew what would be a better way to write this macro to avoid the list of None.
It looks like you wrote ~propertys but meant ~#propertys. You want to splice each of the property declarations into the defclass, instead of combining them into a list. Making this change removes the trailing list of Nones.
I am new to Purescript and I am trying to write a function that
can take any record value and iterate over the fields and values and build
a querystring.
I am thinking something like:
buildQueryString :: forall a. PropertyTraversible r => r -> String
which I want to use like this:
buildQueryString {name: "joe", age: 10} -- returns: "name=joe&age=10"
Is there a way to write something like that in Purescript with existing idioms or do I have to create my own custom Type Class for this?
I'm sure that it can be shorter, but here is my implementation based on purescript-generic-rep (inspired by genericShow). This solution uses typeclasses - it seems to be standard approach with generic-rep:
module Main where
import Prelude
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Console (CONSOLE, log)
import Data.Foldable (intercalate)
import Data.Generic.Rep (class Generic, Constructor(..), Field(..), Product(..), Rec(..), from)
import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol)
class EncodeValue a where
encodeValue ∷ a → String
instance encodeValueString ∷ EncodeValue String where
encodeValue = id
instance encodeValueInt ∷ EncodeValue Int where
encodeValue = show
class EncodeFields a where
encodeFields :: a -> Array String
instance encodeFieldsProduct
∷ (EncodeFields a, EncodeFields b)
⇒ EncodeFields (Product a b) where
encodeFields (Product a b) = encodeFields a <> encodeFields b
instance encodeFieldsField
∷ (EncodeValue a, IsSymbol name)
⇒ EncodeFields (Field name a) where
encodeFields (Field a) =
[reflectSymbol (SProxy :: SProxy name) <> "=" <> encodeValue a]
buildQueryString
∷ ∀ a l n.
Generic n (Constructor l (Rec a))
⇒ (EncodeFields a)
⇒ n
→ String
buildQueryString n =
build <<< from $ n
where
build (Constructor (Rec fields)) = intercalate "&" <<< encodeFields $ fields
newtype Person =
Person
{ name ∷ String
, age ∷ Int
}
derive instance genericPerson ∷ Generic Person _
joe ∷ Person
joe = Person { name: "joe", age: 10 }
main :: forall e. Eff (console :: CONSOLE | e) Unit
main = do
log <<< buildQueryString $ joe
buildQueryString expects value of type with single constructor which contains a record (possibly just newtype) because it is impossible to derive a Generic instance for "unwrapped" Record type.
If you want to handle also Array values etc. then encodeValue should probably return values of type Array String.
This is possible with purescript-generics but it only works on nominal types, not on any record. But it saves you boilerplate, since you can just derive the instance for Generic, so it would work with any data or newtype without further modification.
Downside is, you have to make some assumptions about the type: like it only contains one record and the record does not contain arrays or other records.
Here is a hacky demonstration how it would work:
data Person = Person
{ name :: String
, age :: Int
}
derive instance genericPerson :: Generic Person
joe = Person { name: "joe", age: 10 }
build :: GenericSpine -> String
build (SRecord arr) = intercalate "&" (map (\x -> x.recLabel <> "=" <> build (x.recValue unit)) arr)
build (SProd _ arr) = fromMaybe "TODO" $ map (\f -> build (f unit)) (head arr)
build (SString s) = s
build (SInt i) = show i
build _ = "TODO"
test = build (toSpine joe)
purescript-generics-rep is newer, so possibly there is a better solution, maybe even on any record. I have not tried that (yet).
This is a my function
st3 = (x, y) ->
console.log "#{x?}, #{y?}"
if [x?, y?] is [true, true] # <- this is the line
'good'
'bad'
This is the output
true, true
bad
I want to be able to do a tuple comparison like it is in python.
In python, the if can be written roughly as
if (x, y) == (True, False):
return 'good'
The coffescript if loop is translated into javascript as such
if ([x != null, y != null] === [true, true]) {
'good';
}
That's why this will not evaluated to true.
Is there any alternative way to express it in coffeescript?
If you want to check to see if all of your arguments are not None, then I would do this:
def check_all(*args):
return all(arg is not None for arg in args)
If you wanted to check if they're all True (like literally True) then you could use
def check_all(*args):
return all(arg is True for arg in args)
If you wanted to take in a list (instead of variable number of parameters, remove the asterisk.
In Scala a future can fail and this can be found out asynchronously:
f onComplete {
case Success(_) => println("Great!")
case Failure(t) => println("An error has occurred: " + t.getMessage)
}
How would you 'translate' this into Clojure? My reading leads me to believe that the Clojure future/promise model is not as powerful as Scala's, and you can't just catch the failure like this. So what to do instead?
A Scala future never needs to be asked for it's value - when it is good and ready it will tell you what happened (including whether it failed - that's the crux of this question). This is what I meant by 'asynchronously'. A Scala future can be in one of three possible states - uncompleted, completed with failure, completed with success.
A typical example of a use case in Scala is a remote call that returns a Future[T], where T is the type of what you really want to get back. If the remote JVM is down then after a timeout the case Failure(t) will happen.
This is quite a straightforward model to work with. In the question I was asking for a simple alternative. As a side-comment it would be good to hear that Clojure intends to adopt the Scala Futures model at some point.
I remember futures in Scala are monads so searched algo.monads and fluokitten for something suitable. Finally I found Lenardo Borges' imminent library. I think it's what you want.
With this namespace declaration in place
(ns imminent-proof.core
(:require [imminent.core :as i]
[clojure.core.match :refer [match]])
(:import [imminent.result Success Failure]))
This is the failure case
(-> (i/future (/ 1 0))
(i/on-complete #(match [%]
[{Success v}] (prn "success: " v)
[{Failure e}] (prn "failure: " e))))
This the success case
(-> (i/future (Thread/sleep 1000) 42)
(i/on-complete #(match [%]
[{Success v}] (prn "success: " v)
[{Failure e}] (prn "failure: " e))))
And this the timeout case
(-> (i/future (Thread/sleep 1000) 42)
(i/await 500)
(i/on-complete #(match [%]
[{Success v}] (prn "success: " v)
[{Failure e}] (prn "failure: " e))))
I wonder if Clojure need specific construct to handle this situation with failed futures. The following line gives me the same functionality:
(defn f-cond[f func-ok func-fail]
(future (try (func-ok #f) (catch Exception e (func-fail e)))))
Then:
#(f-cond (future (throw (Exception. "hi")))
identity #(println "Failed: " (.getCause %)))
results in
Failed: #<Exception java.lang.Exception: hi>
the future macro is just wrapping a Java Future, and the deref reader macro is just syntax sugar for calling .get() against the future:
user=> (source future)
(defmacro future
"Takes a body of expressions and yields a future object that will
invoke the body in another thread, and will cache the result and
return it on all subsequent calls to deref/#. If the computation has
not yet finished, calls to deref/# will block, unless the variant of
deref with timeout is used. See also - realized?."
{:added "1.1"}
[& body] `(future-call (^{:once true} fn* [] ~#body)))
nil
user=> (source future-call)
(defn future-call
"Takes a function of no args and yields a future object that will
invoke the function in another thread, and will cache the result and
return it on all subsequent calls to deref/#. If the computation has
not yet finished, calls to deref/# will block, unless the variant
of deref with timeout is used. See also - realized?."
{:added "1.1"
:static true}
[f]
(let [f (binding-conveyor-fn f)
fut (.submit clojure.lang.Agent/soloExecutor ^Callable f)]
(reify
clojure.lang.IDeref
(deref [] (deref-future fut))
clojure.lang.IBlockingDeref
(deref
[ timeout-ms timeout-val]
(deref-future fut timeout-ms timeout-val))
clojure.lang.IPending
(isRealized [] (.isDone fut))
java.util.concurrent.Future
(get [] (.get fut))
(get [_ timeout unit] (.get fut timeout unit))
(isCancelled [] (.isCancelled fut))
(isDone [] (.isDone fut))
(cancel [_ interrupt?] (.cancel fut interrupt?)))))
nil
user=>
So testing for failure is no different than in java: you catch an ExecutionException, see java doc for Future:
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html#get()
So the solution is to catch the exception from the deref, as shown in other answers.
In Clojure, the branch of an if expression that doesn't match the condition is not evaluated, so no exception is thrown in evaluating the below expression:
=> (if nil (/ 1 0))
nil
However, macros will still be expanded before evaluating the if, so you can still get exceptions like so:
=> (if nil (proxy [nil] []))
CompilerException java.lang.NullPointerException, compiling:(NO_SOURCE_PATH:1)
I'm writing a macro for which the name of an interface will sometimes be provided as an arg, in which case the macro will generate the code for a call to proxy. Other times, the interface arg will be nil, and then the macro expands to something like (if nil (proxy [nil] []), so I get the above exception. An SSCE would be:
=> (defmacro make-listener [listener-name & methods]
`(if ~listener-name
(proxy [~listener-name] []
~#(map (fn [m] `(~m [e#])) methods))))
#'user/make-listener
=> (make-listener java.awt.event.ActionListener actionPerformed)
#<Object$ActionListener$46793e3a user.proxy$java.lang.Object$ActionListener$46793e3a#ed5b2>
=> (make-listener nil)
CompilerException java.lang.NullPointerException, compiling:(NO_SOURCE_PATH:1)
How can I write the macro in a way that doesn't throw an exception when no interface arg is provided?
If listener-name is nil then the macro should do nothing i.e you need to check for listener-name at macro expansion (not in the code the macro emit).
(defmacro make-listener [listener-name & methods]
(if listener-name
`(proxy [~listener-name] []
~#(map (fn [m] `(~m [e#])) methods))))