Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 5966809
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 22, 20262026-05-22T19:51:09+00:00 2026-05-22T19:51:09+00:00

This is my first Clojure macro — I am an uber-noob. Yesterday I posted

  • 0

This is my first Clojure macro — I am an uber-noob.

Yesterday I posted and refined a string template replacement function. Several people suggested that the keys could be replaced at compile-time. Here is my first attempt:

(defn replace-templates*
  "Return a String with each occurrence of a substring of the form {key}
   replaced with the corresponding value from a map parameter.
   @param str the String in which to do the replacements
   @param m a map of template->value
   @thanks kotarak https://stackoverflow.com/questions/6112534/
     follow-up-to-simple-string-template-replacement-in-scala-and-clojure"
  [^String text m]
  (let [builder (StringBuilder.)]
    (loop [text text]
      (cond
        (zero? (count text))
        (.toString builder)

        (.startsWith text "{")
        (let [brace (.indexOf text "}")]
          (if (neg? brace)
            (.toString (.append builder text))
            (if-let [[_ replacement] (find m (subs text 1 brace))]
              (do
                (.append builder replacement)
                (recur (subs text (inc brace))))
              (do
                (.append builder "{")
                (recur (subs text 1))))))

        :else
        (let [brace (.indexOf text "{")]
          (if (neg? brace)
            (.toString (.append builder text))
            (do
              (.append builder (subs text 0 brace))
              (recur (subs text brace)))))))))

(def foo* 42)
(def m {"foo" foo*})

(defmacro replace-templates
  [text m]
  (if (map? m)
    `(str
      ~@(loop [text text acc []]
        (cond
          (zero? (count text))
          acc

          (.startsWith text "{")
          (let [brace (.indexOf text "}")]
            (if (neg? brace)
              (conj acc text)
              (if-let [[_ replacement] (find m (subs text 1 brace))]
                (recur (subs text (inc brace)) (conj acc replacement))
                (recur (subs text 1) (conj acc "{")))))

          :else
          (let [brace (.indexOf text "{")]
            (if (neg? brace)
              (conj acc text)
              (recur (subs text brace) (conj acc (subs text 0 brace))))))))
    `(replace-templates* ~text m)))

(macroexpand '(replace-templates "this is a {foo} test" {"foo" foo*}))
;=> (clojure.core/str "this is a " foo* " test")
(println (replace-templates "this is a {foo} test" {"foo" foo*}))
;=> this is a 42 test
(macroexpand '(replace-templates "this is a {foo} test" m))
;=> (user/replace-templates* "this is a {foo} test" user/m)
(println (replace-templates "this is a {foo} test" m))
;=> this is a 42 test

Is there a better way to write this macro? In particular, the expanded version of each value is not getting namespace-qualified.

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-22T19:51:10+00:00Added an answer on May 22, 2026 at 7:51 pm

    I would try reduce the repeated stuff. I adjusted the function to use your macro approach of an accumulator and let replace-templates* do the rest via (apply str ...). In that way one can re-use the function in the macro.

    (defn extract-snippets
      [^String text m]
      (loop [text     text
             snippets []]
        (cond
          (zero? (count text))
          snippets
    
          (.startsWith text "{")
          (let [brace (.indexOf text "}")]
            (if (neg? brace)
              (conj snippets text)
              (if-let [[_ replacement] (find m (subs text 1 brace))]
                (recur (subs text (inc brace)) (conj snippets replacement))
                (recur (subs text 1)           (conj snippets \{)))))
    
            :else
            (let [brace (.indexOf text "{")]
              (if (neg? brace)
                (conj snippets text)
                (recur (subs text brace) (conj snippets (subs text 0 brace))))))))
    
    (defn replace-templates*
      [text m]
      (apply str (extract-snippets text m)))
    
    (defmacro replace-templates
      [text m]
      (if (map? m)
        `(apply str ~(extract-snippets text m))
        `(replace-templates* ~text ~m)))
    

    Note: in your macro, you didn’t unquote the m. So it only works, because you had def’d it before. It wouldn’t with (let [m {"a" "b"}] (replace-templates "..." m)).

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

Clojure has a -> macro which inserts each expression recursively as the first argument
I'm very new in learning Clojure. This intended to be my first and very
This first bit works: $my_id = 617; $post_id_7 = get_post($my_id); $title = $post_id_7->post_excerpt; echo
Saw this piece of code in a Ruby on Rails book. This first one
problem euler #5 i found the solution but i don't know why this first
Using Dozer to map two objects, I have: /** /* This first class uses
#include <iostream> using namespace std; // This first class contains a vector and a
this my first shot at this awesome new (to me) programmers site, I hope
Code first: '''this is main structure of my program''' from twisted.web import http from
I did this: [User.first, User.last].to_xml and got this: <users type=array> <user> <created-at type=datetime>2010-03-16T06:40:51Z</created-at> <id

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.