I understand why let () = 1 + 2 leads to an error message when () is redefined. My question is why utop would be rewriting 1 + 2 as let () = 1 + 2, since it seems that the latter should lead to an error message even when () isn’t redefined.
The full rewriting rule for utop is that is first evaluated as
let _ () = let module _ = struct <expr> end in ()
in order to check the expression without evaluating it.
Slightly off-topic, but another way I find redefining [] and (::) useful locally is when combining them with GADTs and existential types to implement kinds of DSLs.
A recent example was for heterogenous pretty printing of parameters:
let module M = struct
type t =
| []
| (::) : ((formatter -> 'a -> unit) * 'a) * t -> t
let rec pp_ls fmt = function
| [] -> ()
| (pp, v) :: [] -> pp fmt v
| (pp,v) :: tl -> pp fmt v; pp_print_string fmt ","; pp_print_space fmt (); pp_ls fmt tl
end in
and then instantiating them:
(pp_rule "Read" M.[pp_print_string, var; pp_print_int, offset])
(where var is of type string, and offset of type int).
1 Like