Skip to content

Latest commit

 

History

History
105 lines (81 loc) · 2.62 KB

macroexp.md

File metadata and controls

105 lines (81 loc) · 2.62 KB

Macro expressions

Teal supports a restricted form of macro expansion via the macroexp construct, which declares a macro expression. This was added to the language as the support mechanism for implementing the where clauses in records and interfaces, which power the type resolution performed by the is operator.

Macro expressions are always expanded inline in the generated Lua code. The declaration itself produces no Lua code.

A macro expression is declared similarly to a function, only using macroexp instead of function:

local macroexp add(a: number, b: number)
   return a + b
end

There are two important restrictions:

  • the body of the macro expression can only contain a single return statement with a single expression;
  • each argument can only be used once in the macroexp body.

The latter restriction allows for macroexp calls to be expanded inline in any expression context, without the risk for producing double evaluation of side-effecting expressions. This avoids the pitfalls commonly produced by C macros in a simple way.

Because macroexps do not generate code on declaration, you can also declare a macroexp inline in a record definition:

local record R
   x: number

   get_x: function(self): number = macroexp(self: R): number
      return self.x
   end
end

local r: R = { x = 10 }
print(r:get_x())

This generates the following code:

local r: R = { x = 10 }
print(r.x)

You can also use them for metamethods: this will cause the metamethod to be expanded at compile-time, without requiring a metatable:

local record R
   x: number

   metamethod __lt: function(a: R, b: R) = macroexp(a: R, b: R)
      return a.x < b.x
   end
end

local r: R = { x = 10 }
local s: R = { x = 20 }
if r > s then
   print("yes")
end

This generates the following code:

local r = { x = 10 }
local s = { x = 20 }
if s.x < r.x then
   print("yes")
end

This is used to implement the pseudo-metamethod __is, which is used to resolve the is operator. The where construct is syntax sugar to an __is declaration, meaning the following two constructs are equivalent:

local record MyRecord is MyInterface
   where self.my_field == "my_record"
end

-- ...is the same as:

local record MyRecord is MyInterface
   metamethod __is: function(self: MyRecord): boolean = macroexp(self: MyRecord): boolean
      return self.my_field == "my_record"
   end
end

At this time, macroexp declarations within records do not allow inference, so the function type needs to be explicitly declared when implementinga a field or metamethod as a macroexp. This requirement may be dropped in the future.