deriving-sml is a tool for type-generated code generation in Standard ML,
similar to ppx_deriving for OCaml, and deriving traits in Rust.
It currently supports four plugins: show, eq, compare, and map.
deriving-sml is intended to be used for SML projects using the Standard ML of
New Jersey compiler.
deriving-sml is implemented in Standard ML, as all things should be.
deriving-sml is triggered in a similar way to ppx_deriving for OCaml.
After a (data)type declaration or specification, you can write [.deriving <plugins>] to generate code for the attached type.
signature FOO =
sig
type t [.deriving show]
datatype t2 =
One
| Two [.deriving show, compare]
end
structure Foo : FOO =
struct
type t = int * string [.deriving show, eq]
datatype t2 =
One
| Two [.deriving show, compare]
endFor a given plugin, such as show, when derived for a type t, it will
generate functions t_show and show_t within the same scope.
Similarly to how one might run sml <filename_1> ... <filename_n> to load a
certain number of dependencies in to the SML/NJ REPL, you run deriving-sml by
using the enclosed ./run command as ./run <filename_1> ... <filename_n>.
This will cause all involved files (even through CM dependencies) to be
rewritten with all derived code, and restored upon termination of the REPL. This
means that deriving-sml works "invisibly", and ./run is meant to function
identically to sml.
Suppose we had the following code:
datatype 'a t = A | B of 'a [.deriving show, eq, compare, map]Then this will generate code like:
val show_t : ('a -> string) -> 'a t -> string
val eq_t : ('a * 'a -> bool) -> 'a t * 'a t -> bool
val compare_t : ('a * 'a -> order) -> 'a t * 'a t -> order
val map_t : ('a -> 'b) -> 'a t -> 'b tThere are a fair amount of tests included in test/test.sml. You can run them
by running ./run test/sources.cm and then Test.run_tests ().
deriving-sml is meant to be able to be used to enable deriving code anywhere
within a given SML project. As such, it is fully CM compatible - you can run
./run sources.cm and cause it to rewrite all of the dependencies of
sources.cm (even if it is other .cm files).
deriving-sml also permits derive expressions, which are triggered via expressions of the
form [.<plugin>: <type>]. These will generate code for the corresponding
types, similar to above.
So a snippet of SML code may look like:
val _ = print ("current val is " ^ [.show: ast] ast ^ "\n")Notably, these types may contain type variables in them. This will generate a function expecting curried arguments for those types. The order of the curried arguments is in sorted order of the involved type variables.
So, for instance, [.map: ('b * 'a option) list list] : ('a -> 'a2) -> ('b -> 'b2) -> ('b * 'a option) list list -> ('b2 * 'a2 option) list list.