💾 Archived View for r.bdr.sh › gemlog › 1696437389086 › api_notation_updates.gmi captured on 2024-05-26 at 14:43:55. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2024-03-21)
-=-=-=-=-=-=-
A few years ago I created an API notation to use with software specification documents: Back then I was working in a team that relied heavily on software specifications, and we were maintaining projects in objective-c, ruby, and javascript, so the notation emerged out of the need to communicate the public APIs in a way that was generic enough to make implementation in any language simple, while concise enough to avoid integration issues.
For example, I could use it to describe the library I use to build this blog[1]
// Library to generate an ephemeral html blog with a gemini archive Blog -max_posts <Int> -posts_directory <String> -archive_directory <String> -static_directory <String> -templates_directory <String> -remote_config <String> #add(post_location <String>) => Promise<Void> #update(post_location <String>) => Promise<Void> #publish(host <String>) => Promise<Void> #publish_archive(host <String>) => Promise<Void> #add_remote(remote <String>) => Promise<Void> #remove_remote() => Promise<Void> #sync_down() => Promise<Void> #sync_up() => Promise<Void> #generate() => Promise<Void>
[1] A Javascript implementation of that API
I had been using it unchanged for almost ten years, but recently decided to drop a specific symbol for callbacks, and instead add a "Throws" symbol #>. You can see the definition here, or in its home page[2]
// Anything after two forward slashes is a comment NameOfClass.WithPossibleNamespace + class property - instance property ~> listened events (socket) +> listened events (class/module) -> listened events (instance) <~ dispatched events (socket) <+ dispatched events(class/module) <- dispatched events (instance) :: class method # instance method Other symbols => returns #> throws [xx] optional <data type> Recommended order: class first, then sockets, then instance. Internally: Properties, events, methods.
One of the patterns that I started using for functions is to instead define the whole function signature as part of the type definition. So for example, if you have a method that receives a function as an argument, you could write the following:
GenericManipulator #manipulate<T>(input T, manipulator<T>(input T, options <ManipulationOptions>) => T #> ManipulationError) => T #> ManipulationError
I've found this pattern covers most cases where I need to pass a function.
In slightly related news, since I've recently moved fully to using `neovim`, I've also created a tree-sitter parser[3] that you can use as a neovim plugin. It was really fun to learn, but the documentations was clear and easy to follow. A bit less easy to follow was how to get the syntax highlighting to actually work with neovim, but it ended up working.
[3] tree-sitter parser and neovim plugin.
If you use other editors, there's older versions of the plugin available for vim[4], vscode[5], and TextMate / Sublime Text[6]. They don't support the #> throws notation.