3

Is it possible to macro returned macro? I would like to simplify the code maximum, and I can do this using a macro that returns the function. However, it is too much overhead and it is too slow. To keep the code more readable, I did not use type hints, but even with them my code is ~ 5x slower.

My English is not very precise, so I write what I have and what I wanted to have.

I have this...

(defmacro series [java-array]
  `(fn 
     ([i#]
      (aget ~java-array i#))
     ([start# stop#]
      (let [limit# (unchecked-subtract-int stop# start#)
            result# (double-array limit#)]
       (loop [i# 0]
        (if (< i# limit#)
          (let [r# (double (aget ~java-array i#))]
            (aset result# i# r#)
            (recur (inc i#)))
          result#))))))

I want something like this...

(defmacro series [java-array]
  `(defmacro blabla 
     ([i#]
      `(aget ~~java-array i#))
     ([start# stop#]
      `(let [limit# (unchecked-subtract-int stop# start#)
             result# (double-array limit#)]
         (loop [i# 0]
           (if (< i# limit#)
             (let [r# (double (aget ~~java-array i#))]
               (aset result# i# r#)
               (recur (inc i#)))
             result#))))))

But when I call this...

Wrong number of args (1) passed to: blabla

A simpler example.

I have a lot of java arrays. I do not want to use aget. I want macro to expand to (aget array-name i). I write a macro that expand to (fn [n] (aget array-name i)), but this is unnecessary overhead.

(defmacro series [arr]
  `(fn [i#]
     (aget (longs ~arr) (int i#))))

I now declare the series, such as "date", and call it in this way (date i), which will return me "i" element of the array.

1
  • 1
    You can call functions inside of macros, which run at compile-time and help you to manipulate the data structures (source code) passed as arguments to the macro. I think that's probably what you're looking for. I can't quite understand what you're doing from your example though. Could you please give a simpler example? Commented Jun 7, 2014 at 0:03

1 Answer 1

4

I think what you're looking for is a way to declare local macros within functions. The clojure.tools.macro package has a macrolet form, which I think you should be able to wrangle into creating a macro for local array lookups.

Here's a little example I put together:

; project.clj
(defproject testproject "1.0.0-SNAPSHOT"
  :description "FIXME: write description"
  :dependencies [[org.clojure/clojure "1.5.0"]
                 [org.clojure/tools.macro "0.1.2"]])

; src/testproject/core.clj
(ns testproject.core
  (:require [clojure.tools.macro :refer [macrolet]]))

(defn my-test []
  (let [n 1000
        arr (int-array n (range 10))]
    (macrolet [(lookup [i] (list `aget 'arr i))]
      (loop [i 0, acc 0]
        (if (< i n)
          (recur (inc i) (+ acc (lookup i)))
          acc)))))

In the example I use macrolet to declare a macro called lookup that will cause (lookup 5) to expand into (lookup arr 5), which is what I think you're looking for.

Notice how you have to be careful when referencing arr from within the local macro definition. If you just declared the macro as`(aget arr ~i), then it tries to find a fully-qualified symbol arr, which doesn't exist. You could alternatively declare the macro as `(aget ~'arr ~i), but I felt that (list `aget 'arr i) looked a lot nicer.

Now you can take it one step further and use defmacro to create a macro that includes macrolet inside, which you can use to simplify the declaration of arrays with a local lookup macro. We leave this as an exercise for the reader.

Since macros are so subtle, and nesting macros in macros only makes things worse, I decided it would probably be more helpful to just give an example and let you figure out how it works:

(defmacro lookup-binding [[fn-sym value] & body]
  (let [array-sym (symbol (str fn-sym "-raw"))]
    `(let [~array-sym ~value]
       (macrolet [(~fn-sym [i#] (list aget '~array-sym i#))]
         ~@body))))

(defn my-test2 []
  (let [n 1000]
    (lookup-binding [arr (int-array n (range 10))]
      (loop [i 0, acc 0]
        (if (< i n)
          (recur (inc i) (+ acc (arr i)))
          acc)))))

Disclaimer: I'm only trying to show that this is possible, not that it's a good idea. I personally don't think all this extra complexity is worth avoiding aget. I'd suggest just using aget since it's only a few extra characters, and it will make your code more clear/readable.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.