Description
proposal
- for all new APIs, instead of passing a string/seq with optional start/end params, use a single openArray[T] param (openArray[char] for string)
- for existing APIs, deprecate existing such routines and add an overload taking a
openArray[T]
param, while ensuring that the openArray overload is picked when no start/end param is passed (see default last in strutils.find to -1 Nim#18173 (comment) for how to do that) - if the string/seq is a var param, a VM+js limitation (toOpenArray with var openArray doesn't work in nim js Nim#15952) currently prevents this, but that bug is fixable (refs toOpenArray with var openArray doesn't work in nim js Nim#15952 (comment))
- the only exception is when a lookbehind/lookahead behavior is needed, as in regex, refs re, nre have wrong
start
semantics Nim#14284 (comment)
benefits
- simpler APIs (less overloads needed) and separation of concern (callee doesn't need to care where the slice came from), less code bloat
- no broken edge cases (see example 2 below), no design by convention
- more flexible
example 1
- findBounds has 6 overloads ([std/re] fix findBounds and find procs Nim#18028 (comment)), and even then it's still not flexible enough, instead we should have a single proc:
proc findBounds*(input: openArray[char], pattern: Regex, matches: var openArray[Slice[int]],
start = 0): tuple[Slice[int]]
which would subsume all others and also be strictly more flexible, and less error prone
refs nim-lang/Nim#18028 (comment)
example 2
(refs nim-lang/Nim#18173)
optional start/last parameters lead to bad API's where a valid value ends up hijacked with a different meaning, eg:
func find*(a: SkipTable, s, sub: string, start: Natural = 0, last = 0): int
here, when user specifies last = 0
, it ends up meaning last = sub.high, so there's no way for user to specify the edge condition of last = 0
, which should be a valid choice
changing the signature to func find*(a: SkipTable, s, sub: string, start: Natural = 0, last = -1): int
would be an improvement but is still unsatisfactory, as this could equally be interpreted as meaning s.high or empty slice of s (EDIT: func find*(a: SkipTable, s, sub: string, start: Natural = 0, last = sub.high): int
would be possible too)
instead, func find*(a: SkipTable, s: openArray[char], sub: string): int
would be self-documenting and not have any edge cases, the user then would call simply toOpenArray(s, start, end)
if they want a slice