Giving Arrays Map / Filter / Reduce: A Community Functional Extension Module
In Blueprint, when you want to "do something to every element of an array, or filter a batch by condition", you usually hand-wire a ForEach and grind through it. Verse arrays likewise ship without ready-made tools like Map / Filter / Reduce — but the community filled the gap long ago. This page borrows an open-source module to teach two hard-hitting moves the main lesson skipped: how to attach your own utility functions to a type, and how to pass a function itself into another function like a building block.
1. The Gap in Native Arrays — and the Community Patch
Lesson 16 established that Verse arrays are values you can't edit in place — no Blueprint-style Add / Remove nodes; the built-ins are basics like Slice and Find. But "transform every element", "filter a batch by condition", and "accumulate from head to tail" are the three power moves of everyday gameplay logic — surely you shouldn't hand-wire a ForEach every single time?
Community developer stueccles published an open-source Verse module that gives "a row of data" ([]t) its Map, Filter, and Reduce, throwing in a basketful of utilities like Sort, Unique, Shuffle, Reverse, and SumInts / SumFloats. Its value isn't just "grab and use" — it demonstrates two official constructs, and once you've learned them you can write your own set of utility functions for any type.
2. Move One: Extension Methods — Attaching a Function to a Type
Ever wondered why Items.Slice[0, 2] can be dotted out — the way you drag off an object's pin in Blueprint and "dot out" one of its functions? Because Verse supports extension methods: when defining a function, write "who receives it" in parentheses before the function name, and the function attaches itself to that type. Start with the plainest possible example — giving "a row of ints" a Sum:
# (Input:[]int) before the function name: an extension method attached to []int
(Input:[]int).Sum()<transacts>:int =
var Total:int = 0
for (Value : Input):
set Total += Value
Total
Read the snippet: (Input:[]int) before the function name tells Verse "this Sum attaches to a row of ints"; the body opens a counter Total, runs a ForEach to accumulate each number into it, and hands Total back at the end. Call it as if it grew on the array naturally: Scores.Sum(). This function cannot fail, so it's called with parentheses; square brackets are reserved for tools that might not succeed (like Slice).
3. Move Two: Function Types — Passing "What to Do" as a Parameter
The essence of Map: Map wires up the loop, and the caller decides "what to do to each element". That requires the parameter list to accept a "function" as a parameter — like binding a custom event to an Event Dispatcher in Blueprint: "what to do" is itself handed in as an input. In Verse you write type{_(:t)<transacts>:t} — read it as "a function that takes a t and gives back a t"; _ is a placeholder parameter name. The module's Map implementation is almost unfairly pretty — the core is one lap of ForEach:
# Generic Map: run Mapper over every element, produce a new array
# For the exact generic signature, defer to the module source and the official API docs
(Input:[]t where t:type).Map(Mapper:type{_(:t)<transacts>:t})<transacts>:[]t =
for (Value : Input):
Mapper(Value)
# An ordinary function that fits the Mapper shape
Double(N:int)<transacts>:int =
N * 2
Read the snippet: (Input:[]t where t:type) says "this Map attaches to any row of t"; the parameter Mapper is that function building block; the body is one lap of ForEach, calling Mapper once per element. Usage looks like this: Doubled := Numbers.Map(Double) — stuff the function name Double in as a value, and Map calls it once per lap for you. Remember the Lesson 16 bonus? Each ForEach lap's result automatically lines up into a new row, so Map doesn't even need a variable.
The module's Reduce shows another idea: recursion via Slice — handle the first element, then have the function call itself on the rest with Slice[1, Length], rolling results along into one accumulated value. Filter is just "a ForEach with a filter condition" in a wrapper. Understand these three and you'll see they're all permutations of Lesson 16's basic parts.
4. Pop Quiz
Writing type{_(:int)<transacts>:int} in a parameter list — what is it accepting?
After attaching Sum to "a row of ints" ((Input:[]int).Sum()<transacts>:int), how do you call it?
Sources & Further Reading
This page is distilled from community open-source code: stueccles's functional array extension module for Verse (GitHub Gist ↗). The module itself is short — read the source top to bottom; with Lesson 16 under your belt, you can understand every line.