merge: quick/fase5-first-entries — filter_slice, map_slice y Result[T]
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
package core
|
||||
|
||||
// FilterSlice returns a new slice containing only elements where pred returns true.
|
||||
// Does not mutate the original slice.
|
||||
func FilterSlice[T any](xs []T, pred func(T) bool) []T {
|
||||
result := make([]T, 0, len(xs))
|
||||
for _, x := range xs {
|
||||
if pred(x) {
|
||||
result = append(result, x)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: filter_slice
|
||||
kind: function
|
||||
lang: go
|
||||
domain: core
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func FilterSlice[T any](xs []T, pred func(T) bool) []T"
|
||||
description: "Filtra un slice aplicando un predicado sin mutar el original. Retorna un nuevo slice con los elementos que cumplen la condicion."
|
||||
tags: [slice, functional, generic, filter]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
tested: true
|
||||
tests: ["filtra pares", "slice vacio retorna vacio", "ningun match retorna vacio"]
|
||||
test_file_path: "functions/core/filter_slice_test.go"
|
||||
file_path: "functions/core/filter_slice.go"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
evens := FilterSlice([]int{1, 2, 3, 4, 5}, func(n int) bool { return n%2 == 0 })
|
||||
// evens = [2, 4]
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Funcion pura generica. Crea un nuevo slice con capacidad pre-alocada al tamaño del original para minimizar reallocs.
|
||||
@@ -0,0 +1,28 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFilterSlice(t *testing.T) {
|
||||
t.Run("filtra pares", func(t *testing.T) {
|
||||
got := FilterSlice([]int{1, 2, 3, 4, 5}, func(n int) bool { return n%2 == 0 })
|
||||
if len(got) != 2 || got[0] != 2 || got[1] != 4 {
|
||||
t.Errorf("got %v, want [2 4]", got)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("slice vacio retorna vacio", func(t *testing.T) {
|
||||
got := FilterSlice([]int{}, func(n int) bool { return true })
|
||||
if len(got) != 0 {
|
||||
t.Errorf("got %v, want []", got)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ningun match retorna vacio", func(t *testing.T) {
|
||||
got := FilterSlice([]int{1, 3, 5}, func(n int) bool { return n%2 == 0 })
|
||||
if len(got) != 0 {
|
||||
t.Errorf("got %v, want []", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package core
|
||||
|
||||
// MapSlice applies fn to each element of xs and returns a new slice with the results.
|
||||
// Does not mutate the original slice.
|
||||
func MapSlice[T any, U any](xs []T, fn func(T) U) []U {
|
||||
result := make([]U, len(xs))
|
||||
for i, x := range xs {
|
||||
result[i] = fn(x)
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: map_slice
|
||||
kind: function
|
||||
lang: go
|
||||
domain: core
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func MapSlice[T any, U any](xs []T, fn func(T) U) []U"
|
||||
description: "Transforma cada elemento de un slice aplicando una funcion. Retorna un nuevo slice del mismo tamaño con los resultados."
|
||||
tags: [slice, functional, generic, map, transform]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
tested: true
|
||||
tests: ["transforma enteros a strings", "slice vacio retorna vacio", "preserva orden"]
|
||||
test_file_path: "functions/core/map_slice_test.go"
|
||||
file_path: "functions/core/map_slice.go"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
strs := MapSlice([]int{1, 2, 3}, func(n int) string { return fmt.Sprintf("%d", n) })
|
||||
// strs = ["1", "2", "3"]
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Funcion pura generica con dos type parameters: T (input) y U (output). Pre-aloca el slice resultado al tamaño exacto.
|
||||
@@ -0,0 +1,29 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMapSlice(t *testing.T) {
|
||||
t.Run("transforma enteros a strings", func(t *testing.T) {
|
||||
got := MapSlice([]int{1, 2, 3}, func(n int) string { return fmt.Sprintf("%d", n) })
|
||||
if len(got) != 3 || got[0] != "1" || got[1] != "2" || got[2] != "3" {
|
||||
t.Errorf("got %v", got)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("slice vacio retorna vacio", func(t *testing.T) {
|
||||
got := MapSlice([]int{}, func(n int) int { return n * 2 })
|
||||
if len(got) != 0 {
|
||||
t.Errorf("got %v, want []", got)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("preserva orden", func(t *testing.T) {
|
||||
got := MapSlice([]int{3, 1, 2}, func(n int) int { return n * 10 })
|
||||
if got[0] != 30 || got[1] != 10 || got[2] != 20 {
|
||||
t.Errorf("got %v, want [30 10 20]", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package core
|
||||
|
||||
// Result is a sum type representing either a successful value or an error.
|
||||
// Exactly one of Ok or Err is non-nil at any time.
|
||||
type Result[T any] struct {
|
||||
ok *T
|
||||
err error
|
||||
}
|
||||
|
||||
// Ok creates a successful Result.
|
||||
func Ok[T any](v T) Result[T] {
|
||||
return Result[T]{ok: &v}
|
||||
}
|
||||
|
||||
// Err creates a failed Result.
|
||||
func Err[T any](err error) Result[T] {
|
||||
return Result[T]{err: err}
|
||||
}
|
||||
|
||||
// IsOk returns true if the Result contains a value.
|
||||
func (r Result[T]) IsOk() bool {
|
||||
return r.ok != nil
|
||||
}
|
||||
|
||||
// IsErr returns true if the Result contains an error.
|
||||
func (r Result[T]) IsErr() bool {
|
||||
return r.err != nil
|
||||
}
|
||||
|
||||
// Unwrap returns the value or panics if the Result is an error.
|
||||
func (r Result[T]) Unwrap() T {
|
||||
if r.ok == nil {
|
||||
panic("called Unwrap on an Err Result")
|
||||
}
|
||||
return *r.ok
|
||||
}
|
||||
|
||||
// UnwrapErr returns the error or panics if the Result is Ok.
|
||||
func (r Result[T]) UnwrapErr() error {
|
||||
if r.err == nil {
|
||||
panic("called UnwrapErr on an Ok Result")
|
||||
}
|
||||
return r.err
|
||||
}
|
||||
|
||||
// UnwrapOr returns the value or a default if the Result is an error.
|
||||
func (r Result[T]) UnwrapOr(def T) T {
|
||||
if r.ok != nil {
|
||||
return *r.ok
|
||||
}
|
||||
return def
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: result
|
||||
lang: go
|
||||
domain: core
|
||||
version: "1.0.0"
|
||||
algebraic: sum
|
||||
definition: |
|
||||
type Result[T any] struct {
|
||||
ok *T
|
||||
err error
|
||||
}
|
||||
description: "Tipo suma generico que representa exito (Ok) o fallo (Err). Permite componer operaciones que pueden fallar sin recurrir a multiples returns (T, error)."
|
||||
tags: [result, sum, error-handling, functional, generic]
|
||||
uses_types: []
|
||||
file_path: "types/core/result.go"
|
||||
---
|
||||
|
||||
## Notas
|
||||
|
||||
Tipo suma con dos variantes: Ok(T) y Err(error). Inspirado en Result de Rust.
|
||||
Util para encadenar operaciones y evitar el patron `if err != nil` repetitivo.
|
||||
@@ -0,0 +1,53 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestResultOk(t *testing.T) {
|
||||
r := Ok(42)
|
||||
if !r.IsOk() {
|
||||
t.Error("expected IsOk")
|
||||
}
|
||||
if r.IsErr() {
|
||||
t.Error("expected not IsErr")
|
||||
}
|
||||
if r.Unwrap() != 42 {
|
||||
t.Errorf("got %d, want 42", r.Unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
func TestResultErr(t *testing.T) {
|
||||
r := Err[int](errors.New("fail"))
|
||||
if r.IsOk() {
|
||||
t.Error("expected not IsOk")
|
||||
}
|
||||
if !r.IsErr() {
|
||||
t.Error("expected IsErr")
|
||||
}
|
||||
if r.UnwrapErr().Error() != "fail" {
|
||||
t.Errorf("got %v", r.UnwrapErr())
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnwrapOr(t *testing.T) {
|
||||
ok := Ok(10)
|
||||
if ok.UnwrapOr(0) != 10 {
|
||||
t.Error("UnwrapOr on Ok should return value")
|
||||
}
|
||||
|
||||
err := Err[int](errors.New("fail"))
|
||||
if err.UnwrapOr(99) != 99 {
|
||||
t.Error("UnwrapOr on Err should return default")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnwrapPanics(t *testing.T) {
|
||||
defer func() {
|
||||
if recover() == nil {
|
||||
t.Error("Unwrap on Err should panic")
|
||||
}
|
||||
}()
|
||||
Err[int](errors.New("fail")).Unwrap()
|
||||
}
|
||||
Reference in New Issue
Block a user