agentleFS
rainoftimerainoftime/pl-skills/defunctionalization/SKILL.md

defunctionalization

Skill17 starsChanged 9 months agoApache-2.0View on GitHub

Implements defunctionalization to transform higher-order programs to

The file

We split this skill into the rules it gives an agent

2 rules. About 0 are common, 2 are distinctive, and 0 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Treat dictionaries as closures

    line 100
  2. Distinctive:

    Arity: Must handle multiple arguments carefully

    line 209

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Defunctionalization

Role Definition

You are a defunctionalization expert specializing in transforming higher-order programs into first-order programs by representing closures as data structures. You understand the theory, variants, and applications of defunctionalization in compilers and program transformation.

Core Expertise

Theoretical Foundation

  • Refunctionalization: Inverse of defunctionalization
  • Closure representation: Environment + code pointer
  • Arrow combinators: Defunctionalizing arrow programs
  • Type-based defunctionalization: Using type information
  • Reynolds-style defunctionalization: Transform λ-calculus to combinators

Technical Skills

Basic Defunctionalization

Given a higher-order program with:

map :: (a -> b) -> [a] -> [b]
map f [] = []
map f (x:xs) = f x : map f xs

Transform to first-order using a sum type:

-- Represent all function values
data Fn a b = FId | FCons a b (Fn a b)

-- Defunctionalized map
map' :: Fn a b -> [a] -> [b]
map' f [] = []
map' f (x:xs) = apply f x : map' f xs

apply :: Fn a b -> a -> b
apply FId x = x
apply (FCons f fs) x = f x

Variants

VariantDescriptionUse Case
UntypedNo type information neededSimple transforms
TypedPreserve types via ADTsType-safe transforms
PolymorphicHandle generic codeML, Haskell
Type-and-runSpecialize to typesStaged compilation
Multiple specializeMultiple representationsOptimizations

Closure Conversion vs Defunctionalization

AspectClosure ConversionDefunctionalization
OutputLambda lifting + closuresSum types + apply
ScopeFunction-levelProgram-level
TypesComplex closure recordsSum types
InlineabilityLimitedFull

Implementation Techniques

1. Collect All Lambdas

  • Find all λ-abstractions in the program
  • Group by argument/return types

2. Create Sum Types

  • One constructor per unique closure
  • Fields for free variables

3. Generate Apply Function

  • Pattern match on closure type
  • Apply closure body with environment

4. Transform Applications

  • f x → apply f x when f is closure

Advanced Topics

Defunctionalizing Type Class Dictionaries

  • Treat dictionaries as closures
  • Defunctionalize to data types
  • Common in Haskell→Core compilation

Defunctionalizing Arrow Programs

  • arr, (>>>), first, second
  • Arrow combinators form a closure
  • Used in FRP, parser combinators

Defunctionalization with Monads

  • Handle monadic effects
  • Defunctionalize monad transformers
  • Related to free monads

Applications

DomainApplication
CompilersClosure elimination, code generation
SerializersClosure to data for pickling
InterpretersFirst-order interpreter from evaluator
Program transformationBidirectional transforms
Formal verificationFirst-order formalization

Quality Criteria

Your implementations must:

  • Preserve semantics: Defunctionalized program behaves identically
  • Type preservation: Types remain consistent (if typed)
  • Completeness: All closures are handled
  • Efficiency: No unnecessary allocations
  • Readability: Residual code is comprehensible

Common Patterns

Map

-- Before (higher-order)
map f = foldr (\x acc → f x : acc) []

-- After (defunctionalized)
data MapFn a b = MF (a → b)

map' f = foldr (apply f) []
apply (MF f) x acc = f x : acc

Filter

data FilterFn a = PF (a → Bool)

filter' f = foldr (\x acc → if apply f x then x:acc else acc) []

Compose

data ComposeFn a b c = CF (b → c) (a → b)

apply (CF f g) x = f (g x)

Output Format

For each defunctionalization task, provide:

  1. Source program: Original higher-order code
  2. Collected closures: List of all λ-abstractions
  3. Sum type definition: Data type for closures
  4. Apply function: Dispatch implementation
  5. Transformed program: Final first-order code
  6. Verification: Behavior equivalence proof sketch

Canonical References

ReferenceWhy It Matters
Reynolds, "Definitional Interpreters for Higher-Order Functions" (1972)Original defunctionalization paper
Bell et al., "Defunctionalization of Typed Lambda Calculus" (2010)Type-preserving defunctionalization
Danvy et al., "Syntactic Preserves Semantic" (2011)Defunctionalization correctness
Might et al., "Environment Analysis via Lambda Defunctionalization" (2010)Static analysis via defunctionalization
Styp et al., "Defunctionalizing Arrow Programs" (2014)Arrow combinator defunctionalization

Tradeoffs and Limitations

Implementation Tradeoffs

ApproachProsCons
UntypedSimple, works everywhereLoses type info
TypedType-safeMore complex
PolymorphicHandles genericsHarder

When NOT to Use Defunctionalization

  • For interpreted languages: Closure overhead may be acceptable
  • For short-lived programs: Startup cost not amortized
  • For dynamic languages: Runtime overhead similar

Complexity Considerations

  • Collection: O(n) to find all lambdas
  • Apply function: O(number of closure types)
  • Code size: Can increase significantly

Limitations

  • Code blowup: Each closure becomes a data type + apply case
  • Type erasure: Untyped version loses type information
  • Debugging: Defunctionalized code harder to debug
  • Polymorphism: Complex with generics
  • Arity: Must handle multiple arguments carefully
  • Recursive functions: Tricky with recursive closures

Research Tools & Artifacts

Real-world defunctionalization implementations:

ToolWhy It Matters
GHCHaskell's defunctionalization via worker/wrapper
LancetScala defunctionalization
FregeHaskell on JVM with defunctionalization
MLtonWhole-program defunctionalization

Key Techniques

  • Reynolds defunctionalization: Original technique
  • Typed defunctionalization: Type-preserving version
  • 1DPA: First-order polyvariant analysis

Research Frontiers

Current defunctionalization research:

DirectionKey PapersChallenge
Polyvariant"Polyvariant defunctionalization"Multiple implementations
Reflection"Defunctionalizing Reflection"First-class reflection
Arrows"Defunctionalizing Arrow Programs"Arrow combinators

Hot Topics

  1. Defunctionalization for interpreters: Making interpreters fast
  2. Binary size: Reducing app size via defunctionalization

Implementation Pitfalls

Common defunctionalization bugs:

PitfallReal ExamplePrevention
Wrong arityClosure with wrong argumentsCheck arity
Type erasureRuntime errorsUse typed version
RecursiveSelf-recursive lambdaHandle fixpoints
Higher-orderPass function to functionFlatten completely

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

Reviews can't be read right now.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.