fn: add uncons, unsnoc and its component projections

This commit is contained in:
Keagan McClelland 2024-09-24 16:15:46 +09:00
parent 9f0cc159ea
commit 4e1579cf4c
No known key found for this signature in database
GPG Key ID: FA7E65C951F12439
2 changed files with 118 additions and 0 deletions

View File

@ -258,3 +258,63 @@ func ForEachConc[A, B any](f func(A) B,
return bs
}
// Head returns the first element of the slice, assuming it is non-empty.
func Head[A any](items []A) Option[A] {
if len(items) == 0 {
return None[A]()
}
return Some(items[0])
}
// Tail returns the slice without the first element, assuming the slice is not
// empty. Note this makes a copy of the slice.
func Tail[A any](items []A) Option[[]A] {
if len(items) == 0 {
return None[[]A]()
}
res := make([]A, len(items)-1)
copy(res, items[1:])
return Some(res)
}
// Init returns the slice without the last element, assuming the slice is not
// empty. Note this makes a copy of the slice.
func Init[A any](items []A) Option[[]A] {
if len(items) == 0 {
return None[[]A]()
}
res := make([]A, len(items)-1)
copy(res, items[0:len(items)-1])
return Some(res)
}
// Last returns the last element of the slice, assuming it is non-empty.
func Last[A any](items []A) Option[A] {
if len(items) == 0 {
return None[A]()
}
return Some(items[len(items)-1])
}
// Uncons splits a slice into a pair of its Head and Tail.
func Uncons[A any](items []A) Option[T2[A, []A]] {
return LiftA2Option(NewT2[A, []A])(Head(items), Tail(items))
}
// Unsnoc splits a slice into a pair of its Init and Last.
func Unsnoc[A any](items []A) Option[T2[[]A, A]] {
return LiftA2Option(NewT2[[]A, A])(Init(items), Last(items))
}
// Len is the len function that is defined in a way that makes it usable in
// higher-order contexts.
func Len[A any](items []A) uint {
return uint(len(items))
}

View File

@ -374,3 +374,61 @@ func TestPropFindIdxFindIdentity(t *testing.T) {
t.Fatal(err)
}
}
func TestPropLastTailIsLast(t *testing.T) {
f := func(s []uint8) bool {
// We exclude the singleton case because the Tail is empty.
if len(s) <= 1 {
return true
}
return Last(s) == ChainOption(Last[uint8])(Tail(s))
}
require.NoError(t, quick.Check(f, nil))
}
func TestPropHeadInitIsHead(t *testing.T) {
f := func(s []uint8) bool {
// We exclude the singleton case because the Init is empty.
if len(s) <= 1 {
return true
}
return Head(s) == ChainOption(Head[uint8])(Init(s))
}
require.NoError(t, quick.Check(f, nil))
}
func TestPropTailDecrementsLength(t *testing.T) {
f := func(s []uint8) bool {
if len(s) == 0 {
return true
}
return Some(Len(s)-1) == MapOption(Len[uint8])(Tail(s))
}
require.NoError(t, quick.Check(f, nil))
}
func TestPropInitDecrementsLength(t *testing.T) {
f := func(s []uint8) bool {
if len(s) == 0 {
return true
}
return Some(Len(s)-1) == MapOption(Len[uint8])(Init(s))
}
require.NoError(t, quick.Check(f, nil))
}
func TestSingletonTailIsEmpty(t *testing.T) {
require.Equal(t, Tail([]int{1}), Some([]int{}))
}
func TestSingletonInitIsEmpty(t *testing.T) {
require.Equal(t, Init([]int{1}), Some([]int{}))
}