0
def make-list [val?: string] {
    ["f1" "f2"]
}

def use-list [list: list] {
    $list | each { |it| $it + "!" }
}

let $val = "abc"
let $l = (make-list $val)
use-list $l

In this code, I have two functions, one to make a list, and another to consume that list. For the purposes of this MRE, make-list returns a simple hard-coded list, and use-list prints each element with an exclamation mark added to the end.

If this script executes correctly, it should print a list with two elements "f1!" and "f2!".

make-list has an optional parameter. In the code above, I create a $val variable, then pass that into make-list, and store the result of the pipeline to $l. Executing $l | describe instead of use-list $l prints list<string>, so I'm confident that $l is indeed a list.

The code above throws the following compile error:

Error: nu::parser::type_mismatch (link)

  × Type mismatch.
    ╭─[/.../test.nu:10:1]
 10 │ let $l = (make-list $val)
 11 │ use-list $l
    ·          ─┬
    ·           ╰── expected List(Any), found String
    ╰────

However, I can modify the let $l ... line to allow the script to compile and execute correctly. Both of these options will allow the script to compile and returns the expected result.

  • The parameter for make-list can be removed (because the parameter is optional).
    let $l = (make-list)
    
  • The parameter for make-list can be replaced by a string literal
    let $l = (make-list "abc")
    

I don't understand why using a variable as a parameter call is suddenly causing a type issue with the use-list function call. What am I doing wrong here?


As an interesting side note, and this might explain the "...found String" part of the error message, if I change the make-list parameter to an int:

def make-list [val?: int] {
    ["f1" "f2"]
}

# ...

let $val = 3
let $l = (make-list $val)
use-list $l

It will fail to compile with the same kind of error, but the "found" type will reflect the updated parameter type.

Error: nu::parser::type_mismatch (link)

  × Type mismatch.
    ╭─[/.../test.nu:10:1]
 10 │ let $l = (make-list $val)
 11 │ use-list $l
    ·          ─┬
    ·           ╰── expected List(Any), found Int
    ╰────

Tested using Nushell version 0.74.0 and 0.75.0 on Arch Linux and Windows 11, respectively.

gunr2171
  • 16,104
  • 25
  • 61
  • 88
  • Just came to my attention that if you **don't** use a subexpression when binding `$l`, it also works just fine: `let $val = "abc"; let $l = make-list $val; use-list $l` – pmf Feb 03 '23 at 15:46

1 Answers1

1

Something seems to "go wrong" with the variable binding. Your scenario does work if the literal string is being evaluated in a subexpression before it is being bound:

let $val = ("abc")
# or
let $val = do { "abc" }

Even a seemingly useless type casting later on does the trick:

let $val = "abc"
let $l = (make-list ($val | into string))

This is also consistent with other types. Adapting your int example:

def make-list [val?: int] { … }
…
let $val = 123    # fails
let $val = (123)  # works

Likewise, having a do closure, or type-casting using into int, do work as well.


Not of importance, however, is the syntax of the variable binding. With the $ sign omitted, all examples from above still work or fail under the same circumstances, e.g.:

let val = "abc"    # fails
let val = ("abc")  # works

As expected, using let l = instead of let $l = also yields the same results.

pmf
  • 24,478
  • 2
  • 22
  • 31