# How to Inspect or Access Lambda‑Terms from Automatically Generated Code in curryhoward

> Learn to inspect or access lambda terms from Curryhoward generated code. Use the `.lambdaTerm` extension or `TermExpr.lambdaTerm` for symbolic representation.

- Repository: [Chymyst/curryhoward](https://github.com/chymyst/curryhoward)
- Tags: how-to-guide
- Published: 2026-02-27

---

**TLDR:** Use the implicit `.lambdaTerm` extension method on any value produced by `ofType`, `implement`, or `allOfType`, or call `TermExpr.lambdaTerm(value)` to receive an `Option[TermExpr]` containing the symbolic representation.

The **curryhoward** library for Scala synthesizes executable code from type signatures using compile‑time macros. While the output is a standard Scala function, the macro simultaneously constructs a **lambda‑term**—a syntactic tree representing the logical proof or algorithm. You can extract these terms from the generated code to inspect the underlying structure, perform symbolic substitution, or verify equivalence.

## How curryhoward Attaches Lambda‑Terms to Generated Functions

When you invoke a macro such as `ofType[T]` or `implement[T]`, the expansion logic in [`src/main/scala/io/chymyst/ch/Macros.scala`](https://github.com/chymyst/curryhoward/blob/main/src/main/scala/io/chymyst/ch/Macros.scala) builds both the executable function and its symbolic `TermExpr`. The method `Macros.returnTerm` wraps the compiled code alongside the term using arity‑specific classes.

For functions with up to three arguments, the macro instantiates one of the wrapper classes defined in [`src/main/scala/io/chymyst/ch/TermExpr.scala`](https://github.com/chymyst/curryhoward/blob/main/src/main/scala/io/chymyst/ch/TermExpr.scala):

- `Function0Lambda[Res]`
- `Function1Lambda[Ar, Res]`
- `Function2Lambda[Arg1, Arg2, Res]`
- `Function3Lambda[Arg1, Arg2, Arg3, Res]`

Each wrapper constructor accepts two parameters: the compiled Scala function and the `TermExpr` representing the lambda‑term. The term is stored in the public `lambdaTerm` field. For arities greater than three, the macro returns the plain function without a wrapper, and the term is discarded to reduce allocation overhead.

## Retrieving Lambda‑Terms from Macro‑Generated Values

The library provides two idiomatic ways to extract the attached term. Both rely on pattern‑matching against the wrapper classes described above.

### Using the Implicit `.lambdaTerm` Extension

The file [`src/main/scala/io/chymyst/ch/package.scala`](https://github.com/chymyst/curryhoward/blob/main/src/main/scala/io/chymyst/ch/package.scala) defines an implicit class `WithLambdaTerm`. When you import the `io.chymyst.ch._` package, any value generated by a curryhoward macro gains the `.lambdaTerm` method. This method pattern‑matches the underlying wrapper and returns the `TermExpr` directly.

```scala
import io.chymyst.ch._

// Generates a function and its symbolic term
val f: Int => Int = ofType[Int => Int]

// Access the lambda-term
val term: TermExpr = f.lambdaTerm
println(term.prettyPrint)  // Output: a ⇒ a

```

This approach works seamlessly for nullary, unary, binary, and ternary functions. If the value was not produced by a curryhoward macro, the implicit conversion will not apply, resulting in a compile‑time error.

### Using TermExpr.lambdaTerm for Optional Extraction

For scenarios where you need a defensive check—such as when receiving a value from Java code or when you are uncertain of the function’s origin—use the static helper in `TermExpr`. The method `TermExpr.lambdaTerm` accepts any `AnyRef` and returns an `Option[TermExpr]` implemented in [`src/main/scala/io/chymyst/ch/TermExpr.scala`](https://github.com/chymyst/curryhoward/blob/main/src/main/scala/io/chymyst/ch/TermExpr.scala).

```scala
import io.chymyst.ch._

val g = implement[Int => Int]
val maybeTerm: Option[TermExpr] = TermExpr.lambdaTerm(g)

maybeTerm.foreach(t => println(t.prettyRenamePrint))  // a ⇒ a

```

If the input is an ordinary Scala function or a curryhoward function with arity greater than three, the method returns `None`.

## Working with Multi‑Argument Functions

The wrapper mechanism supports functions of up to three arguments. When you generate such functions using `allOfType` or `implement`, you can inspect their curried structure.

```scala
val f2 = allOfType[(Int, Int) => Int].head  // Arity 2
val term2 = f2.lambdaTerm
println(term2.prettyRenamePrint)  // e.g., a ⇒ b ⇒ a._1

```

However, for higher arities, the library intentionally omits the wrapper. Attempting to extract a term from a ten‑argument function yields `None`:

```scala
val f10 = implement[(Int, String, String, String, String, String, String, String, String, String) => Int]
println(TermExpr.lambdaTerm(f10))  // None

```

## Using Lambda‑Terms for Symbolic Reasoning

Once extracted, the `TermExpr` supports algebraic manipulation. You can substitute type variables, apply the term to arguments, and check equivalence between expressions.

```scala
def mapReader[X, A, B] = ofType[(X => A) => (A => B) => (X => B)]
val mapReaderTerm = mapReader.lambdaTerm

val id = ofType[A => A].lambdaTerm
val readerVar = freshVar[X => A]
val substituted = TermExpr.substTypeVar(TP("B"), TP("A"), mapReaderTerm)

assert(substituted(readerVar)(id).equiv(readerVar))

```

This capability is demonstrated in the test suite `LambdaTermsSpec`, which verifies that the generated terms behave correctly under substitution and application.

## Summary

- **Lambda‑terms** are automatically constructed by `curryhoward` macros (`ofType`, `implement`, `allOfType`, `anyOfType`) and attached to the generated function via wrapper classes.
- Access the term using the **implicit `.lambdaTerm`** extension provided in [`src/main/scala/io/chymyst/ch/package.scala`](https://github.com/chymyst/curryhoward/blob/main/src/main/scala/io/chymyst/ch/package.scala) for a direct return, or use **TermExpr.lambdaTerm** for an `Option[TermExpr]`.
- Wrappers exist only for arities **zero through three**; higher‑arity functions do not carry extractable terms.
- Extracted terms support **pretty‑printing**, renaming, substitution, and equivalence checking via the `TermExpr` API.

## Frequently Asked Questions

### Can I extract a lambda‑term from any Scala function?

No. Only functions produced by the `curryhoward` macros contain the hidden wrapper class that stores the term. Standard Scala functions or manually written code lack this metadata, so `TermExpr.lambdaTerm` returns `None` and the implicit `.lambdaTerm` method will not compile.

### Why does `TermExpr.lambdaTerm` return `None` for my generated function?

The macro only attaches lambda‑terms to functions with **three or fewer arguments**. If you use `implement` or `ofType` with a function type that has four or more parameters, the library returns the bare compiled function without a wrapper, making the term inaccessible at runtime.

### What can I do with a `TermExpr` once I have it?

You can inspect its structure using `prettyPrint` or `prettyRenamePrint`, perform symbolic substitution with `substTypeVar`, apply it to other terms using function application syntax, and verify logical equivalence with the `equiv` method. This is useful for testing that the synthesized code matches a specific proof structure.

### Is the `.lambdaTerm` extension method safe to use in production code?

Yes, provided the value is guaranteed to originate from a `curryhoward` macro. The pattern match in `WithLambdaTerm` is exhaustive for the wrapper classes. However, if you pass a non‑wrapped value, you will receive a compile‑time error stating that `lambdaTerm` is not a member of the type. For defensive programming, prefer the `TermExpr.lambdaTerm` utility method.