It's important for this library to be consistent across modules in things like naming conventions, binding optional parameters, handling options objects, using named arguments, etc.
We also use a couple of techniques globally that ought to be used everywhere they apply (in particular, Stream sub-typing, use of BinaryLike.t('a), etc).
With consistency and quality in mind, I am posting this set of guidelines to follow when we run into situations where multiple possible solutions exist. That way we can settle on exactly one of those solutions and stick to it.
Principles
In general, we do not wrap external bindings with our own functions. This can make it tricky to write ergonomic bindings sometimes, but the benefits are worth it.
We want to model the types perfectly, but sometimes that is not practical. In those cases, it is better to bind to a simplified (yet still valid) version of the type signature. Additional bindings can be written later if we need to.
Naming & Techniques
IF an API function specifies any number of optional parameters
THEN the binding should be split into two functions. The first one will have the original name from the API, and will not include any of the optional parameters. The second function will be named <original name>With (e.g. execWith), and will specify all of the optional params as 'optional named parameters'.
EXCEPT in certain edge cases, like when a function's output type depends on one or more of its optional arguments. In that case, we do whatever is sensible.
IF an API function specifies a options object parameter
THEN we create an abstract type for that object, named <function name>Options, and then write an external function to construct that object using the [@bs.obj] attribute. This allows us to specify the object type in the function signature and give users a convenient way to construct a partial version of that object using optional named arguments in the object constructor, which usually matches the API's intent.
EXCEPT when the options object is also a return value in part of the API, in which case the type cannot be abstract. In that case, the only difference should be to make the type concrete as a record type. It's also a good idea to use [@bs.as] on the property names, to guarantee they won't ever get mangled by the compiler. The function signature should go unchanged.
More will be added later. This is all open to discussion and critique, so feel free to offer any opinions or concerns in the comments. If we reach an agreement on something, I will edit this post to reflect any changes.
It's important for this library to be consistent across modules in things like naming conventions, binding optional parameters, handling
options
objects, using named arguments, etc.We also use a couple of techniques globally that ought to be used everywhere they apply (in particular, Stream sub-typing, use of
BinaryLike.t('a)
, etc).With consistency and quality in mind, I am posting this set of guidelines to follow when we run into situations where multiple possible solutions exist. That way we can settle on exactly one of those solutions and stick to it.
Principles
external
bindings with our own functions. This can make it tricky to write ergonomic bindings sometimes, but the benefits are worth it.Naming & Techniques
IF an API function specifies any number of optional parameters
<original name>With
(e.g.execWith
), and will specify all of the optional params as 'optional named parameters'.IF an API function specifies a
options
object parameter<function name>Options
, and then write an external function to construct that object using the[@bs.obj]
attribute. This allows us to specify the object type in the function signature and give users a convenient way to construct a partial version of that object using optional named arguments in the object constructor, which usually matches the API's intent.options
object is also a return value in part of the API, in which case the type cannot be abstract. In that case, the only difference should be to make the type concrete as a record type. It's also a good idea to use[@bs.as]
on the property names, to guarantee they won't ever get mangled by the compiler. The function signature should go unchanged.More will be added later. This is all open to discussion and critique, so feel free to offer any opinions or concerns in the comments. If we reach an agreement on something, I will edit this post to reflect any changes.