Skip to content

Latest commit

 

History

History
80 lines (61 loc) · 3.02 KB

E08.md

File metadata and controls

80 lines (61 loc) · 3.02 KB

Homework Assignment

Because of the delay, partial solutions are more welcome than usual. Please try to solve at least two-three exercises, and focus on the theory part.

  • Consider a macro named aMacro running on an expression with nested subexpressions:
  val intermediateExpr = if (cond) { list map f } else { list map g }

  aMacro(intermediateExpr map h)

We said that aMacro can't know statically the what intermediateExpr is going to contain. Sketch a scenario which exemplifies the problem.

  • Assume again that aMacro is a macro and consider the snippet below, divided in two fragments marked (1) and (2). Will aMacro receive different arguments in examples (1) and (2)?

    //(1)
    aMacro(1)
    //(2)
    aMacro({
      val v = 1
      v
    })

    If there is a difference between the arguments in those scenarios:

    • can you describe scenarios where the difference is or is not interesting, and motivate why?
    • For scenarios where the difference is not interesting, does the difference introduce potential for bugs?
  • Choose an example of boilerplate in the course lecture notes and try describing how macros could be used to automate the generation of such boilerplate.

  • Macros can also be used in the implementation of DSLs. For instance:

    • for integrating external DSLs
    • for implementing alternative semantics of existing code (language virtualization)
    • to perform static analyses, even on code which does not get executed.

    Give an example of a possible usage scenario among those categories, and/or point out additional categories with appropriate examples.

  • Inside trace_impl in the code from the lecture (14-macros/macros/01macros.scala), we have two different proposals for the body:

    def trace(x: Any): Unit = macro trace_impl
    def trace_impl(c: Context)(x: c.Expr[Any]): c.Expr[Unit] = {
      import c.universe._
      //Base version (1):
      //c.Expr(q"""println("The value of %s is %s" format (${show(x)}, $x))""")
      //
      //Question: what's wrong if we write instead (2):
      //c.Expr(q"""println(${"The value of %s is %s" format (show(x), x)})""")
    }

    Please analyze and describe the difference between the two pieces of code, in terms of the different execution times we have discussed during the lecture. Then, try out running both snippets, explain what happens and the difference.

Coding exercises:

  • As mentioned, a drawback of HOAS interfaces is that the names chosen by the user are lost. Consider the following interface for the untyped lambda calculus:

     trait Term
     case class HOASFun(funBody: Term => Term, variableName: String) extends Term
     case class HOASApp(fun: Term, arg: Term) extends Term

    Try writing a macro

    def fun(f: Term => Term): HOASFun = macro ...

    that, when invoked as fun(strangeName => body...), expands to HOASFun(strangeName => body..., "strangeName") by capturing the name ("strangeName") of the parameter (strangeName) of the argument (strangeName => body) for the macro invocation.