SlideShare a Scribd company logo
A little exercise with Macro

          Zehua Liu
Prerequisites
• Basic macro
  (defmacro mywhen [cond-exp & clauses]
    `(if ~cond-exp
       (do
         ~@clauses)))
• Java interop
  (String. "a string")
  (.substring "a string" 0 5)
Motivation
• A script to test a server with a request
  /response protocol
• To send a request to the server, reuse Java
  classes that represent requests in server code
Java Request Classes
Java Request Classes
Sending Login Request
Sending SendMessage Request
Sending Requests
•   Many other types of requests
•   Lots of duplicate codes
•   Refactor them!
•   function?
Refactor using function

(defn send-request [req-pkt sid]
  (let [[new-sid resp-pkts] (send-req req-pkt sid)]
    (if-let [err-msg (check-resp-error resp-pkts)]
      (do
        (println
          (format
            "ERROR: %s failed, sid='%s',req='%s',err='%s'"
            (.getName req-pkt) sid req-pkt err-msg))
        [nil nil])
      [new-sid resp-pkts])))
Refactor using function
Refactor using function
• Not too bad
• Can we do better / differently?
• macro?
Login Request Again
Make it work for Login Request
Make it work for Login Request
(defmacro send-request [request username password sid]
  `(let [req-pkt# (doto (requests.LoginRequest.)
                    (.setUsername ~username)
                    (.setPassword ~password))
        [new-sid# resp-pkts#] (send-req req-pkt# ~sid)]
    (if-let [err-msg# (check-resp-error resp-pkts#)]
      (do
        (println
          (format
            "ERROR: %s failed, sid='%s',req='%s',err='%s'"
            ~(name request) ~sid req-pkt# err-msg#))
        [nil nil])
      [new-sid# resp-pkts#])))

(defn login [:Login username password sid]
  (send-request username password sid))
Make it work for Login Request
Make it work for Login Request
(defmacro send-request [request username password sid]
`(let [req-pkt# (doto (new ~(symbol
                                (str "requests."
                                     (name request)
                                     "Request")))
                    (.setUsername ~username)
                    (.setPassword ~password))
        [new-sid# resp-pkts#] (send-req req-pkt# ~sid)]
    (if-let [err-msg# (check-resp-error resp-pkts#)]
      (do
        (println
          (format
            "ERROR: %s failed, sid='%s',req='%s',err='%s'"
            ~(name request) ~sid req-pkt# err-msg#))
        [nil nil])
      [new-sid# resp-pkts#])))

(defn login [username password sid]
  (send-request :Login username password sid))
Make it work for Login Request
(.setUsername ~username) <==>
  (. setUsername ~username)

(.setUsername ~username) <==>
(. (symbol (str "set" (name :Username))) ~username)

(.setUsername ~username)
(.setPassword ~password) <==>
~@(map (fn [[pn pv]]
           `(. ~(symbol (str "set" (name pn))) ~pv))
   {:Username username :Password password})
Make it work for Login Request
(defmacro send-request [request param-map sid]
  `(let [req-pkt# (doto (new ~(symbol
                                (str "requests." (name request)
                                      "Request")))
                    ~@(map (fn [[pn pv]]
                             `(. ~(symbol (str "set" (name pn))) ~pv))
                          param-map))
        [new-sid# resp-pkts#] (send-req req-pkt# ~sid)]
    (if-let [err-msg# (check-resp-error resp-pkts#)]
      (do
        (println
          (format
            "ERROR: %s failed, sid='%s',req='%s',err='%s'"
            ~(name request) ~sid req-pkt# err-msg#))
        [nil nil])
      [new-sid# resp-pkts#])))

(defn login [username password sid]
  (send-request :Login
                {:Username username :Password password} sid))
Refactor using macro

(defn login [username password sid]
  (send-request :Login
                {:Username username
                 :Password password}
                sid))

(defn send-message [message dest-username type sid]
  (send-request :SendMessage
                {:Message message
                 :DestUsername dest-username
                 :Type type}
                sid))
Refactor using macro
•   It works! Hooray!
•   Let’s use it for more fancy stuff.
•   Optional request fields?
•   On server side, SendMessage type default to
    1, if not specified
Optional field

(defn send-message [message dest-username type sid]
  (send-request :SendMessage
                {:Message message
                 :DestUsername dest-username
                 :Type type}
                sid))

(defn send-message [message dest-username sid]
  (send-request :SendMessage
                {:Message message
                 :DestUsername dest-username}
                sid))
Optional field

(defn send-message
  ([message dest-username sid]
    (send-message message dest-username nil sid))
  ([message dest-username type sid]
    (let [param-map-base {:Message message
                          :DestUsername dest-username}
          param-map (if type
                      (assoc param-map-base :Type type)
                      param-map-base)]
    (send-request :SendMessage param-map sid))))
Optional field, FAILED

(defn send-message
  ([message dest-username sid]
    (send-message message dest-username nil sid))
  ([message dest-username type sid]
    (let [param-map-base {:Message message
                          :DestUsername dest-username}
          param-map (if type
                      (assoc param-map-base :Type type)
                      param-map-base)]
    (send-request :SendMessage param-map sid))))

CompilerException java.lang.IllegalArgumentException: Don't
know how to create ISeq from: clojure.lang.Symbol, compiling:
(NO_SOURCE_PATH:10)
Optional field, FAILED
Optional field, FAILED
• macro is evaluated at compile time, not at run
  time
• macro evaluator only knows about symbols
  – {:Username username :Password
    password} is a map (keywords to symbols)
  – But param-map is a symbol
  – At compile time, macro evaluator does not know
    the run time value that the symbol param-map
    represents
• How do we fix it?
Optional field, Fixing it
• How do we fix it?
• One thought:
  – Tell the macro the complete list of fields, and have
    it generate codes like below for every field:
(if-let [v (:Type param-map)]
  (. setType v))
• And then param-map can be a symbol whose
  value macro evalutor does not need to know,
  its value is only needed at run time.
Optional field, Fixing it
(defmacro send-request [request param-list param-map sid]
  (let [param-map# param-map
        r (gensym)]
  `(let [req-pkt# (let [~r (new ~(symbol
                                 (str "requests." (name request)
                                      "Request")))]
                    ~@(map (fn [pn]
                             `(if-let [pv# (~pn ~param-map#)]
                               (. ~r ~(symbol (str "set" (name pn))) pv#)))
                          param-list))
        [new-sid# resp-pkts#] (send-req req-pkt# ~sid)]
    (if-let [err-msg# (check-resp-error resp-pkts#)]
      (do
        (println
          (format
            "ERROR: %s failed, sid='%s',req='%s',err='%s'"
            ~(name request) ~sid req-pkt# err-msg#))
        [nil nil])
      [new-sid# resp-pkts#]))))
Optional field, Fixing it
(defn login [username password sid]
  (send-request :Login [:Username :Password]
                {:Username username :Password password}
                sid))

(defn send-message
  ([message dest-username sid]
    (send-message message dest-username nil sid))
  ([message dest-username type sid]
    (let [param-map-base {:Message message
                           :DestUsername dest-username}
          param-map (if type
                       (assoc param-map-base :Type type)
                       param-map-base)]
    (send-request :SendMessage
                   [:Message :DestUsername :Type]
                   param-map sid))))
Optional field, Fixing it
(macroexpand '(send-request :SendMessage
                            [:Message :DestUsername :Type] param-map nil))

(let*
  [req-pkt__625__auto__
   (clojure.core/let [G__671 (new requests.SendMessageRequest)]
      (clojure.core/if-let [pv__624__auto__ (:Message param-map)]
        (. G__671 setMessage pv__624__auto__))
      (clojure.core/if-let [pv__624__auto__ (:DestUsername param-map)]
        (. G__671 setDestUsername pv__624__auto__))
      (clojure.core/if-let [pv__624__auto__ (:Type param-map)]
        (. G__671 setType pv__624__auto__)))
   vec__672 (scratch.core/send-req req-pkt__625__auto__ nil)
   new-sid__626__auto__ (clojure.core/nth vec__672 0 nil)
   resp-pkts__627__auto__ (clojure.core/nth vec__672 1 nil)]
  (clojure.core/if-let [err-msg__628__auto__
                (scratch.core/check-resp-error resp-pkts__627__auto__)]
    (do
       (clojure.core/println
         (clojure.core/format
           "ERROR: %s failed, sid='%s',req='%s',err='%s'"
           "SendMessage" nil req-pkt__625__auto__ err-msg__628__auto__))
       [nil nil])
    [new-sid__626__auto__ resp-pkts__627__auto__]))
Lessons Learned
• macro is evaluated at compile time, not at run time
• macro evaluator treats code as data
   – {:Username username :Password password}
     is a map of keywords to symbols, not keywrods to strings
     (or whatever type username/password might be)
   – But param-map is a symbol
• At compile time, macro evaluator treats ref/var as
  symbol, knowing nothing about their run time values

More Related Content

What's hot

Desarrollo de módulos en Drupal e integración con dispositivos móviles
Desarrollo de módulos en Drupal e integración con dispositivos móvilesDesarrollo de módulos en Drupal e integración con dispositivos móviles
Desarrollo de módulos en Drupal e integración con dispositivos móviles
Luis Curo Salvatierra
 
From mysql to MongoDB(MongoDB2011北京交流会)
From mysql to MongoDB(MongoDB2011北京交流会)From mysql to MongoDB(MongoDB2011北京交流会)
From mysql to MongoDB(MongoDB2011北京交流会)
Night Sailer
 
MTDDC 2010.2.5 Tokyo - Brand new API
MTDDC 2010.2.5 Tokyo - Brand new APIMTDDC 2010.2.5 Tokyo - Brand new API
MTDDC 2010.2.5 Tokyo - Brand new API
Six Apart KK
 

What's hot (20)

Desarrollo de módulos en Drupal e integración con dispositivos móviles
Desarrollo de módulos en Drupal e integración con dispositivos móvilesDesarrollo de módulos en Drupal e integración con dispositivos móviles
Desarrollo de módulos en Drupal e integración con dispositivos móviles
 
Simple Ways To Be A Better Programmer (OSCON 2007)
Simple Ways To Be A Better Programmer (OSCON 2007)Simple Ways To Be A Better Programmer (OSCON 2007)
Simple Ways To Be A Better Programmer (OSCON 2007)
 
From mysql to MongoDB(MongoDB2011北京交流会)
From mysql to MongoDB(MongoDB2011北京交流会)From mysql to MongoDB(MongoDB2011北京交流会)
From mysql to MongoDB(MongoDB2011北京交流会)
 
Beyond Breakpoints: Advanced Debugging with XCode
Beyond Breakpoints: Advanced Debugging with XCodeBeyond Breakpoints: Advanced Debugging with XCode
Beyond Breakpoints: Advanced Debugging with XCode
 
Programming Lisp Clojure - 2장 : 클로저 둘러보기
Programming Lisp Clojure - 2장 : 클로저 둘러보기Programming Lisp Clojure - 2장 : 클로저 둘러보기
Programming Lisp Clojure - 2장 : 클로저 둘러보기
 
Ruby - Uma Introdução
Ruby - Uma IntroduçãoRuby - Uma Introdução
Ruby - Uma Introdução
 
MTDDC 2010.2.5 Tokyo - Brand new API
MTDDC 2010.2.5 Tokyo - Brand new APIMTDDC 2010.2.5 Tokyo - Brand new API
MTDDC 2010.2.5 Tokyo - Brand new API
 
Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)
 
Python 1
Python 1Python 1
Python 1
 
My First Ruby
My First RubyMy First Ruby
My First Ruby
 
G*ワークショップ in 仙台 Grails(とことん)入門
G*ワークショップ in 仙台 Grails(とことん)入門G*ワークショップ in 仙台 Grails(とことん)入門
G*ワークショップ in 仙台 Grails(とことん)入門
 
issue35 zh-CN
issue35 zh-CNissue35 zh-CN
issue35 zh-CN
 
Raleigh Web Design Meetup Group - Sass Presentation
Raleigh Web Design Meetup Group - Sass PresentationRaleigh Web Design Meetup Group - Sass Presentation
Raleigh Web Design Meetup Group - Sass Presentation
 
Windows Server 2008 (PowerShell Scripting Uygulamaları)
Windows Server 2008 (PowerShell Scripting Uygulamaları)Windows Server 2008 (PowerShell Scripting Uygulamaları)
Windows Server 2008 (PowerShell Scripting Uygulamaları)
 
F# in the real world (NDC)
F# in the real world (NDC)F# in the real world (NDC)
F# in the real world (NDC)
 
Internationalizing CakePHP Applications
Internationalizing CakePHP ApplicationsInternationalizing CakePHP Applications
Internationalizing CakePHP Applications
 
ATK 'Beyond The Pizza Guides'
ATK 'Beyond The Pizza Guides'ATK 'Beyond The Pizza Guides'
ATK 'Beyond The Pizza Guides'
 
Ruby 1.9
Ruby 1.9Ruby 1.9
Ruby 1.9
 
Implementing a many-to-many Relationship with Slick
Implementing a many-to-many Relationship with SlickImplementing a many-to-many Relationship with Slick
Implementing a many-to-many Relationship with Slick
 
Temporary Cache Assistance (Transients API): WordCamp Birmingham 2014
Temporary Cache Assistance (Transients API): WordCamp Birmingham 2014Temporary Cache Assistance (Transients API): WordCamp Birmingham 2014
Temporary Cache Assistance (Transients API): WordCamp Birmingham 2014
 

Viewers also liked

Macros in Clojure
Macros in ClojureMacros in Clojure
Macros in Clojure
sohta
 
Clojureシンタックスハイライター開発から考えるこれからのlispに必要なもの
Clojureシンタックスハイライター開発から考えるこれからのlispに必要なものClojureシンタックスハイライター開発から考えるこれからのlispに必要なもの
Clojureシンタックスハイライター開発から考えるこれからのlispに必要なもの
sohta
 

Viewers also liked (19)

Clojure: Practical functional approach on JVM
Clojure: Practical functional approach on JVMClojure: Practical functional approach on JVM
Clojure: Practical functional approach on JVM
 
A Dive Into Clojure
A Dive Into ClojureA Dive Into Clojure
A Dive Into Clojure
 
Writing Macros
Writing MacrosWriting Macros
Writing Macros
 
Clojure Macros Workshop: LambdaJam 2013 / CUFP 2013
Clojure Macros Workshop: LambdaJam 2013 / CUFP 2013Clojure Macros Workshop: LambdaJam 2013 / CUFP 2013
Clojure Macros Workshop: LambdaJam 2013 / CUFP 2013
 
不自然なcar/ナチュラルにconsして
不自然なcar/ナチュラルにconsして不自然なcar/ナチュラルにconsして
不自然なcar/ナチュラルにconsして
 
Patterns
PatternsPatterns
Patterns
 
Macros in Clojure
Macros in ClojureMacros in Clojure
Macros in Clojure
 
入門ClojureScript
入門ClojureScript入門ClojureScript
入門ClojureScript
 
Continuation Passing Style and Macros in Clojure - Jan 2012
Continuation Passing Style and Macros in Clojure - Jan 2012Continuation Passing Style and Macros in Clojure - Jan 2012
Continuation Passing Style and Macros in Clojure - Jan 2012
 
Clojure的魅力
Clojure的魅力Clojure的魅力
Clojure的魅力
 
Clojure概览
Clojure概览Clojure概览
Clojure概览
 
Stefan Richter - Writing simple, readable and robust code: Examples in Java, ...
Stefan Richter - Writing simple, readable and robust code: Examples in Java, ...Stefan Richter - Writing simple, readable and robust code: Examples in Java, ...
Stefan Richter - Writing simple, readable and robust code: Examples in Java, ...
 
Clojureシンタックスハイライター開発から考えるこれからのlispに必要なもの
Clojureシンタックスハイライター開発から考えるこれからのlispに必要なものClojureシンタックスハイライター開発から考えるこれからのlispに必要なもの
Clojureシンタックスハイライター開発から考えるこれからのlispに必要なもの
 
(map Clojure everyday-tasks)
(map Clojure everyday-tasks)(map Clojure everyday-tasks)
(map Clojure everyday-tasks)
 
Clojure: a LISP for the JVM
Clojure: a LISP for the JVMClojure: a LISP for the JVM
Clojure: a LISP for the JVM
 
Introduction to clojure
Introduction to clojureIntroduction to clojure
Introduction to clojure
 
DSL in Clojure
DSL in ClojureDSL in Clojure
DSL in Clojure
 
プログラミング言語Clojureのニャンパスでの活用事例
プログラミング言語Clojureのニャンパスでの活用事例プログラミング言語Clojureのニャンパスでの活用事例
プログラミング言語Clojureのニャンパスでの活用事例
 
Clojure from ground up
Clojure from ground upClojure from ground up
Clojure from ground up
 

Similar to A little exercise with clojure macro

R is a very flexible and powerful programming language, as well as a.pdf
R is a very flexible and powerful programming language, as well as a.pdfR is a very flexible and powerful programming language, as well as a.pdf
R is a very flexible and powerful programming language, as well as a.pdf
annikasarees
 
Marrow: A Meta-Framework for Python 2.6+ and 3.1+
Marrow: A Meta-Framework for Python 2.6+ and 3.1+Marrow: A Meta-Framework for Python 2.6+ and 3.1+
Marrow: A Meta-Framework for Python 2.6+ and 3.1+
ConFoo
 
Refactoring to Macros with Clojure
Refactoring to Macros with ClojureRefactoring to Macros with Clojure
Refactoring to Macros with Clojure
Dmitry Buzdin
 
AST - the only true tool for building JavaScript
AST - the only true tool for building JavaScriptAST - the only true tool for building JavaScript
AST - the only true tool for building JavaScript
Ingvar Stepanyan
 
R (Shiny Package) - Server Side Code for Decision Support System
R (Shiny Package) - Server Side Code for Decision Support SystemR (Shiny Package) - Server Side Code for Decision Support System
R (Shiny Package) - Server Side Code for Decision Support System
Maithreya Chakravarthula
 
Gearmam, from the_worker's_perspective copy
Gearmam, from the_worker's_perspective copyGearmam, from the_worker's_perspective copy
Gearmam, from the_worker's_perspective copy
Brian Aker
 
Gearmam, from the_worker's_perspective copy
Gearmam, from the_worker's_perspective copyGearmam, from the_worker's_perspective copy
Gearmam, from the_worker's_perspective copy
Brian Aker
 

Similar to A little exercise with clojure macro (20)

R is a very flexible and powerful programming language, as well as a.pdf
R is a very flexible and powerful programming language, as well as a.pdfR is a very flexible and powerful programming language, as well as a.pdf
R is a very flexible and powerful programming language, as well as a.pdf
 
Deep Dive: AWS Command Line Interface
Deep Dive: AWS Command Line InterfaceDeep Dive: AWS Command Line Interface
Deep Dive: AWS Command Line Interface
 
Super Advanced Python –act1
Super Advanced Python –act1Super Advanced Python –act1
Super Advanced Python –act1
 
A Shiny Example-- R
A Shiny Example-- RA Shiny Example-- R
A Shiny Example-- R
 
Marrow: A Meta-Framework for Python 2.6+ and 3.1+
Marrow: A Meta-Framework for Python 2.6+ and 3.1+Marrow: A Meta-Framework for Python 2.6+ and 3.1+
Marrow: A Meta-Framework for Python 2.6+ and 3.1+
 
Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2
 
Refactoring to Macros with Clojure
Refactoring to Macros with ClojureRefactoring to Macros with Clojure
Refactoring to Macros with Clojure
 
AST - the only true tool for building JavaScript
AST - the only true tool for building JavaScriptAST - the only true tool for building JavaScript
AST - the only true tool for building JavaScript
 
Introduction to Groovy
Introduction to GroovyIntroduction to Groovy
Introduction to Groovy
 
Fabric Python Lib
Fabric Python LibFabric Python Lib
Fabric Python Lib
 
Python 내장 함수
Python 내장 함수Python 내장 함수
Python 내장 함수
 
DataMapper
DataMapperDataMapper
DataMapper
 
Django - sql alchemy - jquery
Django - sql alchemy - jqueryDjango - sql alchemy - jquery
Django - sql alchemy - jquery
 
R (Shiny Package) - Server Side Code for Decision Support System
R (Shiny Package) - Server Side Code for Decision Support SystemR (Shiny Package) - Server Side Code for Decision Support System
R (Shiny Package) - Server Side Code for Decision Support System
 
Gearmam, from the_worker's_perspective copy
Gearmam, from the_worker's_perspective copyGearmam, from the_worker's_perspective copy
Gearmam, from the_worker's_perspective copy
 
Gearmam, from the_worker's_perspective copy
Gearmam, from the_worker's_perspective copyGearmam, from the_worker's_perspective copy
Gearmam, from the_worker's_perspective copy
 
WordPress Cuztom Helper
WordPress Cuztom HelperWordPress Cuztom Helper
WordPress Cuztom Helper
 
(BDT401) Big Data Orchestra - Harmony within Data Analysis Tools | AWS re:Inv...
(BDT401) Big Data Orchestra - Harmony within Data Analysis Tools | AWS re:Inv...(BDT401) Big Data Orchestra - Harmony within Data Analysis Tools | AWS re:Inv...
(BDT401) Big Data Orchestra - Harmony within Data Analysis Tools | AWS re:Inv...
 
Designing a database like an archaeologist
Designing a database like an archaeologistDesigning a database like an archaeologist
Designing a database like an archaeologist
 
Php functions
Php functionsPhp functions
Php functions
 

Recently uploaded

Future Visions: Predictions to Guide and Time Tech Innovation, Peter Udo Diehl
Future Visions: Predictions to Guide and Time Tech Innovation, Peter Udo DiehlFuture Visions: Predictions to Guide and Time Tech Innovation, Peter Udo Diehl
Future Visions: Predictions to Guide and Time Tech Innovation, Peter Udo Diehl
Peter Udo Diehl
 
Essentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with ParametersEssentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with Parameters
Safe Software
 

Recently uploaded (20)

UiPath Test Automation using UiPath Test Suite series, part 2
UiPath Test Automation using UiPath Test Suite series, part 2UiPath Test Automation using UiPath Test Suite series, part 2
UiPath Test Automation using UiPath Test Suite series, part 2
 
Assuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyesAssuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyes
 
Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)
Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)
Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)
 
Connector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonConnector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a button
 
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered QualitySoftware Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
 
Future Visions: Predictions to Guide and Time Tech Innovation, Peter Udo Diehl
Future Visions: Predictions to Guide and Time Tech Innovation, Peter Udo DiehlFuture Visions: Predictions to Guide and Time Tech Innovation, Peter Udo Diehl
Future Visions: Predictions to Guide and Time Tech Innovation, Peter Udo Diehl
 
How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...
 
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptxIOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
 
Unpacking Value Delivery - Agile Oxford Meetup - May 2024.pptx
Unpacking Value Delivery - Agile Oxford Meetup - May 2024.pptxUnpacking Value Delivery - Agile Oxford Meetup - May 2024.pptx
Unpacking Value Delivery - Agile Oxford Meetup - May 2024.pptx
 
UiPath Test Automation using UiPath Test Suite series, part 1
UiPath Test Automation using UiPath Test Suite series, part 1UiPath Test Automation using UiPath Test Suite series, part 1
UiPath Test Automation using UiPath Test Suite series, part 1
 
Essentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with ParametersEssentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with Parameters
 
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
 
Integrating Telephony Systems with Salesforce: Insights and Considerations, B...
Integrating Telephony Systems with Salesforce: Insights and Considerations, B...Integrating Telephony Systems with Salesforce: Insights and Considerations, B...
Integrating Telephony Systems with Salesforce: Insights and Considerations, B...
 
10 Differences between Sales Cloud and CPQ, Blanka Doktorová
10 Differences between Sales Cloud and CPQ, Blanka Doktorová10 Differences between Sales Cloud and CPQ, Blanka Doktorová
10 Differences between Sales Cloud and CPQ, Blanka Doktorová
 
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
 
When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...
 
Speed Wins: From Kafka to APIs in Minutes
Speed Wins: From Kafka to APIs in MinutesSpeed Wins: From Kafka to APIs in Minutes
Speed Wins: From Kafka to APIs in Minutes
 
In-Depth Performance Testing Guide for IT Professionals
In-Depth Performance Testing Guide for IT ProfessionalsIn-Depth Performance Testing Guide for IT Professionals
In-Depth Performance Testing Guide for IT Professionals
 
ODC, Data Fabric and Architecture User Group
ODC, Data Fabric and Architecture User GroupODC, Data Fabric and Architecture User Group
ODC, Data Fabric and Architecture User Group
 
Introduction to Open Source RAG and RAG Evaluation
Introduction to Open Source RAG and RAG EvaluationIntroduction to Open Source RAG and RAG Evaluation
Introduction to Open Source RAG and RAG Evaluation
 

A little exercise with clojure macro

  • 1. A little exercise with Macro Zehua Liu
  • 2. Prerequisites • Basic macro (defmacro mywhen [cond-exp & clauses] `(if ~cond-exp (do ~@clauses))) • Java interop (String. "a string") (.substring "a string" 0 5)
  • 3. Motivation • A script to test a server with a request /response protocol • To send a request to the server, reuse Java classes that represent requests in server code
  • 8. Sending Requests • Many other types of requests • Lots of duplicate codes • Refactor them! • function?
  • 9. Refactor using function (defn send-request [req-pkt sid] (let [[new-sid resp-pkts] (send-req req-pkt sid)] (if-let [err-msg (check-resp-error resp-pkts)] (do (println (format "ERROR: %s failed, sid='%s',req='%s',err='%s'" (.getName req-pkt) sid req-pkt err-msg)) [nil nil]) [new-sid resp-pkts])))
  • 11. Refactor using function • Not too bad • Can we do better / differently? • macro?
  • 13. Make it work for Login Request
  • 14. Make it work for Login Request (defmacro send-request [request username password sid] `(let [req-pkt# (doto (requests.LoginRequest.) (.setUsername ~username) (.setPassword ~password)) [new-sid# resp-pkts#] (send-req req-pkt# ~sid)] (if-let [err-msg# (check-resp-error resp-pkts#)] (do (println (format "ERROR: %s failed, sid='%s',req='%s',err='%s'" ~(name request) ~sid req-pkt# err-msg#)) [nil nil]) [new-sid# resp-pkts#]))) (defn login [:Login username password sid] (send-request username password sid))
  • 15. Make it work for Login Request
  • 16. Make it work for Login Request (defmacro send-request [request username password sid] `(let [req-pkt# (doto (new ~(symbol (str "requests." (name request) "Request"))) (.setUsername ~username) (.setPassword ~password)) [new-sid# resp-pkts#] (send-req req-pkt# ~sid)] (if-let [err-msg# (check-resp-error resp-pkts#)] (do (println (format "ERROR: %s failed, sid='%s',req='%s',err='%s'" ~(name request) ~sid req-pkt# err-msg#)) [nil nil]) [new-sid# resp-pkts#]))) (defn login [username password sid] (send-request :Login username password sid))
  • 17. Make it work for Login Request (.setUsername ~username) <==> (. setUsername ~username) (.setUsername ~username) <==> (. (symbol (str "set" (name :Username))) ~username) (.setUsername ~username) (.setPassword ~password) <==> ~@(map (fn [[pn pv]] `(. ~(symbol (str "set" (name pn))) ~pv)) {:Username username :Password password})
  • 18. Make it work for Login Request (defmacro send-request [request param-map sid] `(let [req-pkt# (doto (new ~(symbol (str "requests." (name request) "Request"))) ~@(map (fn [[pn pv]] `(. ~(symbol (str "set" (name pn))) ~pv)) param-map)) [new-sid# resp-pkts#] (send-req req-pkt# ~sid)] (if-let [err-msg# (check-resp-error resp-pkts#)] (do (println (format "ERROR: %s failed, sid='%s',req='%s',err='%s'" ~(name request) ~sid req-pkt# err-msg#)) [nil nil]) [new-sid# resp-pkts#]))) (defn login [username password sid] (send-request :Login {:Username username :Password password} sid))
  • 19. Refactor using macro (defn login [username password sid] (send-request :Login {:Username username :Password password} sid)) (defn send-message [message dest-username type sid] (send-request :SendMessage {:Message message :DestUsername dest-username :Type type} sid))
  • 20. Refactor using macro • It works! Hooray! • Let’s use it for more fancy stuff. • Optional request fields? • On server side, SendMessage type default to 1, if not specified
  • 21. Optional field (defn send-message [message dest-username type sid] (send-request :SendMessage {:Message message :DestUsername dest-username :Type type} sid)) (defn send-message [message dest-username sid] (send-request :SendMessage {:Message message :DestUsername dest-username} sid))
  • 22. Optional field (defn send-message ([message dest-username sid] (send-message message dest-username nil sid)) ([message dest-username type sid] (let [param-map-base {:Message message :DestUsername dest-username} param-map (if type (assoc param-map-base :Type type) param-map-base)] (send-request :SendMessage param-map sid))))
  • 23. Optional field, FAILED (defn send-message ([message dest-username sid] (send-message message dest-username nil sid)) ([message dest-username type sid] (let [param-map-base {:Message message :DestUsername dest-username} param-map (if type (assoc param-map-base :Type type) param-map-base)] (send-request :SendMessage param-map sid)))) CompilerException java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol, compiling: (NO_SOURCE_PATH:10)
  • 25. Optional field, FAILED • macro is evaluated at compile time, not at run time • macro evaluator only knows about symbols – {:Username username :Password password} is a map (keywords to symbols) – But param-map is a symbol – At compile time, macro evaluator does not know the run time value that the symbol param-map represents • How do we fix it?
  • 26. Optional field, Fixing it • How do we fix it? • One thought: – Tell the macro the complete list of fields, and have it generate codes like below for every field: (if-let [v (:Type param-map)] (. setType v)) • And then param-map can be a symbol whose value macro evalutor does not need to know, its value is only needed at run time.
  • 27. Optional field, Fixing it (defmacro send-request [request param-list param-map sid] (let [param-map# param-map r (gensym)] `(let [req-pkt# (let [~r (new ~(symbol (str "requests." (name request) "Request")))] ~@(map (fn [pn] `(if-let [pv# (~pn ~param-map#)] (. ~r ~(symbol (str "set" (name pn))) pv#))) param-list)) [new-sid# resp-pkts#] (send-req req-pkt# ~sid)] (if-let [err-msg# (check-resp-error resp-pkts#)] (do (println (format "ERROR: %s failed, sid='%s',req='%s',err='%s'" ~(name request) ~sid req-pkt# err-msg#)) [nil nil]) [new-sid# resp-pkts#]))))
  • 28. Optional field, Fixing it (defn login [username password sid] (send-request :Login [:Username :Password] {:Username username :Password password} sid)) (defn send-message ([message dest-username sid] (send-message message dest-username nil sid)) ([message dest-username type sid] (let [param-map-base {:Message message :DestUsername dest-username} param-map (if type (assoc param-map-base :Type type) param-map-base)] (send-request :SendMessage [:Message :DestUsername :Type] param-map sid))))
  • 29. Optional field, Fixing it (macroexpand '(send-request :SendMessage [:Message :DestUsername :Type] param-map nil)) (let* [req-pkt__625__auto__ (clojure.core/let [G__671 (new requests.SendMessageRequest)] (clojure.core/if-let [pv__624__auto__ (:Message param-map)] (. G__671 setMessage pv__624__auto__)) (clojure.core/if-let [pv__624__auto__ (:DestUsername param-map)] (. G__671 setDestUsername pv__624__auto__)) (clojure.core/if-let [pv__624__auto__ (:Type param-map)] (. G__671 setType pv__624__auto__))) vec__672 (scratch.core/send-req req-pkt__625__auto__ nil) new-sid__626__auto__ (clojure.core/nth vec__672 0 nil) resp-pkts__627__auto__ (clojure.core/nth vec__672 1 nil)] (clojure.core/if-let [err-msg__628__auto__ (scratch.core/check-resp-error resp-pkts__627__auto__)] (do (clojure.core/println (clojure.core/format "ERROR: %s failed, sid='%s',req='%s',err='%s'" "SendMessage" nil req-pkt__625__auto__ err-msg__628__auto__)) [nil nil]) [new-sid__626__auto__ resp-pkts__627__auto__]))
  • 30. Lessons Learned • macro is evaluated at compile time, not at run time • macro evaluator treats code as data – {:Username username :Password password} is a map of keywords to symbols, not keywrods to strings (or whatever type username/password might be) – But param-map is a symbol • At compile time, macro evaluator treats ref/var as symbol, knowing nothing about their run time values