Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document builtin constants #14085

Closed
straight-shoota opened this issue Dec 13, 2023 · 5 comments · Fixed by #14276
Closed

Document builtin constants #14085

straight-shoota opened this issue Dec 13, 2023 · 5 comments · Fixed by #14276

Comments

@straight-shoota
Copy link
Member

We're missing documentation on the builtin constants created by the compiler. I think all of them are in the Crystal namespace: https://crystal-lang.org/api/1.10.1/Crystal.html#constant-summary

Because they're generated by the compiler, we cannot add documentation in the standard library. Unlike types or defs, constants cannot be reopened or redefined to attach documentation.

A related issue existed for pseudo methods which couldn't be documented either (#3041).
The solution was that the doc generator strips the prefix __crystal_pseudo_ from method names. This allows defining methods with the same signature as the pseudo methods which appear in their stead, enabling documentation on the prefixed methods.

We could consider a similar prefix solution for constants, i.e. defining constants with a prefix and substituting them for the actual constant names. At least their documentation. We want to show the values of the actual constants (this might be achieved with macro expansion).

module Crystal
  {% begin %}
    # Version of the current compiler.
    _CRYSTAL_PSEUDO_VERSION = {{ VERSION }}
  {% end %}
end

Alternatively, we could consider defining the actual constants in stdlib with values provided from the compiler. This would be a change in the interface between compiler and stdlib. Not sure if there would be any negative consequences. But I wouldn't like this very much. We only need to enable documentation, and that can be achieved without structural changes.

A third option would be to simply add documentation in the compiler. This is probably the easiest solution, it requires no structural changes to the doc generator or compiler. Adding docs to the constants' ASTNodes is trivial. It seems be a bit odd to have stdlib docs in the compiler, but that's already the case for Crystal::Macros. And like those, these constants are actually defined by the compiler, so it makes sense to document them there.

@HertzDevil
Copy link
Contributor

HertzDevil commented Dec 14, 2023

_CRYSTAL_PSEUDO_VERSION is a local variable, by the way.

Maybe we could allow declaring constants with a type but no initializer:

module Crystal
  # doc goes here
  VERSION : String
end

A : Int32
A = 1 # okay

B = 1 # okay
B : Int32

Then during cleanup we require any used constant to have 1 defined initializer. This would be a step beyond #13443.

@straight-shoota
Copy link
Member Author

I'm not sure if there would be relevant use cases for this. For documenting compiler constants there are much easier options. Besides that, I can't think of many reasons why you would declare a constant somewhere and assign the value somewhere else. Maybe with some kind of plug-in architecture / strategy pattern... But even then I don't see much value.
So it would probably just add a sizable amount of complexity with little gain.

@straight-shoota
Copy link
Member Author

Anyway, I think it's probably best to just start with the easiest solution, adding documentation in the compiler. That's trivial to do right now.
If we ever happen to implement any advanced mechanism to do this a different way, we can simply migrate and haven't really lost anything.

@devnote-dev
Copy link
Contributor

devnote-dev commented Dec 14, 2023

I would go a step further from HertzDevil's solution and only allow such syntax to be valid for documentation generation, forbidding it in normal language use. This could be used in combination with a new Docs annotation so that the compiler knows that the definition relates to one that exists in the program.

# docs_main.cr

# The version of the Crystal compiler.
@[Docs]
Crystal::VERSION : String


# Returns `true` if `self` is `Nil`.
#
# ```
# 1.nil?   # => false
# nil.nil? # => true
# ```
#
# This method is equivalent to `is_a?(Nil)`.
@[Docs]
def Object.nil? : Bool

Prefixing everything with _crystal_pseudo may have worked for methods but isn't a good way to go about documenting all hidden types.

(edit: GitHub posted the comment before I could finish writing it...)

@HertzDevil
Copy link
Contributor

In C++ you would do this:

// foo.h

struct Foo {
  static const int x;
};
// foo.cpp

#include "foo.h"
const int Foo::x = 1;

So separating a constant's declaration from its definition is actually not a far-fetched suggestion once we start dealing with incremental compilation units. But I agree that adding the documentation can be done without introducing any new language features.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants