[RFC] Annotating methods that raise `NotImplementedError`

What do you think about improving compile-time information about methods that are not implemented on some platforms?

NotImplementedError

Some APIs in stdlib are not implemented on all targets. We try to minimize these instances in order to ensure portability. But sometimes there’s just no other way.

Still such platform-specific APIs are usually defined for all targets so they do compile. But when there’s no suitable fallback, the implementation is missing, which is signaled by NotImplementedError at runtime.
The reasoning for not making this a compile time error is to allow a program to compile even if some code path could end up calling a non-implemented method which might never happen in practice.
And this would often be quite cumbersome to work around.

Build time information

Yet with the current mechanism, we’ll only know whether a program can potentially reach a non-implemented method if it actually calls it.
Considering that in most cases, non-implemented methods are very clearly derived from the build target, this is quite poor.
A developer might want to know about potential pitfalls in case they end up calling into a potentially-unimplemented method unintentionally and maybe want to handle this differently.

But we currently only have runtime indicators for this problem.

I think it could be helpful to have a means for statically declaring a method as not-implemented, to have that information at build time.

Annotation

A very similar tool is the @[Deprecated] annotation. In fact, as a crude
solution could add @[Deprecated] to all methods that raise NotImplementedError with an appropriate message and get the desired effect.

Of course a dedicated annotation would be more appropriate and allow specific features for this context.

Portable Documentation

If the annotation is only defined on the target for which the method is not
implemented, only API docs built on that target will show it. Other targets won’t.
To fix this we would need to add the annotation for all targets, even when the method actually is implemented. But maybe the annotation could also define the conditions on which a method is not implemented? Then the compiler could show warnings only on affected targets.

The doc generator could list the conditions.

Example

@[NotImplemented(flags: %w[!win32])]
def WinError.value : self
  {% if flag?(:win32) %}
    WinError.new LibC.GetLastError
  {% else %}
    raise NotImplementedError.new("WinError.value")
  {% end %}
end

Extra

With some additional magic, it could even directly inject the runtime exception, making explicit raise NotImplementedError.new unnecessary and keeping everything succinct.
But that might be too much and certainly it’s above a MVP.

Related issue: Helper macro for `NotImplementedError` · Issue #13078 · crystal-lang/crystal · GitHub

7 Likes