Skip to content

refactor(builtin): default end to self.length() on view constructors - #4012

Open
Yu-zh wants to merge 1 commit into
mainfrom
Yu-zh/view-default-end-arg
Open

refactor(builtin): default end to self.length() on view constructors#4012
Yu-zh wants to merge 1 commit into
mainfrom
Yu-zh/view-default-end-arg

Conversation

@Yu-zh

@Yu-zh Yu-zh commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

What

The view/slice constructors in builtin all declared end? : Int with no default and then re-derived the missing case inside the body, each with a slightly different idiom:

let end = match end { Some(end) | (None with end = len) => end }
let end_offset = if end_offset is Some(o) { o } else { self.length() }
match end { None => fixed[start:], Some(e) => fixed[start:e] }

A default-argument expression can reference an earlier parameter, so end? : Int = self.length() states the same thing in the signature. The body then works with a plain Int and the unwrapping goes away — 26 functions, −62 lines net.

Covered types: ArrayView, MutArrayView, BytesView, StringView, ReadOnlyArray, UninitializedArray (plus the deprecated String::substring).

Why it is behavior-preserving

  • len-based cases — the default is literally the len the body already computed.
  • StringView::{get_view,sub} used self.end() in the None branch. self.start() + self.length() is the same value, since StringView::length() is defined as end() - start().
  • {String,StringView}::clamped_view clamped the Some branch into [0, len]. self.length() is already in range, so it clamps to itself.
  • ReadOnlyArray::view fell back to fixed[start:], whose own default is the fixed array's length — and ReadOnlyArray::length() is defined as exactly that.

moon info produces no .mbti diff: the generated interface renders both forms as end? : Int, so this is not a source- or interface-breaking change for downstream users.

Not converted

  • Iter::view genuinely needs the Option: an iterator has no length, and None selects a drop-only fast path that Some cannot express.
  • {Array,FixedArray}::fill take a range but are not view constructors, so they are left alone here.

Verification

moon check --deny-warn --target all          # clean
moon info --target wasm,wasm-gc,js,native    # no diff
moon fmt                                     # no diff
moon bundle --all                            # clean
moon test --target all                       # wasm 6966 / wasm-gc 6967 / js 6911 / native 6885, 0 failed
moon test --release --target js,wasm,wasm-gc # 0 failed
moon test --release --target native          # 6885 passed, 0 failed
MOONC_INTERNAL_PARAMS='use_js_builtin_string = 1 |' \
  moon test --target wasm-gc [--release]     # 6967 passed, 0 failed

@coveralls

coveralls commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 5891

Coverage decreased (-0.02%) to 90.447%

Details

  • Coverage decreased (-0.02%) from the base build.
  • Patch coverage: 6 of 6 lines across 2 files are fully covered (100%).
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 17440
Covered Lines: 15774
Line Coverage: 90.45%
Coverage Strength: 132021.41 hits per line

💛 - Coveralls

@Yu-zh
Yu-zh force-pushed the Yu-zh/view-default-end-arg branch from b6f4e9b to a865718 Compare August 12, 2026 08:25
The view/slice constructors all declared `end? : Int` (no default) and then
re-derived the missing case in the body, each with its own idiom:

    let end = match end { Some(end) | (None with end = len) => end }
    let end_offset = if end_offset is Some(o) { o } else { self.length() }
    match end { None => fixed[start:], Some(e) => fixed[start:e] }

Since MoonBit default-argument expressions can reference earlier parameters,
`end? : Int = self.length()` says the same thing in the signature, so the body
works with a plain `Int` and the unwrapping disappears. 26 functions across
`ArrayView`, `MutArrayView`, `BytesView`, `StringView`, `ReadOnlyArray` and
`UninitializedArray`.

Behavior is unchanged everywhere:

- For the `len`-based cases the default is literally the `len` the body
  computed.
- `StringView::{get_view,sub}` used `self.end()` for the `None` branch;
  `self.start() + self.length()` is the same value because
  `StringView::length() == end() - start()`.
- `{String,StringView}::clamped_view` clamped the `Some` branch into
  `[0, len]`; `self.length()` is already in range, so it clamps to itself.
- `ReadOnlyArray::view` fell back to `fixed[start:]`, whose own default is the
  fixed array's length, and `ReadOnlyArray::length()` is defined as exactly
  that.

`moon info` reports no `.mbti` change — the generated interface does not encode
default values — so this is not a source- or interface-breaking change for
downstream users.

Not converted: `Iter::view` genuinely needs `Option` (an iterator has no length,
and `None` selects a `drop`-only fast path), and `{Array,FixedArray}::fill` are
range-taking but not view constructors.
@bobzhang
bobzhang force-pushed the Yu-zh/view-default-end-arg branch from a865718 to f299681 Compare August 12, 2026 10:40
Comment thread builtin/arrayview.mbt
let end = match end {
Some(end) | (None with end = len) => end
}
guard start >= 0 && start <= end && end <= len else {

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.

what's the motivation? for easy inlining?
Note this could introduce some duplicated computation

@Yu-zh Yu-zh Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

duplicated computations are caused by the wrappers and I plan to inline them as well (if the size of default values are reasonably small)

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.

3 participants