1// Package fp provides functional programming utilities for Gno, enabling2// transformations, filtering, and other operations on slices of any.3//4// Example of chaining operations:5//6// numbers := []any{1, 2, 3, 4, 5, 6}7//8// // Define predicates, mappers and reducers9// isEven := func(v any) bool { return v.(int)%2 == 0 }10// double := func(v any) any { return v.(int) * 2 }11// sum := func(a, b any) any { return a.(int) + b.(int) }12//13// // Chain operations: filter even numbers, double them, then sum14// evenNums := Filter(numbers, isEven) // [2, 4, 6]15// doubled := Map(evenNums, double) // [4, 8, 12]16// result := Reduce(doubled, sum, 0) // 2417//18// // Alternative: group by even/odd, then get even numbers19// byMod2 := func(v any) any { return v.(int) % 2 }20// grouped := GroupBy(numbers, byMod2) // {0: [2,4,6], 1: [1,3,5]}21// evens := grouped[0] // [2,4,6]22package fp2324// Mapper is a function type that maps an element to another element.25type Mapper func(any) any2627// Predicate is a function type that evaluates a condition on an element.28type Predicate func(any) bool2930// Reducer is a function type that reduces two elements to a single value.31type Reducer func(any, any) any3233// Filter filters elements from the slice that satisfy the given predicate.34//35// Example:36//37// numbers := []any{-1, 0, 1, 2}38// isPositive := func(v any) bool { return v.(int) > 0 }39// result := Filter(numbers, isPositive) // [1, 2]40func Filter(values []any, fn Predicate) []any {41 result := []any{}42 for _, v := range values {43 if fn(v) {44 result = append(result, v)45 }46 }47 return result48}4950// Map applies a function to each element in the slice.51//52// Example:53//54// numbers := []any{1, 2, 3}55// toString := func(v any) any { return fmt.Sprintf("%d", v) }56// result := Map(numbers, toString) // ["1", "2", "3"]57func Map(values []any, fn Mapper) []any {58 result := make([]any, len(values))59 for i, v := range values {60 result[i] = fn(v)61 }62 return result63}6465// Reduce reduces a slice to a single value by applying a function.66//67// Example:68//69// numbers := []any{1, 2, 3, 4}70// sum := func(a, b any) any { return a.(int) + b.(int) }71// result := Reduce(numbers, sum, 0) // 1072func Reduce(values []any, fn Reducer, initial any) any {73 acc := initial74 for _, v := range values {75 acc = fn(acc, v)76 }77 return acc78}7980// FlatMap maps each element to a collection and flattens the results.81//82// Example:83//84// words := []any{"hello", "world"}85// split := func(v any) any {86// chars := []any{}87// for _, c := range v.(string) {88// chars = append(chars, string(c))89// }90// return chars91// }92// result := FlatMap(words, split) // ["h","e","l","l","o","w","o","r","l","d"]93func FlatMap(values []any, fn Mapper) []any {94 result := []any{}95 for _, v := range values {96 inner := fn(v).([]any)97 result = append(result, inner...)98 }99 return result100}101102// All returns true if all elements satisfy the predicate.103//104// Example:105//106// numbers := []any{2, 4, 6, 8}107// isEven := func(v any) bool { return v.(int)%2 == 0 }108// result := All(numbers, isEven) // true109func All(values []any, fn Predicate) bool {110 for _, v := range values {111 if !fn(v) {112 return false113 }114 }115 return true116}117118// Any returns true if at least one element satisfies the predicate.119//120// Example:121//122// numbers := []any{1, 3, 4, 7}123// isEven := func(v any) bool { return v.(int)%2 == 0 }124// result := Any(numbers, isEven) // true (4 is even)125func Any(values []any, fn Predicate) bool {126 for _, v := range values {127 if fn(v) {128 return true129 }130 }131 return false132}133134// None returns true if no elements satisfy the predicate.135//136// Example:137//138// numbers := []any{1, 3, 5, 7}139// isEven := func(v any) bool { return v.(int)%2 == 0 }140// result := None(numbers, isEven) // true (no even numbers)141func None(values []any, fn Predicate) bool {142 for _, v := range values {143 if fn(v) {144 return false145 }146 }147 return true148}149150// Chunk splits a slice into chunks of the given size.151//152// Example:153//154// numbers := []any{1, 2, 3, 4, 5}155// result := Chunk(numbers, 2) // [[1,2], [3,4], [5]]156func Chunk(values []any, size int) [][]any {157 if size <= 0 {158 return nil159 }160 var chunks [][]any161 for i := 0; i < len(values); i += size {162 end := i + size163 if end > len(values) {164 end = len(values)165 }166 chunks = append(chunks, values[i:end])167 }168 return chunks169}170171// Find returns the first element that satisfies the predicate and a boolean indicating if an element was found.172//173// Example:174//175// numbers := []any{1, 2, 3, 4}176// isEven := func(v any) bool { return v.(int)%2 == 0 }177// result, found := Find(numbers, isEven) // 2, true178func Find(values []any, fn Predicate) (any, bool) {179 for _, v := range values {180 if fn(v) {181 return v, true182 }183 }184 return nil, false185}186187// Reverse reverses the order of elements in a slice.188//189// Example:190//191// numbers := []any{1, 2, 3}192// result := Reverse(numbers) // [3, 2, 1]193func Reverse(values []any) []any {194 result := make([]any, len(values))195 for i, v := range values {196 result[len(values)-1-i] = v197 }198 return result199}200201// Zip combines two slices into a slice of pairs. If the slices have different lengths,202// extra elements from the longer slice are ignored.203//204// Example:205//206// a := []any{1, 2, 3}207// b := []any{"a", "b", "c"}208// result := Zip(a, b) // [[1,"a"], [2,"b"], [3,"c"]]209func Zip(a, b []any) [][2]any {210 length := min(len(a), len(b))211 result := make([][2]any, length)212 for i := 0; i < length; i++ {213 result[i] = [2]any{a[i], b[i]}214 }215 return result216}217218// Unzip splits a slice of pairs into two separate slices.219//220// Example:221//222// pairs := [][2]any{{1,"a"}, {2,"b"}, {3,"c"}}223// numbers, letters := Unzip(pairs) // [1,2,3], ["a","b","c"]224func Unzip(pairs [][2]any) ([]any, []any) {225 a := make([]any, len(pairs))226 b := make([]any, len(pairs))227 for i, pair := range pairs {228 a[i] = pair[0]229 b[i] = pair[1]230 }231 return a, b232}233234// GroupBy groups elements based on a key returned by a Mapper.235//236// Example:237//238// numbers := []any{1, 2, 3, 4, 5, 6}239// byMod3 := func(v any) any { return v.(int) % 3 }240// result := GroupBy(numbers, byMod3) // {0: [3,6], 1: [1,4], 2: [2,5]}241func GroupBy(values []any, fn Mapper) map[any][]any {242 result := make(map[any][]any)243 for _, v := range values {244 key := fn(v)245 result[key] = append(result[key], v)246 }247 return result248}249250// Flatten flattens a slice of slices into a single slice.251//252// Example:253//254// nested := [][]any{{1,2}, {3,4}, {5}}255// result := Flatten(nested) // [1,2,3,4,5]256func Flatten(values [][]any) []any {257 result := []any{}258 for _, v := range values {259 result = append(result, v...)260 }261 return result262}263264// Helper functions265func min(a, b int) int {266 if a < b {267 return a268 }269 return b270}271Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.