newsfilter.io
Tutorial, Webinar

Exploring the Hidden Secrets within OCaml’s Local Function Types | OCaml Unboxed

Core Mechanics of Local Function Types

  • Functions annotated with local arguments exhibit hidden local annotations in their partial applications.
  • If a function takes a local argument, any intermediate closure created during partial application is also inferred as local.
  • This inference prevents "heap-to-stack" pointers by ensuring that a closure holding a local value cannot persist on the heap if the local value was intended for the stack.

Case Study: append_ints with Local Lists

  • Example Setup: A recursive function append_ints takes two int list arguments, where the first argument is explicitly typed as local.
  • Safety Constraint: The function succeeds only because int values are considered "global" and can be safely consed onto a global list, even if the source list was local.
  • Polymorphic Failure: Converting the function to polymorphic types causes a type error ("value escapes its region") because a generic local value cannot be safely stored in a global list structure.
  • Result Type: The return type of the original definition appears global, but the intermediate partial application is secretly local.

Partial Application and Region Escapes

  • Error Trigger: Attempting to partially apply append_ints to a list (e.g., map (append_ints [1; 2; 3])) results in a compilation error because the resulting closure attempts to capture a local list on the heap.
  • Implicit Hidden Local: Adding parentheses to the type signature (e.g., local list -> (list -> list)) does not alter the code but explicitly reveals that the intermediate function is a global closure containing a local argument, which is disallowed.
  • Standard Library Limitations: The standard List.map function lacks local annotations, forcing the compiler to treat partial applications involving local arguments as potentially unsafe.
  • Resolution via Full Application: Fully applying the function before passing it to map resolves the error, as the local value is consumed immediately and does not need to be stored in a closure.

Workarounds and Syntax Considerations

  • at Expansion: Explicitly writing at expansions in the type system can force the compiler to allocate the intermediate list on the heap, converting it to a global type that can be safely sub-modified to local.
  • Improved Error Messages: Jane Street updated the error message to explicitly warn that a "function or one of its parameters escape their region when partially applied," advising users to fully apply the function.
  • Proposed Syntax Changes: The speaker suggests potential new syntax for function arrows that prohibit partial application to avoid these confusing "hidden local" semantics.
  • Currying Anomalies: Standard functional programming assumptions that A -> B -> C is equivalent to A -> (B -> C) do not hold true for local types; the former allows full application without issues, while the latter introduces a hidden local closure.

Upcoming Development and Context

  • Series Continuation: This content is part of a broader series on the local mode in Jane Street's OCaml compiler branch.
  • Future Directions: There is ongoing consideration of syntax modifications to function definitions to prevent accidental creation of hidden locals during partial application.
  • Compiler Behavior: The compiler infers the hidden local status on partial applications automatically, even when the user does not explicitly type it.