Skip to content

Commit

Permalink
remove Head, Tail, and Uncons
Browse files Browse the repository at this point in the history
Those functions would leak a pull-based iterator in some cases;
and even if the bug in question were fixed,
those functions couldn't work with single-use iterators;
see golang/go#61898 (comment) and
follow-up comments.
  • Loading branch information
jub0bs committed Sep 21, 2024
1 parent 498cb48 commit 1f659c1
Show file tree
Hide file tree
Showing 2 changed files with 0 additions and 191 deletions.
56 changes: 0 additions & 56 deletions seq.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,62 +42,6 @@ func Cons[E any](e E, seq iter.Seq[E]) iter.Seq[E] {
}
}

// Head, if seq is non-empty, returns the head of seq and true;
// otherwise, it returns the zero value and false.
func Head[E any](seq iter.Seq[E]) (E, bool) {
for e := range seq {
return e, true
}
var zero E
return zero, false
}

// Tail, if seq is non-empty, returns an iterator composed of
// all the elements of seq after the latter's head and true;
// otherwise, it returns nil and false.
func Tail[E any](seq iter.Seq[E]) (iter.Seq[E], bool) {
next, stop := iter.Pull(seq)
if _, ok := next(); !ok {
return nil, false
}
f := func(yield func(E) bool) {
defer stop()
for {
e, ok := next()
if !ok {
return
}
if !yield(e) {
return
}
}
}
return f, true
}

// Uncons, if seq is non-empty, returns the head and tail of seq and true;
// otherwise, it returns the zero value, nil, and false.
func Uncons[E any](seq iter.Seq[E]) (E, iter.Seq[E], bool) {
next, stop := iter.Pull(seq)
head, ok := next()
if !ok {
return head, nil, false
}
tail := func(yield func(E) bool) {
defer stop()
for {
e, ok := next()
if !ok {
return
}
if !yield(e) {
return
}
}
}
return head, tail, true
}

// Append returns an iterator resulting from the concatenation of seq1 and
// seq2.
func Append[E any](seq1, seq2 iter.Seq[E]) iter.Seq[E] {
Expand Down
135 changes: 0 additions & 135 deletions seq_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,141 +113,6 @@ func TestCons(t *testing.T) {
}
}

func ExampleHead() {
seq := slices.Values([]int{})
fmt.Println(iterutil.Head(seq))
seq = slices.Values([]int{1, 2, 3, 4})
fmt.Println(iterutil.Head(seq))
// Output:
// 0 false
// 1 true
}

func ExampleTail() {
seq := slices.Values([]int{})
tail, ok := iterutil.Tail(seq)
if ok {
fmt.Println(slices.Collect(tail))
}
seq = slices.Values([]int{1, 2, 3, 4})
tail, ok = iterutil.Tail(seq)
if ok {
fmt.Println(slices.Collect(tail))
}
// Output: [2 3 4]
}

func TestTail(t *testing.T) {
cases := []struct {
desc string
elems []string
breakWhen func(string) bool
want []string
ok bool
}{
{
desc: "empty",
elems: []string{},
breakWhen: alwaysFalse[string],
}, {
desc: "no break",
elems: []string{"one", "two", "three"},
breakWhen: alwaysFalse[string],
want: []string{"two", "three"},
ok: true,
}, {
desc: "break early",
elems: []string{"one", "two", "three"},
breakWhen: equal("three"),
want: []string{"two"},
ok: true,
},
}
for _, tc := range cases {
f := func(t *testing.T) {
seq := slices.Values(tc.elems)
got, ok := iterutil.Tail(seq)
if ok != tc.ok {
t.Fatalf("got %t; want %t", ok, tc.ok)
}
if !tc.ok && got != nil {
t.Fatal("got non-nil iter.Seq[string]; want nil iter.Seq[string]")
}
if !ok {
return
}
assertEqual(t, got, tc.want, tc.breakWhen)

}
t.Run(tc.desc, f)
}
}

func ExampleUncons() {
seq := slices.Values([]int{})
head, tail, ok := iterutil.Uncons(seq)
if ok {
fmt.Println(head, slices.Collect(tail))
}
seq = slices.Values([]int{1, 2, 3, 4})
head, tail, ok = iterutil.Uncons(seq)
if ok {
fmt.Println(head, slices.Collect(tail))
}
// Output: 1 [2 3 4]
}

func TestUncons(t *testing.T) {
cases := []struct {
desc string
elems []string
breakWhen func(string) bool
wantHead string
wantTail []string
ok bool
}{
{
desc: "empty",
elems: []string{},
breakWhen: alwaysFalse[string],
}, {
desc: "no break",
elems: []string{"one", "two", "three"},
breakWhen: alwaysFalse[string],
wantHead: "one",
wantTail: []string{"two", "three"},
ok: true,
}, {
desc: "break early",
elems: []string{"one", "two", "three"},
breakWhen: equal("three"),
wantHead: "one",
wantTail: []string{"two"},
ok: true,
},
}
for _, tc := range cases {
f := func(t *testing.T) {
seq := slices.Values(tc.elems)
head, tail, ok := iterutil.Uncons(seq)
if ok != tc.ok {
t.Fatalf("got %t; want %t", ok, tc.ok)
}
if !tc.ok && tail != nil {
t.Fatal("got non-nil iter.Seq[string]; want nil iter.Seq[string]")
}
if !ok {
return
}
if head != tc.wantHead {
t.Errorf("got %s; want %s", head, tc.wantHead)
}
assertEqual(t, tail, tc.wantTail, tc.breakWhen)
}
t.Run(tc.desc, f)
}
}

func ExampleAppend() {
seq1 := slices.Values([]string{"foo", "bar"})
seq2 := slices.Values([]string{"baz", "qux"})
Expand Down

0 comments on commit 1f659c1

Please sign in to comment.