Does CurryHoward Support Type Aliases and Parametric Polymorphism?

Yes, CurryHoward fully supports both type aliases and parametric polymorphism through its Scala macro system, which automatically dealsias types and represents type parameters as TP nodes in its internal TypeExpr AST.

The CurryHoward library for Scala enables automatic implementation of types via the Curry-Howard correspondence. A common question among developers working with generic code is whether the library handles type aliases and parametric polymorphism. According to the source code in chymyst/curryhoward, the macro expansion system explicitly dealsias types and constructs a parametric AST using the TP case class, enabling seamless support for both features.

How CurryHoward Handles Type Aliases

The macro responsible for converting Scala types into the library's internal representation explicitly resolves type aliases before processing. In src/main/scala/io/chymyst/ch/Macros.scala, the implementation calls dealias on the given type:

// Lines 65-67 in Macros.scala
val finalType = givenType.dealias

This ensures that any type alias—whether simple or parameterized—is expanded to its underlying concrete type before the library constructs the TypeExpr AST. Consequently, using typeExpr[MyAlias] produces the same internal representation as using the aliased type directly.

Practical Example with Type Aliases

import io.chymyst.ch._

type MyEither[A, B] = Either[A, B]

// The macro automatically expands MyEither to Either
val ty = typeExpr[MyEither[Int, String]]
println(ty.prettyPrint)  // Output: <tc>Either[Int,String]

The dealias operation ensures that the theorem prover works with the canonical type representation, eliminating any distinction between aliases and their targets.

Parametric Polymorphism and the TP AST Node

CurryHoward represents type parameters using the TP case class defined in src/main/scala/io/chymyst/ch/TypeExpr.scala:

// Lines 89-91 in TypeExpr.scala
final case class TP(name: String) extends TypeExpr with AtomicTypeExpr

When the macro encounters a generic type, it creates TP nodes for each type parameter. These nodes participate in the full unification and substitution pipeline, enabling the library to reason about polymorphic types.

Unification and Substitution Logic

The library implements unification for parametric types through methods like leftUnifyRec (lines 332-356 in TypeExpr.scala). These methods handle TP nodes during type inference, allowing the theorem prover to:

  • Substitute concrete types for type variables via substTypeVar
  • Match generic constructors against concrete instances
  • Generate implementations for polymorphic signatures like def foo[F[_], A]: F[A]

Working with Generic Case Classes

case class Box[T](value: T)

// Generate an implementation for Box[Int] => Int
val impl = ofType[Box[Int] => Int]
println(impl)  
// Result: (b: Box[Int]) => b.value

Here, the macro identifies Box[Int] as a NamedConjunctT with type parameter TP("T") substituted by BasicT("Int"). The theorem prover solves the implication and emits the accessor code.

Higher-Kinded Types and Type Constructors

CurryHoward extends its polymorphic support to higher-kinded types (type constructors). When encountering a type like F[_], the macro treats the unknown constructor as atomic but preserves the TP structure for the type argument, enabling limited but effective reasoning about generic contexts.

def lift[F[_], A](fa: F[A]): F[Option[A]] = 
  implement[fa.type => F[Option[A]]]

While the library treats F as a ConstructorT without knowledge of its specific structure, the TP placeholder allows the unifier to treat the type parametrically, often synthesizing identity-like implementations that pass the generic structure through unchanged.

Summary

CurryHoward provides comprehensive support for both type aliases and parametric polymorphism through its macro-based architecture:

  • Type aliases are automatically resolved via dealias in Macros.scala before AST construction, ensuring seamless use of aliased types.
  • Parametric polymorphism is implemented through the TP case class in TypeExpr.scala, which integrates with unification and substitution algorithms to handle generic types.
  • Higher-kinded types receive partial support through parametric placeholders, enabling generic programming patterns with type constructors.

Frequently Asked Questions

Does CurryHoward expand recursive type aliases?

Yes. Because the macro calls dealias on the input type, recursive aliases are expanded to their underlying recursive structure. The library then processes the resulting type according to its standard rules for recursive types and case classes.

Can CurryHoward generate implementations for methods with multiple type parameters?

Yes. The TP representation handles each type parameter independently. When using implement or ofType with signatures like def process[A, B, C]: (A, B) => C, the macro creates separate TP nodes for A, B, and C, allowing the theorem prover to find valid implementations across multiple generic parameters.

How does CurryHoward handle variance annotations on type parameters?

The library reflects on the erased type structure after dealias, which preserves variance information in the type signature. However, the internal TypeExpr AST (including TP nodes) focuses on structural typing for theorem proving rather than variance-specific subtyping. The generated code respects Scala's variance rules because it emits standard Scala syntax that the compiler then checks for variance correctness.

Is there a performance penalty for using type aliases with CurryHoward?

No. The dealias operation occurs at compile time during macro expansion in Macros.scala. By the time the code is generated, all aliases have been replaced with their underlying types, resulting in zero runtime overhead compared to using the original types directly.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →