Skip to content

Introducing QuasiQuoters

We're excited to announce QuasiQuoters (QQ), a powerful new feature in Nic that enables type-safe, compile-time validated domain-specific languages.

The Problem

Embedding domain-specific content in code often requires string manipulation that's error-prone and lacks type safety:

nic
// Traditional approach - no compile-time validation
let query = "SELECT * FROM users WHERE id = " + id_string;
let pattern = "[a-zA-Z]+";  // Hope you got the regex right!

These strings are just text. Typos, syntax errors, or type mismatches are only caught at runtime - if at all.

The Solution: QuasiQuoters

QuasiQuoters provide compile-time parsing and validation of domain-specific literals:

nic
// With QuasiQuoters - type-safe and validated at compile time
let query = sql`SELECT * FROM users WHERE id = ${user_id}`;
let pattern = regex`[a-zA-Z]+`;

The compiler parses these literals at compile time, validates their syntax, and provides full type safety.

How It Works

A QuasiQuoter is defined as a function that transforms raw string content into a typed value at compile time:

nic
// The type: a function from string to a specific type
type QuasiQuoter[T] = fn(string) -> T;

// Usage: name`content` calls the QuasiQuoter and returns type T
let result: T = name`content`;

Built-in QuasiQuoters

Nic provides several built-in QQs:

nic
// Regular expressions
let email_pattern = regex`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`;

// SQL queries (with interpolation)
let user_query = sql`SELECT name, email FROM users WHERE id = ${user_id}`;

// JSON (returns typed data)
let config = json`{"debug": true, "port": 8080}`;

Interpolation

QuasiQuoters support variable interpolation with ${expr}:

nic
let name = String.from("Alice");
let age = 30;

let greeting = fmt`Hello, ${name}! You are ${age} years old.`;

The interpolated expressions are type-checked and safely converted to the target format.

Creating Custom QuasiQuoters

You can define your own QuasiQuoters for domain-specific needs:

nic
// A simple URI QuasiQuoter
fn uri_qq(raw: string) -> Uri {
    // Parse and validate URI at compile time
    return Uri.parse(raw);
}

// Register as a QuasiQuoter
qq uri = uri_qq;

// Usage
let api_endpoint = uri`https://api.example.com/v1/users`;

Parser Generators

One powerful application is parser generators:

nic
// Define a grammar
let expr_parser = parser`
    expr   := term (('+' | '-') term)*
    term   := factor (('*' | '/') factor)*
    factor := number | '(' expr ')'
    number := [0-9]+
`;

// Use the generated parser
let ast = expr_parser.parse("3 + 4 * 5");

The grammar is validated at compile time, and a type-safe parser is generated.

Why QuasiQuoters?

1. Compile-Time Validation

Errors are caught during compilation, not at runtime:

nic
// Compilation error: invalid regex syntax
let bad = regex`[a-z`;  // Error: unclosed character class

2. Type Safety

The result type is known at compile time:

nic
let query = sql`SELECT name FROM users`;
// query has type SqlQuery, not just string

3. Better Tooling

IDEs can provide syntax highlighting, completion, and error checking within QQ literals.

4. Performance

Parsing happens at compile time. The runtime cost is minimal - often just validating pre-computed values.

The Journey Here

QuasiQuoters replace our experimental "temporal zippers" feature. While temporal zippers were an interesting exploration of time-based state management, QuasiQuoters provide more practical, widely-applicable value.

The design draws inspiration from:

  • Haskell's Template Haskell QuasiQuotes
  • Scala's string interpolation
  • Rust's procedural macros

Getting Started

Check out the QuasiQuoters Guide in Learn Nic for complete documentation and examples.

QuasiQuoters are available in Nic v0.3.0 and later.

What's Next?

We're exploring:

  • More built-in QQs: HTML, CSS, GraphQL, TOML
  • QQ macros: User-defined compile-time transformations
  • IDE integration: Syntax highlighting within QQ literals in VS Code

Stay tuned for more updates, and let us know what domain-specific languages you'd like to see supported!


Have questions or feedback? Open an issue on GitHub or join the discussion.

Released under the MIT License.