Skip to content

boot.janet docstring improvements#1764

Open
andrewrothman wants to merge 5 commits into
janet-lang:masterfrom
andrewrothman:doc-improve
Open

boot.janet docstring improvements#1764
andrewrothman wants to merge 5 commits into
janet-lang:masterfrom
andrewrothman:doc-improve

Conversation

@andrewrothman

@andrewrothman andrewrothman commented Jun 11, 2026

Copy link
Copy Markdown

Hi! First contribution to Janet. It has been a very nice language to use.

Please let me know if you have any thoughts on these proposed changes or if changes like this are welcome / unwelcome.

For third person indicative mood...

This style is used by both Ruby and Lua and to me reads clearer:

For polymorphic compare...

Janet for Mortals chapter 8 discusses this behavior for some built-in functions.

https://janet.guide/tables-and-polymorphism/

In fact the only built-in functions that use polymorphic compare are zero?, pos?, neg?, one?, even?, and odd? ...

Later in that chapter...

Tables and abstract types are the only polymorphic values in Janet.

However, tested working for structs:

# create a prototype with compare function

(def my-proto
	{:compare (fn [self other] (compare (self :v) other))})

# define a function to create a struct with the above prototype

(defn make-test-struct [v]
	(struct/with-proto my-proto :v v))

# make some test structs

(def test-struct-1 (make-test-struct 0))
(def test-struct-2 (make-test-struct 5))

# test polymorphic compare on structs

(print (zero? test-struct-1)) # => true
(print (zero? test-struct-2)) # => false

Thanks!

@andrewrothman andrewrothman marked this pull request as ready for review June 11, 2026 04:49
@sogaiu

sogaiu commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Regarding the text:

Tables and abstract types are the only polymorphic values in Janet.

Please note that on the same linked page, the disclaimer from the author:

Remember that tables come in both mutable (“table”) and immutable (“struct”) varieties, but I’m going to use the term “table” loosely to mean “one of Janet’s associative data structures.” They’re exactly the same, apart from the mutability bit.

Also please note that the author of the text says:

I’m just a fan of Janet; I am not affiliated with the language in any way. I have no real qualifications to be writing a book about it, and nothing that you read here should be considered authoritative, idiomatic, or educational.

via: https://janet.guide/

On a side note, the term "dictionary" is used on at least one page in the website docs to refer to both tables and structs. There is also a function named dictionary?. It doesn't seem like that term is used much in the existing docstrings though.


It is true that the existing docstrings for zero? and friends leave things on the underspecified side. I'm not sure what the intention is there. I think this issue might give some hints about why things are the way they are regarding behavior though.

@andrewrothman

Copy link
Copy Markdown
Author

Please note that on the same linked page, the disclaimer from the author:

Ah I did miss that note, good catch, thanks.

Also please note that the author of the text says:

I should mention I'm a bit fan of the book - the author did a fantastic job (despite their claims that nothing there should be considered "educational" xD)

It is true that the existing docstrings for zero? and friends leave things on the underspecified side. I'm not sure what the intention is there. I think #272 might give some hints about why things are the way they are regarding behavior though.

Thanks for the link, interesting. It would seem best to confirm whether or not that's intended behavior for those symbols before changing their documentation.

On a side note, the term "dictionary" is used on at least one page in the website docs to refer to both tables and structs. There is also a function named dictionary?. It doesn't seem like that term is used much in the existing docstrings though.

This is also something I was looking into - but figured I'd start small and leave it for future discussion.

In general (personal opinion) I find these qualities really help when using reference documentation:

  • Be brief
    • I saw this commit by Calvin mentioned shortening some docstrings to reduce RAM usage, where he noted that docstrings aren't the same as tutorials... This to me makes sense, docstrings seem to be more a form of "reference documentation" which should be brief.
  • Focus on "what it's for" and "why you'd want to use it" (basically focus on what observable side-effects it produces or what it returns - what it will do for me, not how it does it)
    • ie. "Check if x is zero" -> "Returns true if x is zero, false otherwise."
  • Briefly note important edge-cases and error conditions.
    • Haven't dug into this yet... But ie. string/split seems to error on delimiter as empty string - that seems to me like something that should either be considered a bug, or briefly noted in the docstring.
      • ie. "The delimiter must be a non-empty string."

Another thing on indicative mood:

  • Seems Python and Julia use imperative mood (as opposed to indicative, which is common in other ecosystems) - there is a good discussion on the Julia forums
    • I would still argue that indicative is the clearer choice - because it's more clearly a description of what the symbol is for vs. a command / suggestion of what the user must do...
    • ie. "Map a function f over every value in a data structure ind" vs "Maps a function f over every value in a data structure ind"
      • "Maps a function" makes it clear at a glance that the docstring is describing what the function does when applied, whereas "Map a function" could be read as a command to me the user - that I must "map a function" for some reason, or before use.
    • This could just be the style I'm used to (but it is a broadly popular style) - maybe others have thoughts on this?

@McSinyx

McSinyx commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Other here, I prefer imperative mood as the docs can then be used in place of the calls or function signatures.

@sogaiu

sogaiu commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

I can appreciate wanting consistency for certain aspects of the docstrings, but practically speaking I'm not sure how well that can be made to hold up over time.

FWIW, my sense regarding what seems to have been valued for docstrings in janet itself include (not necessarily in this order):

  1. Compactness (I believe this is partly due to the goal of embedding. For reference, see this commit message.)
  2. Correctness

This is just what comes to mind when I think back over the PRs I've seen. Ultimately it's not my call though :)

@bakpakin

Copy link
Copy Markdown
Member

I am ambivalent to stylistic changes like tense here, but the point about structs is correct. Older versions of Janet did not have polymorphic structs, but they were later extended to behave more like tables.

@sogaiu

sogaiu commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

... the point about structs is correct. Older versions of Janet did not have polymorphic structs, but they were later extended to behave more like tables.

May be this PR should be just about the structs bit then?

@andrewrothman

Copy link
Copy Markdown
Author

Just pushed commits reverting the tense / mood.

Better to rebase it out of the history instead of revert? Let me know.

That leaves only the change to docstrings for polymorphic compare (isn't currently mentioned for tables or structs).

@sogaiu

sogaiu commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

I don't know how others feel about using the term "dictionary" to cover "table or struct", but FWIW some points in favor:

  • it is more concise
  • used in the website docs
  • there is a function named dictionary?
  • it is used in the C source in various places

Against might be:

  • currently not used in too many docstrings [1]
  • folks might not be as familiar with the term

Would like to hear what folks think.


[1] lengthable? is a notable exception, though we could make an effort to start using it more

@andrewrothman

Copy link
Copy Markdown
Author

Some variants to propose:

(dictionary? x)
    Check if x is a table or struct.
(dictionary? x)
    Check if x is a dictionary.
(dictionary? x)
    Check if x is a dictionary (table or struct).
(dictionary? x)
    Check if x is a dictionary data structure (table or struct).
(kvs dict)
    Takes a table or struct and returns a new array of key value pairs
    like @[k v k v ...].
(kvs dict)
    Takes a dictionary and returns a new array of key value pairs
    like @[k v k v ...].
(kvs dict)
    Takes a dictionary (table or struct) and returns a new array of key value pairs
    like @[k v k v ...].
(kvs dict)
    Takes a dictionary data structure (table or struct) and returns a new array of key value pairs
    like @[k v k v ...].
(kvs dict-ds)
    Takes a dictionary data structure (table or struct) and returns a new array of key value pairs
    like @[k v k v ...].

The "dictionary data structure (table or struct)" / "dict-ds" version is a bit more verbose, but does have two advantages:

  • Good on familiarity.
  • Works cleanly for arrays and tuples as well...
(tuple/slice idx-ds [,start=0 [,end=(length idx-ds)]])
    Take a sub-sequence of an indexed data structure (array or tuple) from index start inclusive
    to index end exclusive...

For "indexed" - "a dictionary" reads well but "an indexed" not as cleanly - at least to me, though it's probably something one could get used to after reading enough docstrings with that format. (though "idx" as the variable name often means the "index" integer)

Anyway - just some random thoughts for y'all to ponder.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants