Expand

for

iterate over items or stream of items

<for>  = for [<decl>[, <decl>]] [in <expr>] <body>
<body> = { <statements> }
       | if <expr> <body> 
       | [pass]
       | <command>

Input

for is very versatile and can work in two input modes:

for defines a new variable or reuses an existing one, if it's already present in the parent scope. This is somewhat consistent with := behavior, except := requires at least one new variable to be defined. Each for has its own scope, new variables are not visible outside of this scope.

for-in allows to specify two variables, in this case the first one would contain a key or an index, and the second will be an item itself. stream-for supports only one.

for each item in $list, assign it to $item and run body
for $item in $list {
    print "Got an $item"
}
stream dictionary values (keys are ignored)
for $value in $map {
    put $value
}
stream dictionary keys (values are ignored)
for $key, _ in $map {
    put $value
}
make first 10 powers of 2
for $i in (range 10) {
    put (2 ** $i)
}
print file sizes in the current directory
ls | for $file {
    $stat := os stat $file
    print "$file size is {$stat.size} bytes"
}
consume function's input stream using >>, threat it like filenames and create a map with stat information for each file
fn file-stats {
    $stats := {}
    >> for $file {
        $stats[$file] = os stat $file
    }
    put $stats
}
ls | file-stats

Filtering

It's possible to iterate only on items that match a predicate using if syntax. Items, that don't match the predicate, are discarded. It's possible to specify if multiple times. There's no else part.

print a first few even numbers
for $i in (range 10) if $i%2 == 0 {
    print "$i is even"
}
output files, whose file name is longer 20 symbols
ls | for $file if (len $file) > 20 {
    cat $file
}

Pass Through

Instead of using { put $item } as a body, it's possible to use pass or to to not provide body at all.

output a first few even numbers for $i in (range 10) if $i%2 == 0
output file names, that are longer than 20 symbols ls | for $f if (len $f) > 20
save file names, that are longer than 20 symbols, in a file
ls
  | for $f
    if (len $f) > 20
    pass > long-files.txt

Pattern Matching

String and list pattern matching can be used inside for's declarations.

convert all jpg files to png
find . -name *.jpg
   | for "$f.jpg" {
       convert "$f.jpg" "$f.png"
   }
apply :colons formatter before assigning to a variable
cat /etc/passwd | for (colons)$user {
    print "{$user[0]}'s homedir is {$user[5]}"
}

Direct Commands

A single command can be written without using { }. This should feel similar to xargs -l1, but with variables specified explicitly.

run uptime on all hosts via ssh cat hosts.txt | for $h ssh $h uptime
convert all jpg files to png
find . -name *.jpg |
    for "{$f}.jpg" convert "$f.jpg" "$f.png"

Discussion: this is, perhaps, too dense. Even if it's okay syntactically, it is maybe worth to put an arrow before command:

cat hosts.txt | for $h -> ssh $h uptime

See Also