Documenting elements of a struct

It’s good to not add lots of uneccesary dependencies (it can help with compilation time etc), so if you find the alternative sufficient, then stick with that. On the other hand, packages are there to be used, and Julia makes barrier to entry for dependencies really low. Julia also started moving lots of base code into seperate packages, like Move SharedArrays to a package · Issue #23713 · JuliaLang/julia · GitHub and encourages the community to expand and use the package infrastructure.

So basically all in moderation. You seem to know what you aim for, so stick with that

2 Likes

Yeah I’ve got no problem with dependences. But putting a documentation dependence in the package itself feels wrong.

Ah okay I think I understand what you mean, like generating documentation should be a seperate process to the core package and therefore adding a documentation package in the source code is not a good seperation of concern.

Maybe rethink the goal of docstrings, it’s not just for generating documentation, it’s also for attaching metadata to your code, something we all appreciate when using such code interactively. DocStringExtensions is for extending docstring annotations. And those annotations already live next to the actual code (and rightfully so). So wanting to extend the annotations may rightfully call for a package that does exactly that, even if your package has completely different goals and outcomes.

Btw I’m not advocating for adding the package, especially if you only want to annotate a single struct. I’m just saying that to me it’s perfectly healthy to add a docstring extension package to just about any package that wants to use extra docstring features. Your code is in essence not just code for outcome’s sake, but also serves as documentation. That is, documentation via your source code, your comments and your docstrings.

7 Likes

Totally agree — in theory. But it’s really a design decision where to draw the boundary between what functionality the package provides, and how its documentation is generated (REPL or otherwise). IMV, REPL help should be coming from the static docs, which should in turn be generated (as they are) by a separate environment. But I can certainly see how one might design things differently.

I cannot agree with this. Fields are considered by many (me too) to be internal implementation details. If they are documented as part of the interface, that means changes to fields are breaking and require updating major version. Fixing bugs or reorganizing implementations risks breaking other people’s code, completely unnecessarily.

Imho, field names should be possible to change without notice. For those who want fields as part of the API, by all means document them, but this should not be a general requirement.

1 Like

If that’s the case then they shouldn’t be publicly accessible, or the default behavior of Base.getproperty should be different. Relying on the absence of documentation to protect them seems like weak language design, but I’m sure this has been discussed before at length and there’s wisdom here that I’m missing.

2 Likes

It’s helpful to allow hackery so it’s good to allow accessing nonpublic properties. However, I do think it could be useful to have a separate function getprivateproperty instead of using the same syntax as public-property access to visually distinguish the two. Maybe the compiler could even use more optimizations if it knows I’m not using any private properties.

2 Likes

This is the same as in Python. Everything is accessible. Accessing fields is hacking. You can do it, but your code can break. I think that’s a reasonable compromise.

1 Like

Not quite: __.

You can access everything, right? It’s only about convention. If the convention is that fields should not be accessed (unfortunately, there isn’t quite consensus about this) then that’s as good as using underscores (or even better). The reason python uses underscores is exactly because field access is generally done, otherwise they would be redundant.

1 Like

@orome’s point is that while self._a can always be accessed as x._a, double-underscores get “mangled”: self.__a needs to be accessed as x.__Foo_a from outside the class. It’s not actually that common to double underscores in my experience.

1 Like

Ok, so it’s mangled as an extra barrier. But it’s still accessible, and I don’t see how that convention is more robust than “Don’t access fields, period.”

And anyway, as far as I have seen, the double underscore is used for ‘special methods’, not for general internal fields. A single leading underscore says ‘leave me alone’.

1 Like

__foo__ doesn’t get mangled, only __foo. 9. Classes — Python 3.10.6 documentation

What a mess…

2 Likes

I agree that struct fields should be considered implementation details unless specified otherwise, but I find that documenting things that are not part of the API is also useful. I docstring a lot of internal functions, type specs, and fields, so that when I come back to the code 2 years after I wrote it I can navigate it better. Also, this helps potential contributors.

11 Likes

Indeed inline comments are absolutely good and useful. But I wouldn’t like it if those comments were automatically collected into the docstring/documentation of the type.

Just to clarify: I am talking about docstrings ("..."), not comments (# ...). I think that using docstings to document your internals is good practice.

The fact that something is not part of the API does not mean that it cannot or should not be documented, nor should we refrain from using docstrings for this purpose, they are so much nicer than comments.

Conversely, the fact that some type/function/field is documented does not mean that it is part of the API, only that it is documented.

2 Likes

Yes, all of the above and also that Python actually does the opposite of what some are saying should be the case for Julia: All fields are public and treated that way, and it’s even the case that something special (a form of “documentation” really) is recommended for elements that aren’t.

In any case, the OP is answered. What’s lingering here is some confusion (probably just on my part) about the assertion that all elements of a Julia struct should be considered private since (1) that’s news to me (2) it seems wrong or at least constraining in general and, critically, (3) if that is true, then why does the language fail to enforce it?

I usually follow the convention of

  • things that are exported are public API, everything else is internal
  • unless stated otherwise, struct fields are internal
  • constructors are usually fair game, though I usually hide the default constructors in favor of custom ones

This has the advantage of not hiding things from people who want to look under the hood (maybe they have a usecase I haven’t thought of and need direct access?) but still being able to document what I’m doing for my own future benefit.

1 Like

There’s no one way to make everyone happy, but the reason I make my fields “internal” only has to do with what I feel is only data. The exact representation shouldn’t matter, what matters to me is what you can do with it, e.g. the functions you can use to manipulate that data.

This may seem extreme to certain OOP views, where data and methods that work on that data are much mote closely coupled than in julia.

It’s not that julia fails to enforce this, it’s chosen not to do so by design. Both structs and functions operating on structs (and functions!) are of equal standing in julia, so the argument somewhat would boil down to “I don’t want code outside of my module to interact with that field”, to which I reply “Then I’ll just eval a method into your module that does what I want :man_shrugging:”.

1 Like