2025-11-06 09:27:00 +01:00
// Copyright (c) 2023 - 2025 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package nonempty
import (
G "github.com/IBM/fp-go/v2/array/generic"
EM "github.com/IBM/fp-go/v2/endomorphism"
"github.com/IBM/fp-go/v2/internal/array"
2025-12-16 14:03:01 +01:00
"github.com/IBM/fp-go/v2/option"
2025-11-06 09:27:00 +01:00
S "github.com/IBM/fp-go/v2/semigroup"
)
// Of constructs a single element array
func Of [ A any ] ( first A ) NonEmptyArray [ A ] {
return G . Of [ NonEmptyArray [ A ] ] ( first )
}
// From constructs a [NonEmptyArray] from a set of variadic arguments
func From [ A any ] ( first A , data ... A ) NonEmptyArray [ A ] {
count := len ( data )
if count == 0 {
return Of ( first )
}
// allocate the requested buffer
buffer := make ( NonEmptyArray [ A ] , count + 1 )
buffer [ 0 ] = first
copy ( buffer [ 1 : ] , data )
return buffer
}
2025-12-16 14:03:01 +01:00
//go:inline
2025-11-06 09:27:00 +01:00
func IsEmpty [ A any ] ( _ NonEmptyArray [ A ] ) bool {
return false
}
2025-12-16 14:03:01 +01:00
//go:inline
2025-11-06 09:27:00 +01:00
func IsNonEmpty [ A any ] ( _ NonEmptyArray [ A ] ) bool {
return true
}
2025-12-16 14:03:01 +01:00
//go:inline
2025-11-06 09:27:00 +01:00
func MonadMap [ A , B any ] ( as NonEmptyArray [ A ] , f func ( a A ) B ) NonEmptyArray [ B ] {
return G . MonadMap [ NonEmptyArray [ A ] , NonEmptyArray [ B ] ] ( as , f )
}
2025-12-16 14:03:01 +01:00
//go:inline
func Map [ A , B any ] ( f func ( a A ) B ) Operator [ A , B ] {
return G . Map [ NonEmptyArray [ A ] , NonEmptyArray [ B ] ] ( f )
2025-11-06 09:27:00 +01:00
}
func Reduce [ A , B any ] ( f func ( B , A ) B , initial B ) func ( NonEmptyArray [ A ] ) B {
return func ( as NonEmptyArray [ A ] ) B {
return array . Reduce ( as , f , initial )
}
}
func ReduceRight [ A , B any ] ( f func ( A , B ) B , initial B ) func ( NonEmptyArray [ A ] ) B {
return func ( as NonEmptyArray [ A ] ) B {
return array . ReduceRight ( as , f , initial )
}
}
2025-12-16 14:03:01 +01:00
//go:inline
2025-11-06 09:27:00 +01:00
func Tail [ A any ] ( as NonEmptyArray [ A ] ) [ ] A {
return as [ 1 : ]
}
2025-12-16 14:03:01 +01:00
//go:inline
2025-11-06 09:27:00 +01:00
func Head [ A any ] ( as NonEmptyArray [ A ] ) A {
return as [ 0 ]
}
2025-12-16 14:03:01 +01:00
//go:inline
2025-11-06 09:27:00 +01:00
func First [ A any ] ( as NonEmptyArray [ A ] ) A {
return as [ 0 ]
}
2025-12-16 14:03:01 +01:00
//go:inline
2025-11-06 09:27:00 +01:00
func Last [ A any ] ( as NonEmptyArray [ A ] ) A {
return as [ len ( as ) - 1 ]
}
2025-12-16 14:03:01 +01:00
//go:inline
2025-11-06 09:27:00 +01:00
func Size [ A any ] ( as NonEmptyArray [ A ] ) int {
return G . Size ( as )
}
func Flatten [ A any ] ( mma NonEmptyArray [ NonEmptyArray [ A ] ] ) NonEmptyArray [ A ] {
return G . Flatten ( mma )
}
2025-12-16 14:03:01 +01:00
func MonadChain [ A , B any ] ( fa NonEmptyArray [ A ] , f Kleisli [ A , B ] ) NonEmptyArray [ B ] {
2025-11-11 11:01:49 +01:00
return G . MonadChain ( fa , f )
2025-11-06 09:27:00 +01:00
}
2025-12-16 14:03:01 +01:00
func Chain [ A , B any ] ( f func ( A ) NonEmptyArray [ B ] ) Operator [ A , B ] {
2025-11-11 11:01:49 +01:00
return G . Chain [ NonEmptyArray [ A ] ] ( f )
2025-11-06 09:27:00 +01:00
}
func MonadAp [ B , A any ] ( fab NonEmptyArray [ func ( A ) B ] , fa NonEmptyArray [ A ] ) NonEmptyArray [ B ] {
return G . MonadAp [ NonEmptyArray [ B ] ] ( fab , fa )
}
func Ap [ B , A any ] ( fa NonEmptyArray [ A ] ) func ( NonEmptyArray [ func ( A ) B ] ) NonEmptyArray [ B ] {
return G . Ap [ NonEmptyArray [ B ] , NonEmptyArray [ func ( A ) B ] ] ( fa )
}
// FoldMap maps and folds a [NonEmptyArray]. Map the [NonEmptyArray] passing each value to the iterating function. Then fold the results using the provided [Semigroup].
func FoldMap [ A , B any ] ( s S . Semigroup [ B ] ) func ( func ( A ) B ) func ( NonEmptyArray [ A ] ) B {
return func ( f func ( A ) B ) func ( NonEmptyArray [ A ] ) B {
return func ( as NonEmptyArray [ A ] ) B {
return array . Reduce ( Tail ( as ) , func ( cur B , a A ) B {
return s . Concat ( cur , f ( a ) )
} , f ( Head ( as ) ) )
}
}
}
// Fold folds the [NonEmptyArray] using the provided [Semigroup].
func Fold [ A any ] ( s S . Semigroup [ A ] ) func ( NonEmptyArray [ A ] ) A {
return func ( as NonEmptyArray [ A ] ) A {
return array . Reduce ( Tail ( as ) , s . Concat , Head ( as ) )
}
}
// Prepend prepends a single value to an array
func Prepend [ A any ] ( head A ) EM . Endomorphism [ NonEmptyArray [ A ] ] {
return array . Prepend [ EM . Endomorphism [ NonEmptyArray [ A ] ] ] ( head )
}
2025-12-16 14:03:01 +01:00
// ToNonEmptyArray attempts to convert a regular slice into a NonEmptyArray.
// This function provides a safe way to create a NonEmptyArray from a slice that might be empty,
// returning an Option type to handle the case where the input slice is empty.
//
// Type Parameters:
// - A: The element type of the array
//
// Parameters:
// - as: A regular slice that may or may not be empty
//
// Returns:
// - Option[NonEmptyArray[A]]: Some(NonEmptyArray) if the input slice is non-empty, None if empty
//
// Behavior:
// - If the input slice is empty, returns None
// - If the input slice has at least one element, wraps it in Some and returns it as a NonEmptyArray
// - The conversion is a type cast, so no data is copied
//
// Example:
//
// // Convert non-empty slice
// numbers := []int{1, 2, 3}
// result := ToNonEmptyArray(numbers) // Some(NonEmptyArray[1, 2, 3])
//
// // Convert empty slice
// empty := []int{}
// result := ToNonEmptyArray(empty) // None
//
// // Use with Option methods
// numbers := []int{1, 2, 3}
// result := ToNonEmptyArray(numbers)
// if O.IsSome(result) {
// nea := O.GetOrElse(F.Constant(From(0)))(result)
// head := Head(nea) // 1
// }
//
// Use cases:
// - Safely converting user input or external data to NonEmptyArray
// - Validating that a collection has at least one element before processing
// - Converting results from functions that return regular slices
// - Ensuring type safety when working with collections that must not be empty
//
// Example with validation:
//
// func processItems(items []string) Option[string] {
// return F.Pipe2(
// items,
// ToNonEmptyArray[string],
// O.Map(func(nea NonEmptyArray[string]) string {
// return Head(nea) // Safe to get head since we know it's non-empty
// }),
// )
// }
//
// Example with error handling:
//
// items := []int{1, 2, 3}
// result := ToNonEmptyArray(items)
// switch {
// case O.IsSome(result):
// nea := O.GetOrElse(F.Constant(From(0)))(result)
// fmt.Println("First item:", Head(nea))
// case O.IsNone(result):
// fmt.Println("Array is empty")
// }
//
// Example with chaining:
//
// // Process only if non-empty
// result := F.Pipe3(
// []int{1, 2, 3},
// ToNonEmptyArray[int],
// O.Map(Map(func(x int) int { return x * 2 })),
// O.Map(Head[int]),
// ) // Some(2)
//
// Note: This function is particularly useful when working with APIs or functions
// that return regular slices but you need the type-level guarantee that the
// collection is non-empty for subsequent operations.
func ToNonEmptyArray [ A any ] ( as [ ] A ) Option [ NonEmptyArray [ A ] ] {
if G . IsEmpty ( as ) {
return option . None [ NonEmptyArray [ A ] ] ( )
}
return option . Some ( NonEmptyArray [ A ] ( as ) )
}