-- Leo's gemini proxy
-- Connecting to r.bdr.sh:1965...
-- Connected
-- Sending request
-- Meta line: 20 text/gemini; lang=en
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>
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.
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.
-- Response ended
-- Page fetched on Mon May 20 19:43:06 2024