Giter Club home page Giter Club logo

go-functional's People

Contributors

booleancat avatar dependabot[bot] avatar dranikpg avatar euanwm avatar mrchocha avatar mse99 avatar myusko avatar rajan-226 avatar seiyab avatar william-young-97 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

go-functional's Issues

[Feature ๐Ÿ”จ]: Generator

Is your feature request related to a problem? Please describe.

No, just an idea.
It should be convenient because this pattern can be seen in other languages.
examples: Python, JavaScript, Kotlin

Describe the solution you'd like

Creating iterator from imperative function.

Provide code snippets to show how this new feature might be used.

func ExampleGenerator() {
  it := iter.Generator[string](func(yield func(v string)) {
    yield("hello")
    yield("generator")
  })
  fmt.Println(iter.Collect(it))
  // Output: [hello generator]
}

Does this incur a breaking change?

no.

Do you intend to build this feature yourself?

Maybe.
Feel free to comment if someone intend to commit.

Additional context
I'm not sure whether it is good feature or not.
It's because following reasons.

  • It is imperative rather than functional.
  • The functionality can be nearly achieved by #29 .

Justifications exist, but weak.

  • It bridges gaps between imperative and functional.
  • It is a bit clear and readable.

[Feature ๐Ÿ”จ]: Iterators should not repeatedly call their underlying iterators when exhausted

Is your feature request related to a problem? Please describe.

Iterators often call underlying iterators (such as Map or Filter). Sometimes an iterator can become exhausted before its underlying iterator has been exhausted (e.g. Take). Currently there is nothing defined in documentation or dictated by tests that prevents iterators from consuming from inner iterators, which will cause unnecessary runtime overhead and possibly surprising users if they re-use the inner iterators later.

Describe the solution you'd like

  1. Introduce a new nomenclature for discussing iterators in documentation in order to make talking about them easier. a "primary" iterator is an original producer of values, it does not depend on underlying iterators (e.g. Count). A "second-order" iterator is one which depends on another iterator (e.g. Take).
  2. Documentation should be clear for each iterator whether it is "primary" or "second-order".
  3. Documentation should be clear that "second-order" iterators that are exhausted should *cease consumption" of their underlying iterators on subsequent calls to Next.
  4. All existing "second-order" iterators in the iter package should have tests that guarantee this behaviour.

Provide code snippets to show how this new feature might be used.

N/A documentation change.

Does this incur a breaking change?

No.

Do you intend to build this feature yourself?

Maybe - I'd prefer to give someone else the opportunity to contribute.

Additional context

PRs that implement part of this are OK, this issue can be closed one all 4 points are addressed.

Hint - I've thought about using counterfeiter to generate a fake iterator in order to make call count testing easier. If you use this, please place a fake iterator inside the internal package of this project.

Alternatively, I'm open to a conversation about the failure of a fake iterator as something iter exposes to make testing easier but that should probably be a separate issue.

[Feature ๐Ÿ”จ]: I can use a template when raising a pull request

Is your feature request related to a problem? Please describe.

Contributors are doing a kindness by raising pull requests their job could be made easier by offering a pull request template to reduce the likelihood that their PR would be rejected and reduce the back-and-forth on the PR.

Describe the solution you'd like

A pull request template that contains a set of standard questions could act as a forcing function to ensure PRs are raised with as much context as is needed. The template should:

  1. Ask for a linked issue and encourage conversation on the issue (the PR should be only about the implementation).
  2. Ensure that the contributor has read the contributing guide.

Provide code snippets to show how this new feature might be used.

Not applicable.

Does this incur a breaking change?

No.

Do you intend to build this feature yourself?

Yes.

Additional context

N/A.

[Feature ๐Ÿ”จ]: Offer commutative built in operators in the "ops" package

Is your feature request related to a problem? Please describe.

I can imagine using the built in operators (see this list) in iterators like Map or collectors like Fold as a common use case. Some operators are commutative (the order of argument does not matter) whilst others are not. I'm not yet convinced of the value of providing non-commutative operators so am considering those out of scope for this feature request.

Currently users of Map-like iterators or Fold-like collectors would have to define these themselves.

Describe the solution you'd like

Offer the following operators to users of go-functional via the ops package:

  • Sum (+)
  • Product (*)
  • BitwiseAnd (&)
  • BitwiseOr (|)
  • BitwiseXor (^)

Provide code snippets to show how this new feature might be used.

func ExampleSum() {
  fmt.Println(iter.Fold[string](iter.Lift([]{"hello ", "there"}), "", ops.Sum[string]))
  // Output: hello there
}
func ExampleProduct() {
  fmt.Println(iter.Fold[int](iter.Lift([]{1, 2, 3, 4}), 1, ops.Product[int]))
  // Output: 24
}
func ExampleBitwiseAnd() {
  fmt.Println(iter.Fold[int](iter.Lift([]{1, 3}), 1, ops.BitwiseAnd[int]))
  // Output: 1
}
func ExampleBitwiseOr() {
  fmt.Println(iter.Fold[int](iter.Lift([]{1, 3}), 0, ops.BitwiseOr[int]))
  // Output: 3
}
func ExampleBitwiseXor() {
  fmt.Println(iter.Fold[int](iter.Lift([]{1, 3}), 0, ops.BitwiseXor[int]))
  // Output: 2
}

Does this incur a breaking change?

Yes.

Do you intend to build this feature yourself?

No - I'd like to offer this out to folks who wish to practice Go or open-source contribution.

Additional context

Within the ops package there is already a Add operation. This should be renamed to Sum and incurs a breaking change (which is fine since we're not at version 1.0.0 yet.

[Feature ๐Ÿ”จ]: Support `Fold`ing of iterators via methods

Is your feature request related to a problem? Please describe.

Folding an iterator is achieved by using the iter package's Fold function. This can lead to deeply nested code that can be difficult to read. The issue belongs to a series of issues that remedy that problem. See #55 for more details.

Describe the solution you'd like

Each iterator (except Count and Repeat because Fold will never terminate) should allow Folding via a method rather than the function iter.Fold.

For example, this test:

func ExampleFold() {
	sum := iter.Fold[int](iter.Take[int](iter.Count(), 4), 0, ops.Add[int])

	fmt.Println(sum)
	// Output: 6
}

Could instead read like:

func ExampleFold() {
	sum := iter.Take[int](iter.Count(), 4).Fold(0, ops.Add[int])

	fmt.Println(sum)
	// Output: 6
}

Does this incur a breaking change?

No.

Do you intend to build this feature yourself?

No - I'd like to offer this to first-time contributors ideally.

Checklist

  • Implement .Fold(initial, biop) on each iterator except Count and Repeat
  • Add a simple test for this (don't test extensively, Fold is already comprehensively tested elsewhere).
  • Update any Example... tests or README references to Fold to use the method.

See #57 for an example of where this was implemented for Collect.

[Feature ๐Ÿ”จ]: Add `iter.Find`

Is your feature request related to a problem? Please describe.

Add function Find to iter package.

Describe the solution you'd like

iter package should have a Find function that takes an iterator and a predicate, Find will
repeatedly call the underlying iterator and test the returned values using the predicate, if the
predicate returns true then the value is returned wrapped in a Some variant if the underlying
iterator is exhausted option.None is returned.

Provide code snippets to show how this new feature might be used.

package main

import "github.com/BooleanCat/go-functional/iter"

func main() {
  isFour := func (x int) bool { return x == 4 }
  isHundred = func (x int) bool { return x == 100 }

  numbers := []int{1, 2, 3, 4, 5}
  
  fmt.Println(iter.Find(iter.Lift(numbers)), isFour) // Some(4)
  fmt.Println(iter.Find(iter.Lift(numbers)), isHundred) // None
}

Does this incur a breaking change?

No

Do you intend to build this feature yourself?

Yes

[Feature ๐Ÿ”จ]: Release version 1.0.0

Is your feature request related to a problem? Please describe.

I'd like to release version 1.0.0 to indicate that the project is ready for use and I'll continue supporting it.

I'm concerned that people may choose not to use the project because of it's 0. status. I'm feeling confident in the usage of the package and the iter interface as it is now.

Describe the solution you'd like

Version 1.0.0 released or some good reasons why it's not a good idea.

Provide code snippets to show how this new feature might be used.

N/A.

Does this incur a breaking change?

No.

Do you intend to build this feature yourself?

N/A.

[Feature ๐Ÿ”จ]: Limited iterator chaining support

Is your feature request related to a problem? Please describe.

Chaining different iterators is not currently possible due to limitations around Go's type system (new generic types cannot be introduced on type methods, they must be declared on the type).

This can cause the chaining of operations to be verbose:

iter.Take(iter.Filter(iter.Count(), isEven), 5)

Describe the solution you'd like

iter.Count().Filter(isEven).Take(5)

This isn't possible for all types because it's not possible to know the output type at Iterator type declaration: for example MapIter can return any type on Next, but Filter needs no additional type information.

Does this incur a breaking change?

No.

Do you intend to build this feature yourself?

Parts of it, maybe.

Additional context

From a brief scan, the iterators than could be chained are Filter, Exclude, Take, Drop and Chain.

In addition the collection methods could be chained also: Collect, ForEach, Fold, Find.

Primary iterators cannot be chained (e.g. Count) because that's nonsensical, and Map and Zip are excluded because of type system limitations.

As such this chaining should be thought of as a feature of each iterator individually rather than a general feature of iterators.

Work list

  • Add .Collect() to all iterators except Count and Repeat (#57)
  • Add .ForEach(op) to all iterators except Count (#68)
  • Add .Find(filter) to all iterators (#69)
  • Add .ToChannel() to all iterators except Count and Repeat
  • Add .Chain(iter) to all iterators except Count and Repeat
  • Add .Drop(n) to all iterators (#61)
  • Add .Filter(filter) to all iterators (#59)
  • Add .Exclude(filter) to all iterators (#60)
  • Add .Take(n) to all iterators (#67)

Iterator chains should not call their underlying iterators when exhausted

For iterators that call other iterators (such as iter.Take), there should be a guarantee that underlying iterators are never call again once a parent iterator becomes exhausted. This may already be the case in some or all iterators but this would be by chance - it certainly isn't tested.

To satisfy this issue, write tests to guarantee this behaviour and document that iterator authors should do likewise in the README.

[Feature ๐Ÿ”จ]: Add a Chain iterator

Is your feature request related to a problem? Please describe.

Currently, in order to "Chain" two iterators, users of go-functional must do something like appending slices together and then lifting. It should be possible to chain a variable number of iterators.

Describe the solution you'd like

A new iterator iter.Chain(iter ...Iterator[T]) will take a variable number of iterators. For each iterator, from left to right, it shall yield all values to exhaustion and move to the next iterator. The chain iterator is exhausted once all iterators are exhausted. Chain may be passed no arguments, in which case it behaves like iter.Exhausted.

Provide code snippets to show how this new feature might be used.

func ExampleChain() {
  fmt.Println(iter.Collect[int](iter.Chain[int](iter.Lift([]int{1, 2}), iter.Lift([]int{3, 4}), iter.Lift([]int{0, 9}))))
  // Output: [1 2 3 4 0 9]
}

Does this incur a breaking change?

No.

Do you intend to build this feature yourself?

No.

Additional context
Add any other context or screenshots about the feature request here.

[Feature ๐Ÿ”จ]: String representations for all iterators

Is your feature request related to a problem? Please describe.

Printing as a string today (0.17.0) looks ugly:

counter := iter.Count()
fmt.Println(counter)
fmt.Printf("%s\n", counter)
// &{{%!s(*iter.CountIter=&{{0xc0000a6000} 0})} %!s(int=0)}

This makes it hard to debug issues when you need to print iterators.

Describe the solution you'd like

Implement the stringer interface for each iterator. The text should make it clear that it's an iterator and the kind of iterator that it is. Perhaps something like:

fmt.Printf("%s\n", iter.Count())  // Iterator<Count>
fmt.Printf("%s\n", iter.Count().Take(4)) // Iterator<Take>

I'm open to better ideas about how it should be represented.

Provide code snippets to show how this new feature might be used.

The fmt.Stringer() interface should be implemented for each iterator.

Does this incur a breaking change?

No.

Do you intend to build this feature yourself?

Maybe.

Additional context

Doesn't all need to be in one PR, I'd accept a PR for 1+ iterators. This issue is closed when all are implemented.

Add Fold functionality

Fold is often used in functional paradigms for collect items from an iterator and apply a transformation during collection. As an example, imagine we wanted to find the sum of all numbers from 1 to 50, one way to achieve this would be to Fold over the iterator with a sum method:

add := func(a, b int) int { return a + b }
total := iter.Fold[int](iter.Take[int](iter.Count(), 51), add, 0)

Fold takes a value from the iterator and applies it to the provided function along with the result of the previous application, using a provided seed value for the first application. In the example above, 0 (the seed value) and 0 (the first value from Count) are passed to add which returns 0. The result (0) is then passed again along with 1 (the next value in the iterator) until the iterator is exhausted.

I can imagine Add being ubiquitous enough that it's worth adding to the ops package and making the arguments a and b any type for which + is a valid operator.

[Feature ๐Ÿ”จ]: CollectResult convenience method on LinesIter

LinesIter yields result.Result[[]byte]. There's a collection function that collect such an iterator into result.Result[[][]byte]. To make this easier to use, add .CollectResults as a method to the Iterator.

iter.Lines(file).CollectResult().Unwrap()

Does this incur a breaking change?

No.

Do you intend to build this feature yourself?

Maybe.

Additional context

I'd like the same for LinesString, however lines string is simply a MapIter. In order to achieve this you'll first need to make LinesString its own iterator.

[Feature ๐Ÿ”จ]: Support `Filter`ing of iterators via methods

Is your feature request related to a problem? Please describe.

Filtering an iterator is achieved by using the iter package's Filter function. This can lead to deeply nested code that can be difficult to read. The issue belongs to a series of issues that remedy that problem. See #55 for more details.

Describe the solution you'd like

Each iterator should allow Filtering via a method rather than the function iter.Filter.

For example, this test:

func ExampleFilter() {
	filtered := iter.Filter[int](iter.Lift([]int{0, 1, 0, 2}), filters.IsZero[int]).Collect()
	fmt.Println(filtered)
	// Output: [0 0]
}

Could instead read like:

func ExampleFilter() {
	filtered := iter.Lift([]int{0, 1, 0, 2}).Filter(filters.IsZero[int]).Collect()
	fmt.Println(filtered)
	// Output: [0 0]
}

Does this incur a breaking change?

No.

Do you intend to build this feature yourself?

No - I'd like to offer this to first-time contributors ideally.

Checklist

  • Implement .FIlter(filter) on each iterator
  • Add a simple test for this (don't test extensively, Filter is already comprehensively tested elsewhere).
  • Update any Example... tests or README references to Filter to use the method.

See #57 for an example of where this was implemented for Collect.

[Feature ๐Ÿ”จ]: Partial JSON support for result.Result

Is your feature request related to a problem? Please describe.

It's not clear to me how a a result should be marshalled into JSON when it holds an error value (since error is represented by an interface). Type information regarding the error would be lost.

However, it seems reasonable to me to support Unmarshal of plain values into result.Result[T] since they will always be unmarshalled into result.Ok variants.

Describe the solution you'd like

Support unmarshalling of values into result.Result[T]. For example the JSON value 42 should unmarshall into:

result.Ok(42)

Does this incur a breaking change?

No.

Do you intend to build this feature yourself?

Maybe.

Additional context

Do not implement marshalling, only unmarshalling.

Type inference simplifies examples greatly

What happened?

Type inference rules have gotten much better since 1.18, so now you can:

func main() {
	// All even natural numbers (2, 4, 6, 8...)
	isEven := func(n int) bool { return n%2 == 0 }
	evens := iter.Filter(iter.Drop(iter.Count(), 1), isEven)
	for i := 0; i < 5; i++ {
		fmt.Println(evens.Next().Unwrap())
	}

	file, _ := os.Open(`main.go`)
	// All non-empty lines from a file
	lines := iter.Exclude(iter.LinesString(file), filters.IsZero)
	for line := lines.Next(); line.IsSome(); line = lines.Next() {
		fmt.Println(line.Unwrap())
	}

	// String representations of numbers
	numbers := iter.Map(iter.Count(), strconv.Itoa)
	for i := 0; i < 5; i++ {
		fmt.Println(numbers.Next().Unwrap())
	}
}

Which outputs

2
4
6
8
10
Ok(package main)
Ok(import ()
Ok(     "fmt")
Ok(     "os")
Ok(     "strconv")
Ok(     "github.com/BooleanCat/go-functional/iter")
Ok(     "github.com/BooleanCat/go-functional/iter/filters")
Ok())
Ok(func main() {)
Ok(     // All even natural numbers (2, 4, 6, 8...))
Ok(     isEven := func(n int) bool { return n%2 == 0 })
Ok(     evens := iter.Filter(iter.Drop(iter.Count(), 1), isEven))
Ok(     for i := 0; i < 5; i++ {)
Ok(             fmt.Println(evens.Next().Unwrap()))
Ok(     })
Ok(     file, _ := os.Open(`main.go`))
Ok(     // All non-empty lines from a file)
Ok(     lines := iter.Exclude(iter.LinesString(file), filters.IsZero))
Ok(     for line := lines.Next(); line.IsSome(); line = lines.Next() {)
Ok(             fmt.Println(line.Unwrap()))
Ok(     })
Ok(     // String representations of numbers)
Ok(     numbers := iter.Map(iter.Count(), strconv.Itoa))
Ok(     for i := 0; i < 5; i++ {)
Ok(             fmt.Println(numbers.Next().Unwrap()))
Ok(     })
Ok(})
0
1
2
3
4

Though it's worth noting that lines seems to require extra indirection.

[Bug ๐Ÿ›]: Test bug report please ignore

What happened?

Provide a description of the issue.

What did you expect to happen?

Explain what you would have expected to happen instead.

Which version of go-functional were you using?

Provide the version number (such as v0.3.0).

To Reproduce

Provide a code snippet or steps to reproduce the issue.

Do you intend to fix this issue yourself?

Yes or no. This is so that the maintainers know to leave you time to make a
contribution rather than just fixing it themselves.

Additional context

Add any other context about the problem here.

[Bug ๐Ÿ›]: Run report card linters in `make lint`

What happened?

Sometimes go-functional fails a report card lint (misspell has been an example). I only learn about this after I ship a new release.

What did you expect to happen?

I'd like linting to fail in CI if go-functional scores anything less than 100% on the report card linters.

Which version of go-functional were you using?

v0.15.0

To Reproduce

You can see lint errors on the report card (if there are any currently).

Do you intend to fix this issue yourself?

No.

Additional context

I suspect these are probably optional linters in golangci-lint and it's just a case of configuring a yaml file in the repo. If that is the case, run all default linters + linters run in the report card. Adding additional linters is out of scope but I would consider a separate PR with solid reasoning for using other linters.

[Feature ๐Ÿ”จ]: Transform iterator

Is your feature request related to a problem? Please describe.

It's not possible to chain Map iterators because of limitations with Go's type system (you cannot define new type parameters on methods). So this is not possible:

func (iter *BaseIter[T]) Transform[U any](func(T) U) *MapIter[T, U] {
  ...
}

Describe the solution you'd like

I'd like a more limited version of the Map iterator where the return type is the same as the input type.

Provide code snippets to show how this new feature might be used.

The following should be possible and will allow chaining on simpler Map operations.

func (iter *BaseIter[T]) Transform(func(T) T) *TransformIter[T] {
  ...
}
evens := iter.Count().Transform(double).Take(5).Collect()

Does this incur a breaking change?

No.

Do you intend to build this feature yourself?

Maybe, be a good one for first time contributors to pick up.

  • Create TransformIter[T]
  • Test it
  • Write two example tests for calling it straight up or via method chaining

Just a friendly comment about documentation

Hi,

I stumbled upon your project via Twitter and while I read your TODOs in your README,
this echoed with me since I'm writing a go library as well.
I also searched for simple ways, to include example code in the README,
which is automatically checked by the compiler and I would like to avoid manual copy&paste actions.

May I share my solution?

  • using Go's support for examples: name your file like "example_*_test.go". This will be compiled during 'go test' run and will not be compiled into production code. See also https://go.dev/doc/effective_go#package-names
  • using Markdown-Autodocs github action, which replaces placeholders in your REAME.md with the actual example file ode (see also dineshsonachalam/[email protected])

I hope this saves you some research time, as I found this very helpful for my own library 'lib-bpmn-engine'.

๐Ÿ‘ for you and your project.

PS: not an issue, this can be closed.

[design question] Chainable iterators

I see you've recently rewritten the package to support go generics. Having this kinds of wrappers is really useful ๐Ÿ‘๐Ÿป

You went sort of the pythonic way with passing iterators into functions. However most other languages have a chainable iterator API that is more convenient to use (js map/filter/... on arrays, java streams, c++20 ranges, rust iterators). Have you considered something smilar?
Given that Go has no inheritance (for java streams) nor trait implementations for traits (for rust iters), building a convenient chainable API is somewhat challenging. Avoiding code duplication would require one more level of abstraction - always hiding specific iterators behind an iterator wrapper.

[Bug ๐Ÿ›]: Possible race condition in LiftHashMap

What happened?

This test failed unexpectedly: https://github.com/BooleanCat/go-functional/actions/runs/5985534714/job/16237724514 (TestLiftHashMapKeysCloseEarly).

What did you expect to happen?

The test should be deterministic.

Which version of go-functional were you using?

HEAD @ ece50cdf941d64d8384d982a9e1c4ee62a757605

To Reproduce

go test -run TestLiftHashMapKeysCloseEarly ./iter -count=100000

Do you intend to fix this issue yourself?

Yes.

[Feature ๐Ÿ”จ]: Stringer implementation for `iter.Pair[T, U]`

Is your feature request related to a problem? Please describe.

Printing a Pair of values should be reconisably a 2-length tuple in a simple representation.

Describe the solution you'd like

Tuples should print like so:

foo := iter.Pair[string, int]("apple", 42)
fmt.Printf("%s\n", foo) // (apple, 42)

This will involve implementing the fmt.Stringer interface for the type.

Does this incur a breaking change?

No.

Do you intend to build this feature yourself?

No.

[Feature ๐Ÿ”จ]: Iterator from channel

Is your feature request related to a problem? Please describe.

A common way to do transformation pipelines currently is using channels. It would be useful to be able to plug into those.

Describe the solution you'd like

An iterator that plugs consumes a channel. The iterator is exhausted when the channel is closed.

Provide code snippets to show how this new feature might be used.

func ExampleFromChannel() {
  ch := make(chan int, 2)

  go func() {
    ch <- 1
    ch <- 2
    close(ch)
  }()

  fmt.Println(iter.Collect[int](iter.FromChannel[int](ch)))

  // Output: [1 2]
}

Does this incur a breaking change?

No.

Do you intend to build this feature yourself?

No.

Additional context

N/A.

[Feature ๐Ÿ”จ]: Support callback operations over iterators (`ForEach`)

Is your feature request related to a problem? Please describe.

There ought to be a way to "do something" with items yielded from iterators without collecting the values. ForEach supports providing a callback to operate over each member.

Describe the solution you'd like

words := iter.Lift([]string{"some", "words", "please"})
iter.ForEach(words, func(word string) {
  fmt.Println(word)
})

Does this incur a breaking change?

No.

Do you intend to build this feature yourself?

Maybe.

[Feature ๐Ÿ”จ]: Support `Exclude`ing of iterators via methods

Is your feature request related to a problem? Please describe.

Excludeing an iterator is achieved by using the iter package's Exclude function. This can lead to deeply nested code that can be difficult to read. The issue belongs to a series of issues that remedy that problem. See #55 for more details.

Describe the solution you'd like

Each iterator should allow Excludeing via a method rather than the function iter.Exclude.

For example, this test:

func ExampleExclude() {
	filtered := iter.Exclude[int](iter.Lift([]int{0, 1, 0, 2}), filters.IsZero[int]).Collect()
	fmt.Println(filtered)
	// Output: [1, 2]
}

Could instead read like:

func ExampleFilter() {
	filtered := iter.Lift([]int{0, 1, 0, 2}).Exclude(filters.IsZero[int]).Collect()
	fmt.Println(filtered)
	// Output: [1 2]
}

Does this incur a breaking change?

No.

Do you intend to build this feature yourself?

No - I'd like to offer this to first-time contributors ideally.

Checklist

  • Implement .Exclude(filter) on each BaseIter
  • Add a simple test for this (don't test extensively, Exclude is already comprehensively tested elsewhere).
  • Update any Example... tests or README references to Exclude to use the method.

See #57 for an example of where this was implemented for Collect.

[Bug ๐Ÿ›]: Example specs not visible in reference docs for Option and Result packages

What happened?

When browsing the reference documentation, the Option and Result packages do not have any examples visible.

What did you expect to happen?

I expect to see examples for each function and receiver in both the Option and Result packages' reference documentation.

Which version of go-functional were you using?

v0.4.0

To Reproduce

Visit the reference documentation and observe the missing examples.

Do you intend to fix this issue yourself?

Not at the moment.

Additional context

There are example tests in the packages' source, but for some reason it isn't rendered in the reference documentation.

[Feature ๐Ÿ”จ]: Create a new function by chaining existing ones

Is your feature request related to a problem? Please describe.

It is a feature that I liked a lot from the IBM/fp-go library, but that library is very confusing to use and has hardly any documentation.

Describe the solution you'd like

Create a function that takes an initial value t0 and successively applies N functions where the input of a function is the return value of the previous function.

Provide code snippets to show how this new feature might be used.

// example implementation
func Flow2[F1 ~func(T0) T1, F2 ~func(T1) T2, T0, T1, T2 any](f1 F1, f2 F2) func(T0) T2 {
	return func(t0 T0) T2 {
		return f2(f1(t0))
	}
}

var (
	toStr = func(value int) string { return fmt.Sprint(value) }
	length = func(value string) int { return len(value) }
)

func main() {
	// usage
	myFun := Flow2(toStr, length)
	
	// first convert int to string, then get the length of the string
	
	fmt.Println(myFun(123456)) // 6
}

Does this incur a breaking change?

No.

Do you intend to build this feature yourself?

Yes

Additional context

My English is quite bad, so I could not do the documentation of the feature, but I could use a translator to leave notes for whoever is going to do the final documentation.

[Feature ๐Ÿ”จ]: Lint code with golangci-lint

Is your feature request related to a problem? Please describe.

It'd be great to get a trusted linter such as golangci-lint running over the code as a step in the check job of the test workflow.

Describe the solution you'd like

Each time a PR is raised, merge should be blocked on golangci-lint passing.

Provide code snippets to show how this new feature might be used.

N/A.

Does this incur a breaking change?

No.

Do you intend to build this feature yourself?

No.

Additional context

I'm not sure if the default configuration of the linter is good enough or if we should enable or disable particular lints. I'll leave it to whoever works on this to figure that out and suggest something in a PR.

If there are issues found by the linter and they are not particularly arduous then please fix them in separate commits in the same PR or provide justifcation why we should disable that linter.

[Feature ๐Ÿ”จ]: `Tee` behaviour for iterators

Is your feature request related to a problem? Please describe.

It's possible users would like to use an iterator more than once. Be using tee as inspiration we can have that behaviour in go-functional.

Describe the solution you'd like

evens := iter.Count().Filter(isEven).Take(5)

iters := iter.Tee(evens)

iters.One.Take(2).Collect()  // [0 2]
iters.Two.Take(2).Collect()  // [0 2]

Does this incur a breaking change?

No.

Do you intend to build this feature yourself?

If no one else picks it up.

Additional context

Since the iterators that are generated by tee may be consumed at different rates, the tee struct will need to remember a portion of the results from underlying iterator (the lag between the faster and slower iterators). A naive solution would just remember the entire underlying iterator, a better solution would use something like a double ended queue.

Thread safety is out of scope unless you think it should be in scope and can covince me.

Users can understand the core concepts of go-functional by reading the README

The README should provide a brief overview of go-functional and leave the details to the auto-generated docs. The following topics should be covered in the README.

  • Badges for test action result and docs link
  • TL;DR - show a nifty example of a real problem being solved (first 10 primes?)
  • Link again to docs
  • Overview of iterators and options
  • Show how to write an iterator

[Feature ๐Ÿ”จ]: Add `option.Map`

Is your feature request related to a problem? Please describe.

It's a feature request, a lot of std libs of functional programming languages have this built in.

Describe the solution you'd like

A robust implementation of the option.Map

Provide code snippets to show how this new feature might be used.

Given the following function

func x (int) string {
  if x == 0 {
    return "zero"
  }

  return "non-zero"
}

We should be able to pass function x to option.Map

option.Map[int, string](x)

and get back a closure y

func y (o option.Option[int]) option.Option[string] {
  if o.IsNone() {
    return option.None[string]()
  }

  return option.Some[string](x(o.Unwrap())
}

Effectively Map allows a function to work with option types, it's pretty useful when we want functions to more easily
work with functions that return an Option variant

Does this incur a breaking change?

No, it does not.

Do you intend to build this feature yourself?

Yes, it should be pretty simple.

[Proposal] Iterator over chan

I think that having an iterator over a channel would be very convenient, as it would allow to build delayed pipelines.
Maybe even not only over one channel, but over multiple. The reflect package has a dynamic select to listen to multiple channels.

Another idea would be a function for consuming an iterator and sending all its values to a channel.
This way we could describe a pipeline that reads from a channel, does some mapping/filtering and writes to another. All imperatively ๐Ÿฅณ

I'm eager to implement it. What do you think of it?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.