Use explicit in/out list types for vectorized commands (#9742)

# Description
All commands that declared `.vectorizes_over_list(true)` now also
explicitly declare the list form of their scalar types.

- Explicit in/out list signatures for nu-command
- Explicit in/out list signatures for nu-cmd-extra
- Add comments about cellpath behavior that is still unresolved


# User-Facing Changes
Our type signatures will now be more explicit about which commands
support vectorization over lists.
On the downside this is a bit more verbose and less systematic.
This commit is contained in:
Stefan Holderbach 2023-07-23 20:46:53 +02:00 committed by GitHub
parent 4dbdb1fe54
commit 17f8ad7210
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 247 additions and 23 deletions

View File

@ -24,6 +24,7 @@ impl Command for BitsNot {
), ),
]) ])
.vectorizes_over_list(true) .vectorizes_over_list(true)
.allow_variants_without_examples(true)
.switch( .switch(
"signed", "signed",
"always treat input number as a signed number", "always treat input number as a signed number",

View File

@ -30,8 +30,15 @@ impl Command for BytesAdd {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("bytes add") Signature::build("bytes add")
.input_output_types(vec![(Type::Binary, Type::Binary)]) .input_output_types(vec![
(Type::Binary, Type::Binary),
(
Type::List(Box::new(Type::Binary)),
Type::List(Box::new(Type::Binary)),
),
])
.vectorizes_over_list(true) .vectorizes_over_list(true)
.allow_variants_without_examples(true)
.required("data", SyntaxShape::Binary, "the binary to add") .required("data", SyntaxShape::Binary, "the binary to add")
.named( .named(
"index", "index",

View File

@ -37,7 +37,13 @@ impl Command for BytesAt {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("bytes at") Signature::build("bytes at")
.input_output_types(vec![(Type::Binary, Type::Binary)]) .input_output_types(vec![
(Type::Binary, Type::Binary),
(
Type::List(Box::new(Type::Binary)),
Type::List(Box::new(Type::Binary)),
),
])
.vectorizes_over_list(true) .vectorizes_over_list(true)
.required("range", SyntaxShape::Range, "the range to get bytes") .required("range", SyntaxShape::Range, "the range to get bytes")
.rest( .rest(

View File

@ -13,7 +13,14 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("math arccos") Signature::build("math arccos")
.switch("degrees", "Return degrees instead of radians", Some('d')) .switch("degrees", "Return degrees instead of radians", Some('d'))
.input_output_types(vec![(Type::Number, Type::Float)]) .input_output_types(vec![
(Type::Number, Type::Float),
(
Type::List(Box::new(Type::Number)),
Type::List(Box::new(Type::Float)),
),
])
.allow_variants_without_examples(true)
.vectorizes_over_list(true) .vectorizes_over_list(true)
.category(Category::Math) .category(Category::Math)
} }

View File

@ -12,7 +12,14 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("math arccosh") Signature::build("math arccosh")
.input_output_types(vec![(Type::Number, Type::Float)]) .input_output_types(vec![
(Type::Number, Type::Float),
(
Type::List(Box::new(Type::Number)),
Type::List(Box::new(Type::Float)),
),
])
.allow_variants_without_examples(true)
.vectorizes_over_list(true) .vectorizes_over_list(true)
.category(Category::Math) .category(Category::Math)
} }

View File

@ -13,7 +13,14 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("math arcsin") Signature::build("math arcsin")
.switch("degrees", "Return degrees instead of radians", Some('d')) .switch("degrees", "Return degrees instead of radians", Some('d'))
.input_output_types(vec![(Type::Number, Type::Float)]) .input_output_types(vec![
(Type::Number, Type::Float),
(
Type::List(Box::new(Type::Number)),
Type::List(Box::new(Type::Float)),
),
])
.allow_variants_without_examples(true)
.vectorizes_over_list(true) .vectorizes_over_list(true)
.category(Category::Math) .category(Category::Math)
} }

View File

@ -12,7 +12,14 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("math arcsinh") Signature::build("math arcsinh")
.input_output_types(vec![(Type::Number, Type::Float)]) .input_output_types(vec![
(Type::Number, Type::Float),
(
Type::List(Box::new(Type::Number)),
Type::List(Box::new(Type::Float)),
),
])
.allow_variants_without_examples(true)
.vectorizes_over_list(true) .vectorizes_over_list(true)
.category(Category::Math) .category(Category::Math)
} }

View File

@ -13,7 +13,14 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("math arctan") Signature::build("math arctan")
.switch("degrees", "Return degrees instead of radians", Some('d')) .switch("degrees", "Return degrees instead of radians", Some('d'))
.input_output_types(vec![(Type::Number, Type::Float)]) .input_output_types(vec![
(Type::Number, Type::Float),
(
Type::List(Box::new(Type::Number)),
Type::List(Box::new(Type::Float)),
),
])
.allow_variants_without_examples(true)
.vectorizes_over_list(true) .vectorizes_over_list(true)
.category(Category::Math) .category(Category::Math)
} }

View File

@ -12,7 +12,14 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("math arctanh") Signature::build("math arctanh")
.input_output_types(vec![(Type::Number, Type::Float)]) .input_output_types(vec![
(Type::Number, Type::Float),
(
Type::List(Box::new(Type::Number)),
Type::List(Box::new(Type::Float)),
),
])
.allow_variants_without_examples(true)
.vectorizes_over_list(true) .vectorizes_over_list(true)
.category(Category::Math) .category(Category::Math)
} }

View File

@ -12,7 +12,14 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("math cosh") Signature::build("math cosh")
.input_output_types(vec![(Type::Number, Type::Float)]) .input_output_types(vec![
(Type::Number, Type::Float),
(
Type::List(Box::new(Type::Number)),
Type::List(Box::new(Type::Float)),
),
])
.allow_variants_without_examples(true)
.vectorizes_over_list(true) .vectorizes_over_list(true)
.category(Category::Math) .category(Category::Math)
} }

View File

@ -12,7 +12,14 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("math exp") Signature::build("math exp")
.input_output_types(vec![(Type::Number, Type::Float)]) .input_output_types(vec![
(Type::Number, Type::Float),
(
Type::List(Box::new(Type::Number)),
Type::List(Box::new(Type::Float)),
),
])
.allow_variants_without_examples(true)
.vectorizes_over_list(true) .vectorizes_over_list(true)
.category(Category::Math) .category(Category::Math)
} }

View File

@ -12,7 +12,14 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("math ln") Signature::build("math ln")
.input_output_types(vec![(Type::Number, Type::Float)]) .input_output_types(vec![
(Type::Number, Type::Float),
(
Type::List(Box::new(Type::Number)),
Type::List(Box::new(Type::Float)),
),
])
.allow_variants_without_examples(true)
.vectorizes_over_list(true) .vectorizes_over_list(true)
.category(Category::Math) .category(Category::Math)
} }

View File

@ -12,7 +12,14 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("math sinh") Signature::build("math sinh")
.input_output_types(vec![(Type::Number, Type::Float)]) .input_output_types(vec![
(Type::Number, Type::Float),
(
Type::List(Box::new(Type::Number)),
Type::List(Box::new(Type::Float)),
),
])
.allow_variants_without_examples(true)
.vectorizes_over_list(true) .vectorizes_over_list(true)
.category(Category::Math) .category(Category::Math)
} }

View File

@ -12,7 +12,14 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("math tanh") Signature::build("math tanh")
.input_output_types(vec![(Type::Number, Type::Float)]) .input_output_types(vec![
(Type::Number, Type::Float),
(
Type::List(Box::new(Type::Number)),
Type::List(Box::new(Type::Float)),
),
])
.allow_variants_without_examples(true)
.vectorizes_over_list(true) .vectorizes_over_list(true)
.category(Category::Math) .category(Category::Math)
} }

View File

@ -46,6 +46,10 @@ impl Command for SubCommand {
) )
.input_output_types(vec![ .input_output_types(vec![
(Type::String, Type::String), (Type::String, Type::String),
(
Type::List(Box::new(Type::String)),
Type::List(Box::new(Type::String)),
),
(Type::Table(vec![]), Type::Table(vec![])), (Type::Table(vec![]), Type::Table(vec![])),
]) ])
.vectorizes_over_list(true) .vectorizes_over_list(true)

View File

@ -15,7 +15,14 @@ impl Command for DecodeHex {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("decode hex") Signature::build("decode hex")
.input_output_types(vec![(Type::String, Type::Binary)]) .input_output_types(vec![
(Type::String, Type::Binary),
(
Type::List(Box::new(Type::String)),
Type::List(Box::new(Type::Binary)),
),
])
.allow_variants_without_examples(true)
.vectorizes_over_list(true) .vectorizes_over_list(true)
.rest( .rest(
"rest", "rest",

View File

@ -15,7 +15,14 @@ impl Command for EncodeHex {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("encode hex") Signature::build("encode hex")
.input_output_types(vec![(Type::Binary, Type::String)]) .input_output_types(vec![
(Type::Binary, Type::String),
(
Type::List(Box::new(Type::Binary)),
Type::List(Box::new(Type::String)),
),
])
.allow_variants_without_examples(true)
.vectorizes_over_list(true) .vectorizes_over_list(true)
.rest( .rest(
"rest", "rest",

View File

@ -47,8 +47,15 @@ impl Command for Fill {
(Type::Float, Type::String), (Type::Float, Type::String),
(Type::String, Type::String), (Type::String, Type::String),
(Type::Filesize, Type::String), (Type::Filesize, Type::String),
(Type::List(Box::new(Type::Int)), Type::List(Box::new(Type::String))),
(Type::List(Box::new(Type::Float)), Type::List(Box::new(Type::String))),
(Type::List(Box::new(Type::String)), Type::List(Box::new(Type::String))),
(Type::List(Box::new(Type::Filesize)), Type::List(Box::new(Type::String))),
// General case for heterogeneous lists
(Type::List(Box::new(Type::Any)), Type::List(Box::new(Type::String))),
]) ])
.vectorizes_over_list(true) .vectorizes_over_list(true)
.allow_variants_without_examples(true)
.named( .named(
"width", "width",
SyntaxShape::Int, SyntaxShape::Int,

View File

@ -22,7 +22,29 @@ impl Command for SubCommand {
(Type::String, Type::Filesize), (Type::String, Type::Filesize),
(Type::Filesize, Type::Filesize), (Type::Filesize, Type::Filesize),
(Type::Table(vec![]), Type::Table(vec![])), (Type::Table(vec![]), Type::Table(vec![])),
(
Type::List(Box::new(Type::Int)),
Type::List(Box::new(Type::Filesize)),
),
(
Type::List(Box::new(Type::Number)),
Type::List(Box::new(Type::Filesize)),
),
(
Type::List(Box::new(Type::String)),
Type::List(Box::new(Type::Filesize)),
),
(
Type::List(Box::new(Type::Filesize)),
Type::List(Box::new(Type::Filesize)),
),
// Catch all for heterogeneous lists.
(
Type::List(Box::new(Type::Any)),
Type::List(Box::new(Type::Filesize)),
),
]) ])
.allow_variants_without_examples(true)
.vectorizes_over_list(true) .vectorizes_over_list(true)
.rest( .rest(
"rest", "rest",

View File

@ -23,6 +23,7 @@ impl Command for SubCommand {
), ),
]) ])
.vectorizes_over_list(true) .vectorizes_over_list(true)
.allow_variants_without_examples(true)
.named( .named(
"precision", "precision",
SyntaxShape::Number, SyntaxShape::Number,

View File

@ -18,8 +18,17 @@ impl Command for DecodeBase64 {
.input_output_types(vec![ .input_output_types(vec![
(Type::String, Type::String), (Type::String, Type::String),
(Type::String, Type::Binary), (Type::String, Type::Binary),
(
Type::List(Box::new(Type::String)),
Type::List(Box::new(Type::String)),
),
(
Type::List(Box::new(Type::String)),
Type::List(Box::new(Type::Binary)),
),
]) ])
.vectorizes_over_list(true) .vectorizes_over_list(true)
.allow_variants_without_examples(true)
.named( .named(
"character-set", "character-set",
SyntaxShape::String, SyntaxShape::String,

View File

@ -18,8 +18,23 @@ impl Command for EncodeBase64 {
.input_output_types(vec![ .input_output_types(vec![
(Type::String, Type::String), (Type::String, Type::String),
(Type::Binary, Type::String), (Type::Binary, Type::String),
(
Type::List(Box::new(Type::String)),
Type::List(Box::new(Type::String)),
),
(
Type::List(Box::new(Type::Binary)),
Type::List(Box::new(Type::String)),
),
// Relaxed for heterogeneous list.
// Should be removed as soon as the type system supports better restrictions
(
Type::List(Box::new(Type::Any)),
Type::List(Box::new(Type::String)),
),
]) ])
.vectorizes_over_list(true) .vectorizes_over_list(true)
.allow_variants_without_examples(true)
.named( .named(
"character-set", "character-set",
SyntaxShape::String, SyntaxShape::String,

View File

@ -19,9 +19,14 @@ impl Command for SubCommand {
Signature::build("str camel-case") Signature::build("str camel-case")
.input_output_types(vec![ .input_output_types(vec![
(Type::String, Type::String), (Type::String, Type::String),
(
Type::List(Box::new(Type::String)),
Type::List(Box::new(Type::String)),
),
(Type::Table(vec![]), Type::Table(vec![])), (Type::Table(vec![]), Type::Table(vec![])),
]) ])
.vectorizes_over_list(true) .vectorizes_over_list(true)
.allow_variants_without_examples(true)
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,

View File

@ -17,9 +17,14 @@ impl Command for SubCommand {
Signature::build("str capitalize") Signature::build("str capitalize")
.input_output_types(vec![ .input_output_types(vec![
(Type::String, Type::String), (Type::String, Type::String),
(
Type::List(Box::new(Type::String)),
Type::List(Box::new(Type::String)),
),
(Type::Table(vec![]), Type::Table(vec![])), (Type::Table(vec![]), Type::Table(vec![])),
]) ])
.vectorizes_over_list(true) .vectorizes_over_list(true)
.allow_variants_without_examples(true)
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,

View File

@ -17,9 +17,14 @@ impl Command for SubCommand {
Signature::build("str downcase") Signature::build("str downcase")
.input_output_types(vec![ .input_output_types(vec![
(Type::String, Type::String), (Type::String, Type::String),
(
Type::List(Box::new(Type::String)),
Type::List(Box::new(Type::String)),
),
(Type::Table(vec![]), Type::Table(vec![])), (Type::Table(vec![]), Type::Table(vec![])),
]) ])
.vectorizes_over_list(true) .vectorizes_over_list(true)
.allow_variants_without_examples(true)
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,

View File

@ -20,8 +20,13 @@ impl Command for SubCommand {
.input_output_types(vec![ .input_output_types(vec![
(Type::String, Type::String), (Type::String, Type::String),
(Type::Table(vec![]), Type::Table(vec![])), (Type::Table(vec![]), Type::Table(vec![])),
(
Type::List(Box::new(Type::String)),
Type::List(Box::new(Type::String)),
),
]) ])
.vectorizes_over_list(true) .vectorizes_over_list(true)
.allow_variants_without_examples(true)
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,

View File

@ -20,8 +20,13 @@ impl Command for SubCommand {
.input_output_types(vec![ .input_output_types(vec![
(Type::String, Type::String), (Type::String, Type::String),
(Type::Table(vec![]), Type::Table(vec![])), (Type::Table(vec![]), Type::Table(vec![])),
(
Type::List(Box::new(Type::String)),
Type::List(Box::new(Type::String)),
),
]) ])
.vectorizes_over_list(true) .vectorizes_over_list(true)
.allow_variants_without_examples(true)
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,

View File

@ -18,9 +18,14 @@ impl Command for SubCommand {
Signature::build("str screaming-snake-case") Signature::build("str screaming-snake-case")
.input_output_types(vec![ .input_output_types(vec![
(Type::String, Type::String), (Type::String, Type::String),
(
Type::List(Box::new(Type::String)),
Type::List(Box::new(Type::String)),
),
(Type::Table(vec![]), Type::Table(vec![])), (Type::Table(vec![]), Type::Table(vec![])),
]) ])
.vectorizes_over_list(true) .vectorizes_over_list(true)
.allow_variants_without_examples(true)
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,

View File

@ -18,9 +18,14 @@ impl Command for SubCommand {
Signature::build("str snake-case") Signature::build("str snake-case")
.input_output_types(vec![ .input_output_types(vec![
(Type::String, Type::String), (Type::String, Type::String),
(
Type::List(Box::new(Type::String)),
Type::List(Box::new(Type::String)),
),
(Type::Table(vec![]), Type::Table(vec![])), (Type::Table(vec![]), Type::Table(vec![])),
]) ])
.vectorizes_over_list(true) .vectorizes_over_list(true)
.allow_variants_without_examples(true)
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,

View File

@ -19,9 +19,14 @@ impl Command for SubCommand {
Signature::build("str title-case") Signature::build("str title-case")
.input_output_types(vec![ .input_output_types(vec![
(Type::String, Type::String), (Type::String, Type::String),
(
Type::List(Box::new(Type::String)),
Type::List(Box::new(Type::String)),
),
(Type::Table(vec![]), Type::Table(vec![])), (Type::Table(vec![]), Type::Table(vec![])),
]) ])
.vectorizes_over_list(true) .vectorizes_over_list(true)
.allow_variants_without_examples(true)
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,

View File

@ -16,6 +16,10 @@ impl Command for SubCommand {
Signature::build("str upcase") Signature::build("str upcase")
.input_output_types(vec![ .input_output_types(vec![
(Type::String, Type::String), (Type::String, Type::String),
(
Type::List(Box::new(Type::String)),
Type::List(Box::new(Type::String)),
),
(Type::Table(vec![]), Type::Table(vec![])), (Type::Table(vec![]), Type::Table(vec![])),
]) ])
.vectorizes_over_list(true) .vectorizes_over_list(true)

View File

@ -31,6 +31,7 @@ impl Command for SubCommand {
Signature::build("str contains") Signature::build("str contains")
.input_output_types(vec![ .input_output_types(vec![
(Type::String, Type::Bool), (Type::String, Type::Bool),
// TODO figure out cell-path type behavior
(Type::Table(vec![]), Type::Table(vec![])), (Type::Table(vec![]), Type::Table(vec![])),
(Type::List(Box::new(Type::String)), Type::List(Box::new(Type::Bool))) (Type::List(Box::new(Type::String)), Type::List(Box::new(Type::Bool)))
]) ])

View File

@ -28,7 +28,10 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("str ends-with") Signature::build("str ends-with")
.input_output_types(vec![(Type::String, Type::Bool)]) .input_output_types(vec![
(Type::String, Type::Bool),
(Type::List(Box::new(Type::String)), Type::List(Box::new(Type::Bool))),
])
.vectorizes_over_list(true) .vectorizes_over_list(true)
.required("string", SyntaxShape::String, "the string to match") .required("string", SyntaxShape::String, "the string to match")
.rest( .rest(
@ -73,9 +76,12 @@ impl Command for SubCommand {
result: Some(Value::test_bool(true)), result: Some(Value::test_bool(true)),
}, },
Example { Example {
description: "Checks if string ends with '.txt'", description: "Checks if strings end with '.txt'",
example: "'my_library.rb' | str ends-with '.txt'", example: "['my_library.rb', 'README.txt'] | str ends-with '.txt'",
result: Some(Value::test_bool(false)), result: Some(Value::test_list(vec![
Value::test_bool(false),
Value::test_bool(true),
])),
}, },
Example { Example {
description: "Checks if string ends with '.RB', case-insensitive", description: "Checks if string ends with '.RB', case-insensitive",

View File

@ -22,8 +22,15 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("str expand") Signature::build("str expand")
.input_output_types(vec![(Type::String, Type::List(Box::new(Type::String)))]) .input_output_types(vec![
(Type::String, Type::List(Box::new(Type::String))),
(
Type::List(Box::new(Type::String)),
Type::List(Box::new(Type::List(Box::new(Type::String)))),
),
])
.vectorizes_over_list(true) .vectorizes_over_list(true)
.allow_variants_without_examples(true)
.category(Category::Strings) .category(Category::Strings)
} }

View File

@ -37,8 +37,9 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("str index-of") Signature::build("str index-of")
.input_output_types(vec![(Type::String, Type::Int)]) .input_output_types(vec![(Type::String, Type::Int),(Type::List(Box::new(Type::String)), Type::List(Box::new(Type::Int)))])
.vectorizes_over_list(true) // TODO: no test coverage .vectorizes_over_list(true) // TODO: no test coverage
.allow_variants_without_examples(true)
.required("string", SyntaxShape::String, "the string to find in the input") .required("string", SyntaxShape::String, "the string to find in the input")
.switch( .switch(
"grapheme-clusters", "grapheme-clusters",

View File

@ -36,6 +36,7 @@ impl Command for SubCommand {
Signature::build("str replace") Signature::build("str replace")
.input_output_types(vec![ .input_output_types(vec![
(Type::String, Type::String), (Type::String, Type::String),
// TODO: clarify behavior with cellpath-rest argument
(Type::Table(vec![]), Type::Table(vec![])), (Type::Table(vec![]), Type::Table(vec![])),
( (
Type::List(Box::new(Type::String)), Type::List(Box::new(Type::String)),

View File

@ -30,8 +30,9 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("str starts-with") Signature::build("str starts-with")
.input_output_types(vec![(Type::String, Type::Bool)]) .input_output_types(vec![(Type::String, Type::Bool),(Type::List(Box::new(Type::String)), Type::List(Box::new(Type::Bool)))])
.vectorizes_over_list(true) .vectorizes_over_list(true)
.allow_variants_without_examples(true)
.required("string", SyntaxShape::String, "the string to match") .required("string", SyntaxShape::String, "the string to match")
.rest( .rest(
"rest", "rest",

View File

@ -42,7 +42,7 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("str substring") Signature::build("str substring")
.input_output_types(vec![(Type::String, Type::String), (Type::Table(vec![]), Type::Table(vec![]))]) .input_output_types(vec![(Type::String, Type::String), (Type::List(Box::new(Type::String)), Type::List(Box::new(Type::String))), (Type::Table(vec![]), Type::Table(vec![]))])
.vectorizes_over_list(true) .vectorizes_over_list(true)
.allow_variants_without_examples(true) .allow_variants_without_examples(true)
.switch( .switch(