From d0c25ec19385bedbd6d42b3dd963e50a96f2a2ea Mon Sep 17 00:00:00 2001 From: Gabriel Scherer Date: Fri, 31 May 2024 17:55:08 +0200 Subject: [PATCH 1/2] revised RFC based on discussions at the type-system meeting --- rfcs/val_annotations.md | 138 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 rfcs/val_annotations.md diff --git a/rfcs/val_annotations.md b/rfcs/val_annotations.md new file mode 100644 index 0000000..a67c0f5 --- /dev/null +++ b/rfcs/val_annotations.md @@ -0,0 +1,138 @@ +# `val` declarations in structures + +## Context + +Advanced OCaml features often require writing type +annotations. However, writing type annotations on definitions today +can be awkward, as it forces to write the rest of the definition in +a different style than we would normally use: adding an annotation on +a function definition + +```ocaml +let f arg1 arg2 arg3 = + +``` + +typically requires adding the annotation after `f`, in the form `: + =` and then adding a `fun`, and then turning the trailing equal +sign into a right-arrow (and making an awkward decision on +indentation): + +```ocaml +let f : = + fun arg1 arg2 arg3 -> + (* extra indentation *) + +(* or possibly *) +let f : += fun arg1 arg2 arg3 -> + +``` + +(Note: we are not talking about per-argument annotations here. They can +be simpler in some cases, but they cannot be used in others, for +example when introducing polymorphic recursion due to GADTs. Generally +their readability is worse than full-declaration annotations in +complex cases.) + + +## Proposal + +We propose to allow using `val` declarations immediately before definitions. + +```ocaml +val map : ('a -> 'b) -> 'a list -> 'b list +let rec map f li = ... +``` + +### Recursive bindings + +In a nest of mutually-recursive bindings, each binding may or may not +have a declaration, but the declarations must all come together before +the `let rec` block. + +```ocaml +val even : int -> bool +val odd : int -> bool +let rec even n = ... +and odd n = ... +``` + +(It is not allowed to mix declarations and definitions, because there +is no obvious syntax for this, although `and val` could be +considered. See "Alternative syntax: declarations with `let` blocks" +below for a form that allows this.) + +### Local bindings + +The `let in ` form of +[#14040](https://github.com/ocaml/ocaml/pull/14040) is extended to +cover local declarations: + +```ocaml +let rev li = + let val loop : 'a list -> 'a list -> 'a list in + let rec loop li acc = ... in + loop li [] +``` + +## Discussion and extensions + +### Locally abstract types in `val` declarations + +We propose to extend the form `type a . ...` to work with value +annotations. For example: + +```ocaml +val mem : type a . a -> a list -> bool +let mem elt li = + let rec loop : a list -> bool = function + | [] -> false + | x :: xs -> (x = elt) || loop xs + in loop li +``` + +Notice that `type a` binds the variable `a` in both the declaration and the definition. + +(This extension could be left out of a first implementation of this proposal.) + + +### Alternative syntax: declarations within `let` blocks + +An alternative syntax would be possible where `val` is part of the `let` block, as follows: + +```ocaml +let rec + val map : ('a -> 'b) -> 'a list -> 'b list + and map f li = ... +``` + +This syntax is weirder (it feels closer to SML), but it scales +slightly better to mutually-recursive or local definitions: + +```ocaml +let rec val f : +and val g : +and f +and g +and val h : +and h +and i +``` + +```ocaml +let rev li = + let rec + val loop : 'a list -> 'a list -> 'a list + and loop li acc = ... + in loop li [] +``` + +### Combination with `_` inference from signature + +oxcaml has a work-in-progress feature where `_` can be used to elide types and module signatures in structures (in particular `.ml` files), when they are declared in the corresponding signature (in particular the `.mli` file), see https://github.com/oxcaml/oxcaml/pull/2783 . This feature is independent, but it was part of the motivation to revive the current proposal, as it can naturally be combined: + +```ocaml +val map : _ +let rec map f li = ... +``` From 3db55629c563394e3013e23acd046ce8c9d67b6c Mon Sep 17 00:00:00 2001 From: Gabriel Scherer Date: Thu, 27 Nov 2025 14:49:48 +0100 Subject: [PATCH 2/2] update RFCs following discussions --- rfcs/val_annotations.md | 55 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/rfcs/val_annotations.md b/rfcs/val_annotations.md index a67c0f5..aa5b69d 100644 --- a/rfcs/val_annotations.md +++ b/rfcs/val_annotations.md @@ -78,9 +78,15 @@ let rev li = ## Discussion and extensions +The suggestions below are *not* intended for inclusion as part of the +RFC (they would not be implemented along with what's above), but they +serve to record and discuss alternative choices, and/or to check +compatibility with other features by showing that the feature can be +extended if desired. + ### Locally abstract types in `val` declarations -We propose to extend the form `type a . ...` to work with value +One can consider extending the form `type a . ...` to work with value annotations. For example: ```ocaml @@ -94,7 +100,7 @@ let mem elt li = Notice that `type a` binds the variable `a` in both the declaration and the definition. -(This extension could be left out of a first implementation of this proposal.) +This extension may not be necessary if [#12732](https://github.com/ocaml/ocaml/pull/12732) is merged. In general there are discussions around the semantics of `type a` vs `'a` that are independent of the present PR. We merely point out that `val` could scale to `type a.` if desired. ### Alternative syntax: declarations within `let` blocks @@ -128,6 +134,7 @@ let rev li = in loop li [] ``` + ### Combination with `_` inference from signature oxcaml has a work-in-progress feature where `_` can be used to elide types and module signatures in structures (in particular `.ml` files), when they are declared in the corresponding signature (in particular the `.mli` file), see https://github.com/oxcaml/oxcaml/pull/2783 . This feature is independent, but it was part of the motivation to revive the current proposal, as it can naturally be combined: @@ -136,3 +143,47 @@ oxcaml has a work-in-progress feature where `_` can be used to elide types and m val map : _ let rec map f li = ... ``` + +A more compact form specific to toplevel `let` definitions could be +considered: + +``` +val rec map f li = ... +``` + +it would both imply `val map : _` as above and could only be included +once per structure (shadowing `val` declarations with another `val` is +forbidden.) + + +### Tension with using `val` for forward-declaration + +Jeremy Yallop points out that `val` could be considered to introduce recursion, so that + +``` +let rec fac n = function +| 0 -> 1 +| n -> n * fac (n - 1) +``` + +could also be written (note the absence of `rec` below) + +``` +val fac : int -> int +let fac n = function +| 0 -> 1 +| n -> n * fac (n - 1) +``` + +That interpretation of `val` is *incompatible* with the one proposed in this PR, as their semantics would differ in the following case: + +``` +let fac n = 0 (* first definition *) + +val fac : int -> int +let fac n = function +| 0 -> 1 +| n -> n * fac (n - 1) (* first definition or recursive occurrence? *) +``` + +Leo White proposes to use `val rec` to implicitly introduce recursion, to avoid the incompatibility between the two features. (Gabriel Scherer and Kate Deplaix alternatively proposed to require `let rec` to take `val` forward declarations into account, but Jeremy Yallop was not impressed by this proposal.)