crystal-macros

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Crystal Macros

Crystal 宏

You are Claude Code, an expert in Crystal's macro system and compile-time metaprogramming. You specialize in building powerful abstractions, DSLs, and code generation systems using Crystal's compile-time execution capabilities.
Your core responsibilities:
  • Write macros for code generation and boilerplate reduction
  • Build domain-specific languages (DSLs) using macro methods
  • Implement compile-time computations and validations
  • Generate methods, classes, and modules dynamically
  • Manipulate abstract syntax trees (AST) at compile time
  • Create type-safe abstractions through macro expansion
  • Build debugging and introspection tools
  • Implement compile-time configuration and feature flags
  • Generate serialization and deserialization code
  • Design annotation-based programming patterns
你是Claude Code,一位Crystal宏系统和编译时元编程专家。你擅长利用Crystal的编译时执行能力构建强大的抽象、DSL和代码生成系统。
你的核心职责:
  • 编写用于代码生成和减少样板代码的宏
  • 使用宏方法构建领域特定语言(DSL)
  • 实现编译时计算与验证
  • 动态生成方法、类和模块
  • 在编译时操作抽象语法树(AST)
  • 通过宏展开创建类型安全的抽象
  • 构建调试与自省工具
  • 实现编译时配置和功能标志
  • 生成序列化与反序列化代码
  • 设计基于注解的编程模式

Macro Basics

宏基础

Macros run at compile time and receive AST nodes as arguments. They can generate and return code that gets inserted into the program.
宏在编译时运行,接收AST节点作为参数。它们可以生成并返回要插入到程序中的代码。

Simple Macro Definition

简单宏定义

crystal
undefined
crystal
undefined

Basic macro that generates a method

Basic macro that generates a method

macro define_getter(name) def {{name}} @{{name}} end end
class Person def initialize(@name : String, @age : Int32) end
define_getter name define_getter age end
person = Person.new("Alice", 30) puts person.name # Generated method puts person.age # Generated method
undefined
macro define_getter(name) def {{name}} @{{name}} end end
class Person def initialize(@name : String, @age : Int32) end
define_getter name define_getter age end
person = Person.new("Alice", 30) puts person.name # Generated method puts person.age # Generated method
undefined

Macro with Multiple Arguments

多参数宏

crystal
macro define_property(name, type)
  @{{name}} : {{type}}?

  def {{name}} : {{type}}?
    @{{name}}
  end

  def {{name}}=(value : {{type}})
    @{{name}} = value
  end
end

class Config
  define_property host, String
  define_property port, Int32
  define_property ssl, Bool

  def initialize
  end
end

config = Config.new
config.host = "localhost"
config.port = 8080
puts config.host
crystal
macro define_property(name, type)
  @{{name}} : {{type}}?

  def {{name}} : {{type}}?
    @{{name}}
  end

  def {{name}}=(value : {{type}})
    @{{name}} = value
  end
end

class Config
  define_property host, String
  define_property port, Int32
  define_property ssl, Bool

  def initialize
  end
end

config = Config.new
config.host = "localhost"
config.port = 8080
puts config.host

Macro with Block

带块的宏

crystal
macro measure_time(name, &block)
  start_time = Time.monotonic
  {{yield}}
  elapsed = Time.monotonic - start_time
  puts "{{name}} took #{elapsed.total_milliseconds}ms"
end

measure_time("database query") do
  sleep 0.5
  # Database operation here
end
crystal
macro measure_time(name, &block)
  start_time = Time.monotonic
  {{yield}}
  elapsed = Time.monotonic - start_time
  puts "{{name}} took #{elapsed.total_milliseconds}ms"
end

measure_time("database query") do
  sleep 0.5
  # Database operation here
end

String Interpolation in Macros

宏中的字符串插值

Macros use
{{}}
for interpolation and can generate identifiers, literals, and code.
宏使用
{{}}
进行插值,可以生成标识符、字面量和代码。

Generating Method Names

生成方法名

crystal
macro define_flag_methods(name)
  def {{name}}?
    @{{name}}
  end

  def {{name}}!
    @{{name}} = true
  end

  def clear_{{name}}
    @{{name}} = false
  end
end

class FeatureFlags
  def initialize
    @feature_a = false
    @feature_b = false
  end

  define_flag_methods feature_a
  define_flag_methods feature_b
end

flags = FeatureFlags.new
flags.feature_a!
puts flags.feature_a?  # true
flags.clear_feature_a
puts flags.feature_a?  # false
crystal
macro define_flag_methods(name)
  def {{name}}?
    @{{name}}
  end

  def {{name}}!
    @{{name}} = true
  end

  def clear_{{name}}
    @{{name}} = false
  end
end

class FeatureFlags
  def initialize
    @feature_a = false
    @feature_b = false
  end

  define_flag_methods feature_a
  define_flag_methods feature_b
end

flags = FeatureFlags.new
flags.feature_a!
puts flags.feature_a?  # true
flags.clear_feature_a
puts flags.feature_a?  # false

Generating with String Manipulation

通过字符串操作生成代码

crystal
macro define_enum_helpers(enum_type)
  {% for member in enum_type.resolve.constants %}
    def {{member.downcase.id}}?
      self == {{enum_type}}::{{member}}
    end
  {% end %}
end

enum Status
  Pending
  Running
  Completed
  Failed
end

class Job
  def initialize(@status : Status)
  end

  def status
    @status
  end

  # Generate pending?, running?, completed?, failed?
  define_enum_helpers Status
end

job = Job.new(Status::Pending)
puts job.pending?    # true
puts job.running?    # false
crystal
macro define_enum_helpers(enum_type)
  {% for member in enum_type.resolve.constants %}
    def {{member.downcase.id}}?
      self == {{enum_type}}::{{member}}
    end
  {% end %}
end

enum Status
  Pending
  Running
  Completed
  Failed
end

class Job
  def initialize(@status : Status)
  end

  def status
    @status
  end

  # Generate pending?, running?, completed?, failed?
  define_enum_helpers Status
end

job = Job.new(Status::Pending)
puts job.pending?    # true
puts job.running?    # false

Compile-Time Iteration

编译时迭代

Macros can iterate over collections at compile time using
{% for %}
.
宏可以使用
{% for %}
在编译时遍历集合。

Iterating Over Arrays

遍历数组

crystal
macro define_constants(*names)
  {% for name, index in names %}
    {{name.upcase.id}} = {{index}}
  {% end %}
end

class ErrorCodes
  define_constants success, not_found, unauthorized, server_error
end

puts ErrorCodes::SUCCESS        # 0
puts ErrorCodes::NOT_FOUND      # 1
puts ErrorCodes::UNAUTHORIZED   # 2
puts ErrorCodes::SERVER_ERROR   # 3
crystal
macro define_constants(*names)
  {% for name, index in names %}
    {{name.upcase.id}} = {{index}}
  {% end %}
end

class ErrorCodes
  define_constants success, not_found, unauthorized, server_error
end

puts ErrorCodes::SUCCESS        # 0
puts ErrorCodes::NOT_FOUND      # 1
puts ErrorCodes::UNAUTHORIZED   # 2
puts ErrorCodes::SERVER_ERROR   # 3

Iterating Over Hash Literals

遍历哈希字面量

crystal
macro define_validators(**rules)
  {% for name, validator in rules %}
    def validate_{{name.id}}(value)
      {{validator}}
    end
  {% end %}
end

class Validator
  define_validators(
    email: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i,
    phone: /\A\d{3}-\d{3}-\d{4}\z/,
    zip_code: /\A\d{5}(-\d{4})?\z/
  )
end

validator = Validator.new
puts validator.validate_email("test@example.com")
puts validator.validate_phone("555-123-4567")
crystal
macro define_validators(**rules)
  {% for name, validator in rules %}
    def validate_{{name.id}}(value)
      {{validator}}
    end
  {% end %}
end

class Validator
  define_validators(
    email: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i,
    phone: /\A\d{3}-\d{3}-\d{4}\z/,
    zip_code: /\A\d{5}(-\d{4})?\z/
  )
end

validator = Validator.new
puts validator.validate_email("test@example.com")
puts validator.validate_phone("555-123-4567")

Iterating Over Type Methods

遍历类型方法

crystal
macro log_all_methods(type)
  {% for method in type.resolve.methods %}
    puts "Method: {{method.name}}"
  {% end %}
end

class Calculator
  def add(a, b)
    a + b
  end

  def subtract(a, b)
    a - b
  end
end
crystal
macro log_all_methods(type)
  {% for method in type.resolve.methods %}
    puts "Method: {{method.name}}"
  {% end %}
end

class Calculator
  def add(a, b)
    a + b
  end

  def subtract(a, b)
    a - b
  end
end

At compile time, this generates puts statements

At compile time, this generates puts statements

macro list_calculator_methods log_all_methods Calculator end
undefined
macro list_calculator_methods log_all_methods Calculator end
undefined

Conditional Compilation

条件编译

Use
{% if %}
for compile-time conditionals based on flags, types, or expressions.
使用
{% if %}
基于标志、类型或表达式实现编译时条件判断。

Platform-Specific Code

平台特定代码

crystal
macro platform_specific_path
  {% if flag?(:windows) %}
    "C:\\Program Files\\MyApp"
  {% elsif flag?(:darwin) %}
    "/Applications/MyApp.app"
  {% elsif flag?(:linux) %}
    "/usr/local/bin/myapp"
  {% else %}
    "/tmp/myapp"
  {% end %}
end

DEFAULT_PATH = {{platform_specific_path}}
puts DEFAULT_PATH
crystal
macro platform_specific_path
  {% if flag?(:windows) %}
    "C:\\Program Files\\MyApp"
  {% elsif flag?(:darwin) %}
    "/Applications/MyApp.app"
  {% elsif flag?(:linux) %}
    "/usr/local/bin/myapp"
  {% else %}
    "/tmp/myapp"
  {% end %}
end

DEFAULT_PATH = {{platform_specific_path}}
puts DEFAULT_PATH

Feature Flags

功能标志

crystal
macro with_feature(flag, &block)
  {% if flag?(flag) %}
    {{yield}}
  {% end %}
end

class Application
  with_feature(:debug) do
    def debug_info
      puts "Debug mode enabled"
    end
  end

  with_feature(:metrics) do
    def record_metric(name, value)
      puts "Recording #{name}: #{value}"
    end
  end
end
crystal
macro with_feature(flag, &block)
  {% if flag?(flag) %}
    {{yield}}
  {% end %}
end

class Application
  with_feature(:debug) do
    def debug_info
      puts "Debug mode enabled"
    end
  end

  with_feature(:metrics) do
    def record_metric(name, value)
      puts "Recording #{name}: #{value}"
    end
  end
end

Compile with: crystal build app.cr -Ddebug -Dmetrics

Compile with: crystal build app.cr -Ddebug -Dmetrics

undefined
undefined

Type-Based Conditionals

基于类型的条件判断

crystal
macro generate_serializer(type)
  {% if type.resolve < Number %}
    def serialize_{{type.name.downcase.id}}(value : {{type}}) : String
      value.to_s
    end
  {% elsif type.resolve == String %}
    def serialize_{{type.name.downcase.id}}(value : {{type}}) : String
      value.inspect
    end
  {% elsif type.resolve < Array %}
    def serialize_{{type.name.downcase.id}}(value : {{type}}) : String
      "[" + value.map(&.to_s).join(", ") + "]"
    end
  {% end %}
end

class Serializer
  generate_serializer Int32
  generate_serializer String
  generate_serializer Array(Int32)
end

s = Serializer.new
puts s.serialize_int32(42)
puts s.serialize_string("hello")
puts s.serialize_array_int32([1, 2, 3])
crystal
macro generate_serializer(type)
  {% if type.resolve < Number %}
    def serialize_{{type.name.downcase.id}}(value : {{type}}) : String
      value.to_s
    end
  {% elsif type.resolve == String %}
    def serialize_{{type.name.downcase.id}}(value : {{type}}) : String
      value.inspect
    end
  {% elsif type.resolve < Array %}
    def serialize_{{type.name.downcase.id}}(value : {{type}}) : String
      "[" + value.map(&.to_s).join(", ") + "]"
    end
  {% end %}
end

class Serializer
  generate_serializer Int32
  generate_serializer String
  generate_serializer Array(Int32)
end

s = Serializer.new
puts s.serialize_int32(42)
puts s.serialize_string("hello")
puts s.serialize_array_int32([1, 2, 3])

AST Node Types

AST节点类型

Macros receive different types of AST nodes. Understanding these is crucial.
宏接收不同类型的AST节点,理解这些节点至关重要。

Inspecting AST Nodes

检查AST节点

crystal
macro show_ast(expression)
  {{expression.class_name}}
end
crystal
macro show_ast(expression)
  {{expression.class_name}}
end

NumberLiteral

NumberLiteral

puts {{show_ast(42)}}
puts {{show_ast(42)}}

StringLiteral

StringLiteral

puts {{show_ast("hello")}}
puts {{show_ast("hello")}}

Call

Call

puts {{show_ast(foo.bar)}}
puts {{show_ast(foo.bar)}}

ArrayLiteral

ArrayLiteral

puts {{show_ast([1, 2, 3])}}
undefined
puts {{show_ast([1, 2, 3])}}
undefined

Working with Identifiers

处理标识符

crystal
macro create_accessor(name)
  # name is a SymbolLiteral or StringLiteral
  # Convert to identifier with .id
  def {{name.id}}
    @{{name.id}}
  end

  def {{name.id}}=(value)
    @{{name.id}} = value
  end
end

class User
  def initialize
    @username = ""
  end

  create_accessor :username
end
crystal
macro create_accessor(name)
  # name is a SymbolLiteral or StringLiteral
  # Convert to identifier with .id
  def {{name.id}}
    @{{name.id}}
  end

  def {{name.id}}=(value)
    @{{name.id}} = value
  end
end

class User
  def initialize
    @username = ""
  end

  create_accessor :username
end

Manipulating String Literals

操作字符串字面量

crystal
macro define_constants_from_string(str)
  {% parts = str.split(",") %}
  {% for part in parts %}
    {{part.strip.upcase.id}} = {{part.strip.id.stringify}}
  {% end %}
end

module Colors
  define_constants_from_string("red, green, blue, yellow")
end

puts Colors::RED     # "red"
puts Colors::GREEN   # "green"
puts Colors::BLUE    # "blue"
puts Colors::YELLOW  # "yellow"
crystal
macro define_constants_from_string(str)
  {% parts = str.split(",") %}
  {% for part in parts %}
    {{part.strip.upcase.id}} = {{part.strip.id.stringify}}
  {% end %}
end

module Colors
  define_constants_from_string("red, green, blue, yellow")
end

puts Colors::RED     # "red"
puts Colors::GREEN   # "green"
puts Colors::BLUE    # "blue"
puts Colors::YELLOW  # "yellow"

Advanced Macro Patterns

高级宏模式

Building a DSL for Routes

构建路由DSL

crystal
macro route(method, path, handler)
  {% ROUTES ||= [] of {String, String, String} %}
  {% ROUTES << {method.stringify, path, handler.stringify} %}
end

macro compile_routes
  ROUTES_MAP = {
    {% for route in ROUTES %}
      {{route[1]}} => {{route[2].id}},
    {% end %}
  }

  def handle_request(method : String, path : String)
    handler_name = ROUTES_MAP[path]?
    return not_found unless handler_name

    case handler_name
    {% for route in ROUTES %}
    when {{route[2]}}
      {{route[2].id}}
    {% end %}
    end
  end
end

class WebApp
  route :get, "/", :index
  route :get, "/about", :about
  route :post, "/users", :create_user

  def index
    "Home Page"
  end

  def about
    "About Page"
  end

  def create_user
    "Create User"
  end

  def not_found
    "404 Not Found"
  end

  compile_routes
end
crystal
macro route(method, path, handler)
  {% ROUTES ||= [] of {String, String, String} %}
  {% ROUTES << {method.stringify, path, handler.stringify} %}
end

macro compile_routes
  ROUTES_MAP = {
    {% for route in ROUTES %}
      {{route[1]}} => {{route[2].id}},
    {% end %}
  }

  def handle_request(method : String, path : String)
    handler_name = ROUTES_MAP[path]?
    return not_found unless handler_name

    case handler_name
    {% for route in ROUTES %}
    when {{route[2]}}
      {{route[2].id}}
    {% end %}
    end
  end
end

class WebApp
  route :get, "/", :index
  route :get, "/about", :about
  route :post, "/users", :create_user

  def index
    "Home Page"
  end

  def about
    "About Page"
  end

  def create_user
    "Create User"
  end

  def not_found
    "404 Not Found"
  end

  compile_routes
end

Automatic JSON Serialization

自动JSON序列化

crystal
macro json_serializable(*fields)
  def to_json(builder : JSON::Builder)
    builder.object do
      {% for field in fields %}
        builder.field {{field.stringify}} do
          @{{field.id}}.to_json(builder)
        end
      {% end %}
    end
  end

  def self.from_json(parser : JSON::PullParser)
    instance = allocate
    {% for field in fields %}
      {{field.id}} = nil
    {% end %}

    parser.read_object do |key|
      case key
      {% for field in fields %}
      when {{field.stringify}}
        {{field.id}} = typeof(instance.@{{field.id}}).from_json(parser)
      {% end %}
      end
    end

    {% for field in fields %}
      instance.@{{field.id}} = {{field.id}}.not_nil!
    {% end %}

    instance
  end
end

class User
  def initialize(@name : String, @age : Int32, @email : String)
  end

  json_serializable name, age, email
end

user = User.new("Alice", 30, "alice@example.com")
json = user.to_json
puts json
crystal
macro json_serializable(*fields)
  def to_json(builder : JSON::Builder)
    builder.object do
      {% for field in fields %}
        builder.field {{field.stringify}} do
          @{{field.id}}.to_json(builder)
        end
      {% end %}
    end
  end

  def self.from_json(parser : JSON::PullParser)
    instance = allocate
    {% for field in fields %}
      {{field.id}} = nil
    {% end %}

    parser.read_object do |key|
      case key
      {% for field in fields %}
      when {{field.stringify}}
        {{field.id}} = typeof(instance.@{{field.id}}).from_json(parser)
      {% end %}
      end
    end

    {% for field in fields %}
      instance.@{{field.id}} = {{field.id}}.not_nil!
    {% end %}

    instance
  end
end

class User
  def initialize(@name : String, @age : Int32, @email : String)
  end

  json_serializable name, age, email
end

user = User.new("Alice", 30, "alice@example.com")
json = user.to_json
puts json

Compile-Time Configuration

编译时配置

crystal
macro configure(&block)
  {% begin %}
    {% config = {} of String => ASTNode %}
    {{yield}}
    {% for key, value in config %}
      {{key.upcase.id}} = {{value}}
    {% end %}
  {% end %}
end

macro set(key, value)
  {% config[key.stringify] = value %}
end

configure do
  set :app_name, "MyApp"
  set :version, "1.0.0"
  set :max_connections, 100
  set :debug, true
end

puts APP_NAME           # "MyApp"
puts VERSION            # "1.0.0"
puts MAX_CONNECTIONS    # 100
puts DEBUG              # true
crystal
macro configure(&block)
  {% begin %}
    {% config = {} of String => ASTNode %}
    {{yield}}
    {% for key, value in config %}
      {{key.upcase.id}} = {{value}}
    {% end %}
  {% end %}
end

macro set(key, value)
  {% config[key.stringify] = value %}
end

configure do
  set :app_name, "MyApp"
  set :version, "1.0.0"
  set :max_connections, 100
  set :debug, true
end

puts APP_NAME           # "MyApp"
puts VERSION            # "1.0.0"
puts MAX_CONNECTIONS    # 100
puts DEBUG              # true

Macro Methods

宏方法

Macro methods are called on types and can access compile-time type information.
宏方法在类型上调用,可以访问编译时类型信息。

Generating Methods from Type Info

从类型信息生成方法

crystal
class Model
  macro inherited
    # Called when a class inherits from Model
    def self.table_name : String
      {{@type.name.underscore.id.stringify}}
    end

    def self.column_names : Array(String)
      [
        {% for ivar in @type.instance_vars %}
          {{ivar.name.stringify}},
        {% end %}
      ]
    end
  end
end

class User < Model
  def initialize(@name : String, @email : String, @age : Int32)
  end
end

puts User.table_name       # "user"
puts User.column_names     # ["name", "email", "age"]
crystal
class Model
  macro inherited
    # Called when a class inherits from Model
    def self.table_name : String
      {{@type.name.underscore.id.stringify}}
    end

    def self.column_names : Array(String)
      [
        {% for ivar in @type.instance_vars %}
          {{ivar.name.stringify}},
        {% end %}
      ]
    end
  end
end

class User < Model
  def initialize(@name : String, @email : String, @age : Int32)
  end
end

puts User.table_name       # "user"
puts User.column_names     # ["name", "email", "age"]

Property Introspection

属性自省

crystal
class Base
  macro generate_initializer
    def initialize(
      {% for ivar in @type.instance_vars %}
        @{{ivar.name}} : {{ivar.type}},
      {% end %}
    )
    end

    def to_s(io : IO)
      io << "{{@type.name}}("
      {% for ivar, index in @type.instance_vars %}
        {% if index > 0 %}
          io << ", "
        {% end %}
        io << "{{ivar.name}}="
        @{{ivar.name}}.inspect(io)
      {% end %}
      io << ")"
    end
  end
end

class Person < Base
  @name : String
  @age : Int32
  @city : String

  generate_initializer
end

person = Person.new("Bob", 25, "NYC")
puts person  # Person(name="Bob", age=25, city="NYC")
crystal
class Base
  macro generate_initializer
    def initialize(
      {% for ivar in @type.instance_vars %}
        @{{ivar.name}} : {{ivar.type}},
      {% end %}
    )
    end

    def to_s(io : IO)
      io << "{{@type.name}}("
      {% for ivar, index in @type.instance_vars %}
        {% if index > 0 %}
          io << ", "
        {% end %}
        io << "{{ivar.name}}="
        @{{ivar.name}}.inspect(io)
      {% end %}
      io << ")"
    end
  end
end

class Person < Base
  @name : String
  @age : Int32
  @city : String

  generate_initializer
end

person = Person.new("Bob", 25, "NYC")
puts person  # Person(name="Bob", age=25, city="NYC")

Method Delegation

方法委托

crystal
macro delegate(*methods, to target)
  {% for method in methods %}
    def {{method.id}}(*args, **kwargs)
      @{{target.id}}.{{method.id}}(*args, **kwargs)
    end

    def {{method.id}}(*args, **kwargs, &block)
      @{{target.id}}.{{method.id}}(*args, **kwargs) { |*yield_args| yield *yield_args }
    end
  {% end %}
end

class UserRepository
  def find(id : Int32)
    "User #{id}"
  end

  def all
    ["User 1", "User 2"]
  end

  def create(name : String)
    "Created #{name}"
  end
end

class UserService
  def initialize
    @repository = UserRepository.new
  end

  delegate find, all, create, to: repository
end

service = UserService.new
puts service.find(1)
puts service.all
crystal
macro delegate(*methods, to target)
  {% for method in methods %}
    def {{method.id}}(*args, **kwargs)
      @{{target.id}}.{{method.id}}(*args, **kwargs)
    end

    def {{method.id}}(*args, **kwargs, &block)
      @{{target.id}}.{{method.id}}(*args, **kwargs) { |*yield_args| yield *yield_args }
    end
  {% end %}
end

class UserRepository
  def find(id : Int32)
    "User #{id}"
  end

  def all
    ["User 1", "User 2"]
  end

  def create(name : String)
    "Created #{name}"
  end
end

class UserService
  def initialize
    @repository = UserRepository.new
  end

  delegate find, all, create, to: repository
end

service = UserService.new
puts service.find(1)
puts service.all

Debugging Macros

调试宏

Compile-Time Printing

编译时打印

crystal
macro debug_print(value)
  {{puts value}}
  {{value}}
end
crystal
macro debug_print(value)
  {{puts value}}
  {{value}}
end

This will print at compile time

This will print at compile time

result = {{debug_print(42 + 8)}}
result = {{debug_print(42 + 8)}}

Print type information at compile time

Print type information at compile time

macro show_type_info(type) {% puts "Type: #{type.resolve}" %} {% puts "Instance vars: #{type.resolve.instance_vars.map(&.name)}" %} {% puts "Methods: #{type.resolve.methods.map(&.name)}" %} end
class Example @x : Int32 = 0 @y : String = ""
def foo end
def bar end end
{{show_type_info(Example)}}
undefined
macro show_type_info(type) {% puts "Type: #{type.resolve}" %} {% puts "Instance vars: #{type.resolve.instance_vars.map(&.name)}" %} {% puts "Methods: #{type.resolve.methods.map(&.name)}" %} end
class Example @x : Int32 = 0 @y : String = ""
def foo end
def bar end end
{{show_type_info(Example)}}
undefined

Macro Expansion Inspection

宏展开检查

crystal
undefined
crystal
undefined

Use --no-codegen flag to see macro expansion

Use --no-codegen flag to see macro expansion

crystal build --no-codegen app.cr

crystal build --no-codegen app.cr

macro verbose_property(name, type) {{puts "Generating property #{name} of type #{type}"}}
@{{name}} : {{type}}?
def {{name}} : {{type}}? {{puts "Generating getter for #{name}"}} @{{name}} end
def {{name}}=(value : {{type}}) {{puts "Generating setter for #{name}"}} @{{name}} = value end end
class Config verbose_property timeout, Int32 verbose_property host, String end
undefined
macro verbose_property(name, type) {{puts "Generating property #{name} of type #{type}"}}
@{{name}} : {{type}}?
def {{name}} : {{type}}? {{puts "Generating getter for #{name}"}} @{{name}} end
def {{name}}=(value : {{type}}) {{puts "Generating setter for #{name}"}} @{{name}} = value end end
class Config verbose_property timeout, Int32 verbose_property host, String end
undefined

When to Use This Skill

何时使用本技能

Use the crystal-macros skill when you need to:
  • Reduce boilerplate code through code generation
  • Build domain-specific languages (DSLs) for configuration or business logic
  • Generate repetitive methods, classes, or modules
  • Implement compile-time validation and type checking
  • Create property definitions with custom behavior
  • Generate serialization/deserialization code
  • Build annotation-based programming patterns
  • Implement automatic delegation or proxying
  • Create compile-time configuration systems
  • Generate database models from schema definitions
  • Build testing frameworks with custom assertions
  • Implement compile-time dependency injection
  • Create type-safe builder patterns
  • Generate API clients from specifications
  • Implement aspect-oriented programming patterns
当你需要以下功能时,可使用crystal-macros技能:
  • 通过代码生成减少样板代码
  • 为配置或业务逻辑构建领域特定语言(DSL)
  • 生成重复的方法、类或模块
  • 实现编译时验证和类型检查
  • 创建带有自定义行为的属性定义
  • 生成序列化/反序列化代码
  • 设计基于注解的编程模式
  • 实现自动委托或代理
  • 创建编译时配置系统
  • 从模式定义生成数据库模型
  • 构建带有自定义断言的测试框架
  • 实现编译时依赖注入
  • 创建类型安全的构建器模式
  • 从规范生成API客户端
  • 实现面向切面的编程模式

Best Practices

最佳实践

  1. Keep Macros Simple: Break complex macros into smaller, composable pieces
  2. Document Macro Behavior: Explain what code the macro generates and why
  3. Use Meaningful Names: Macro names should clearly indicate what they generate
  4. Validate Inputs: Check macro arguments at compile time when possible
  5. Prefer Macro Methods: Use macro methods over top-level macros for type-specific logic
  6. Use
    {{yield}}
    : Pass blocks to macros for flexible code generation
  7. Debug with
    {{puts}}
    : Print AST nodes and values during macro development
  8. Test Generated Code: Verify that macro-generated code works as expected
  9. Avoid Overuse: Only use macros when the benefit outweighs the complexity
  10. Use Type Information: Leverage
    @type
    and reflection for powerful abstractions
  11. Handle Edge Cases: Consider nil values, empty collections, and type variations
  12. Maintain Readability: Generated code should be as readable as hand-written code
  13. Version Carefully: Macro changes can break downstream code; version appropriately
  14. Use Conditional Compilation: Leverage flags for platform-specific or feature-specific code
  15. Document Expansion: Show example of expanded code in macro documentation
  1. 保持宏简洁:将复杂宏拆分为更小的可组合片段
  2. 记录宏行为:说明宏生成的代码及其用途
  3. 使用有意义的名称:宏名称应清晰表明其生成的内容
  4. 验证输入:尽可能在编译时检查宏参数
  5. 优先使用宏方法:针对特定类型的逻辑,使用宏方法而非顶级宏
  6. 使用
    {{yield}}
    :将块传递给宏以实现灵活的代码生成
  7. 使用
    {{puts}}
    调试
    :在宏开发过程中打印AST节点和值
  8. 测试生成的代码:验证宏生成的代码是否按预期工作
  9. 避免过度使用:仅当收益大于复杂度时才使用宏
  10. 利用类型信息:借助
    @type
    和反射功能构建强大的抽象
  11. 处理边缘情况:考虑空值、空集合和类型变体
  12. 保持可读性:生成的代码应与手写代码一样易读
  13. 谨慎版本化:宏变更可能破坏下游代码,需适当版本化
  14. 使用条件编译:利用标志实现平台特定或功能特定的代码
  15. 记录展开结果:在宏文档中展示展开后的代码示例

Common Pitfalls

常见陷阱

  1. Forgetting
    .id
    Conversion
    : Literals must be converted to identifiers with
    .id
  2. String vs Symbol Confusion: Know when to use stringify vs literal interpolation
  3. Infinite Macro Recursion: Recursive macros must have proper termination conditions
  4. Scope Issues: Variables in macro scope vs generated code scope can conflict
  5. Type Resolution Timing: Some type information isn't available during early compilation
  6. Missing Nil Checks: Generated code may not handle nil properly
  7. Hardcoded Assumptions: Macros assuming specific type structures that may change
  8. Poor Error Messages: Compilation errors in generated code are hard to debug
  9. Overusing Global State: Class variables in macros can cause unexpected behavior
  10. Not Handling Empty Collections: Iterating over empty arrays/hashes without checks
  11. Syntax Errors in Templates: Invalid Crystal syntax in macro bodies causes confusing errors
  12. Type Mismatch: Generated code doesn't match expected types
  13. Namespace Pollution: Generating too many methods or constants in global scope
  14. Platform Dependencies: Not handling platform differences in macro logic
  15. Circular Dependencies: Macros that depend on types that depend on the same macros
  1. 忘记
    .id
    转换
    :字面量必须通过
    .id
    转换为标识符
  2. 字符串与符号混淆:了解何时使用stringify与字面量插值
  3. 无限宏递归:递归宏必须有适当的终止条件
  4. 作用域问题:宏作用域与生成代码的作用域可能冲突
  5. 类型解析时机:某些类型信息在早期编译阶段不可用
  6. 缺少空值检查:生成的代码可能未正确处理nil
  7. 硬编码假设:宏假设的特定类型结构可能发生变化
  8. 错误信息不佳:生成代码中的编译错误难以调试
  9. 过度使用全局状态:宏中的类变量可能导致意外行为
  10. 未处理空集合:遍历空数组/哈希时未进行检查
  11. 模板中的语法错误:宏体中的无效Crystal语法会导致混淆性错误
  12. 类型不匹配:生成的代码与预期类型不匹配
  13. 命名空间污染:在全局作用域中生成过多方法或常量
  14. 平台依赖:宏逻辑未处理平台差异
  15. 循环依赖:宏依赖的类型又依赖于同一宏

Resources

资源