Skip to content

Doctest stdlib timerelated#11327

Open
spoo wants to merge 11 commits into
erlang:masterfrom
spoo:doctest_stdlib_timerelated
Open

Doctest stdlib timerelated#11327
spoo wants to merge 11 commits into
erlang:masterfrom
spoo:doctest_stdlib_timerelated

Conversation

@spoo

@spoo spoo commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

This PR adds doctest examples to time related modules in Stdlib application for most of the functions

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

CT Test Results

    2 files    100 suites   1h 6m 55s ⏱️
2 306 tests 2 253 ✅ 52 💤 1 ❌
2 728 runs  2 671 ✅ 56 💤 1 ❌

For more details on these failures, see this check.

Results for commit 146a141.

♻️ This comment has been updated with latest results.

To speed up review, make sure that you have read Contributing to Erlang/OTP and that all checks pass.

See the TESTING and DEVELOPMENT HowTo guides for details about how to run test locally.

Artifacts

// Erlang/OTP Github Action Bot

@jhogberg jhogberg added the team:VM Assigned to OTP team VM label Jul 6, 2026
Comment thread lib/stdlib/src/random.erl Outdated
Comment on lines +206 to +207
2> F = random:uniform(), is_float(F), F > 0.0, F < 1.0.
true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This results in true for any F that is a number (including integers) that is less than 1.0, for example -5.

@spoo spoo Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing it . The idea was to demonstrate output as floating and between 0.0 and 1.0. Added 'and' operator now to correct the same than the 'comma' operator. Hope now is ok

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be andalso.

@spoo spoo Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

andalso is generally used for performance when we do not want the second operand to be evaluated, but since here emphasis is on user understanding and documentation i kept it as just "and". Just wondering if really need a change or if this would keep it simpler for user

@juhlig juhlig Jul 14, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and and or are deprecated and will raise warnings starting with OTP 29. Effectively, their usage is discouraged. Bringing them up again in documentation examples plainly runs against that. The need for precedence-parentheses that comes with them also makes the examples more confusing. (My 2ct anyway XD).

Comment thread lib/stdlib/src/random.erl Outdated
Comment on lines +208 to +209
3> I = random:uniform(10), I >= 1, I =< 10.
true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This results in true for any I that is a number (including floats) that is less than or equal to 10, for example -5.5.

Comment thread lib/stdlib/src/random.erl Outdated
Comment on lines +249 to +250
2> {Float, _NewState} = random:uniform_s(State), is_float(Float), Float > 0.0, Float < 1.0.
true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This results in true for any Float that is a number (including integers) that is less than 1.0, for example -5.

Comment thread lib/stdlib/src/random.erl Outdated
Comment on lines +251 to +252
3> {Int, _NewState2} = random:uniform_s(10, State), Int >= 1, Int =< 10.
true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This results in true for any Int that is a number (including floats) that is less than or equal to 10, for example -5.5.

Comment thread lib/stdlib/src/timer.erl Outdated
Comment on lines +55 to +56
1> ok = element(1, timer:apply_after(5000, io, format, ["~nHello World!~n", []])).
ok

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wrapping in element(1, ...) makes the examples pretty confusing. A newcomer will certainly wonder, "Is that needed? What for? 🤔"?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that it makes the example not too clear enough for beginers. I have reverted preexisting examples at start of document back to text script. For newer added examples, since the TRef that is given by apply_after is always dynamic its hard to pattern match output and fails by doctest function in performed by timer_suite module. As per my understanding, examples should pass doctest at anypoint and hence a way verifying they are correct , so here TRef being dynamically generated will not be able to pattern match and hence extracting first element 'ok which will also validate success. I am happy to discuss any other better method that could be adopted otherwise :)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I don't have much experience with doctests, but as far as I understand the documentation (and by a quick trial), something like...

1> timer:apply_after(5000, io, format, ["~nHello World!~n", []]).
{ok, TRef1}

... should work? Or, since you're not using TRef1 subsequently in the example, you could use {ok, _}.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I don't have much experience with doctests, but as far as I understand the documentation (and by a quick trial), something like...

1> timer:apply_after(5000, io, format, ["~nHello World!~n", []]).
{ok, TRef1}

... should work? Or, since you're not using TRef1 subsequently in the example, you could use {ok, _}.

Or...

1> {ok, _} = timer:apply_after(5000, io, format, ["~nHello World!~n", []]).

... and omit the return value if just want to insist on the ok?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we demonstrate only this one liner without output this works as we completly ignore the TRef which is dynamically generated. Was less aware of this way in doctest.

Comment thread lib/stdlib/src/timer.erl Outdated
Comment on lines +286 to +287
2> {ok, {send_local, Ref1}} = timer:send_after(1000, self(), world), is_reference(Ref1).
true

@Maria-12648430 Maria-12648430 Jul 13, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return type of timer:send_after/3 (and friends) is {ok, TRef} (or {error, Reason} for completeness' sake). TRef in turn is of type tref/0, which is an opaque type: you are not supposed to assume anything about what it actually looks like, as you do here with matching on {send_local, Ref1} and later is_reference(Ref1).

Comment thread lib/stdlib/src/random.erl Outdated
Returns a random float uniformly distributed between `0.0` and `1.0`, updating
the state in the process dictionary.
""".
-doc(#{equiv => uniform(1)}).

@juhlig juhlig Jul 13, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not correct. As you can see from the spec already, uniform/0 (this function) returns a float(), whereas uniform/1 (the function you declare as equivalent to uniform/0 here) returns a pos_integer().

That is, uniform(1) will always return integer 1, while uniform() will return a float F in the range 0.0 <= F <= 1.0.

Comment thread lib/stdlib/src/random.erl Outdated
Comment on lines +206 to +207
2> F = random:uniform() , is_float(F) and ((F>= 0.0) and (F =< 1.0)).
true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not belong here. As I said in my previous comment, uniform/0 is a different function (which just happens to have the same name).

Comment thread lib/stdlib/src/random.erl Outdated
Comment on lines +208 to +209
3> I = random:uniform(10), ((I >= 1) and (I =< 10)).
true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that this is especially enlightening, if anything it makes the example confusing. If you absolutely want to have the "I is an integer 1 =< I =< 10" demonstration in there, you should put it on a different line, like this:

3> I = random:uniform(10).
4> is_integer(I, 1, 10).
true

Comment thread lib/stdlib/src/random.erl Outdated
Returns, for a specified state, a random float uniformly distributed between
`0.0` and `1.0`, and a new state.
""".
-doc(#{equiv => uniform_s(1, State0)}).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as with uniform/0/uniform/1, this is not correct.

@juhlig

juhlig commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

One of your doctests for calendar is failing. This is not your fault though, the example in question itself is sound. I opened a ticket regarding the underlying issue, #11362.

@juhlig

juhlig commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Looking at your PR in general, you are focusing a lot on testable (vs enlightening) examples. This in turn adds a lot of noise which obfuscates what the examples are actually supposed to demonstrate. IMO, examples in documentation should, first of all, highlight the usage of the function it documents. If the examples can be tested without adding confusing noise, then by all means do so - but if the testability adds confusing noise, don't.

Think about it: there was documentation with imperfect, typo-ridden examples for decades, and people were fine with them nonetheless since it gave them a general idea of how to use something and what happened when. They were rarely used as they were written verbatim.

I guess what I'm trying to say is, there is nothing wrong with debugging examples and making sure that they stay debugged. But making them testable at all costs actually reduces their value for readers.

@spoo

spoo commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

Looking at your PR in general, you are focusing a lot on testable (vs enlightening) examples. This in turn adds a lot of noise which obfuscates what the examples are actually supposed to demonstrate. IMO, examples in documentation should, first of all, highlight the usage of the function it documents. If the examples can be tested without adding confusing noise, then by all means do so - but if the testability adds confusing noise, don't.

Think about it: there was documentation with imperfect, typo-ridden examples for decades, and people were fine with them nonetheless since it gave them a general idea of how to use something and what happened when. They were rarely used as they were written verbatim.

I guess what I'm trying to say is, there is nothing wrong with debugging examples and making sure that they stay debugged. But making them testable at all costs actually reduces their value for readers.

Thanks for feedback . Thinking on same lines, I have now tried to reiterate on some examples that felt like noise , removed emphasis on "testing" (for example (is_tuple(x), or just checking on ok etc) and have tried to rather emphasize on user readability and understanding. Since the doctest idea is that the code examples in module documentation remain guaranteed to stay correct because they are run during testing, have tried to keep in mind that they could be tested as far as possible :).

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

Labels

team:VM Assigned to OTP team VM

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants