New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Functional will contain functional programming patterns made possible by Go generics. #55
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think personally this is really neat, from a technical point of view, but I worry about the wider use of it. This trades clarity for code size. It can make sense in some contexts where these patterns are widely established among users, but I think it's going to make the code harder to read to the generic reader. Including it in the secure-stdlib is an implicit approval of the pattern. To me, clarity is paramount and I'd prefer this was kept private to repositories where it is widely employed and understood, if at all. These sort of patterns are not widely used within Go, as far as I know. Even a year on from generics being released these patterns have not emerged as a common way to do things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, one question
package functional | ||
|
||
type Producer[V any] func() V | ||
type ErrorableProducer[V any] func() (V, error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it standard practice to say Errorable
as a prefix? I think in general I like ProducerWithError
, for example, just for discoverability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I didn't love that but didn't know if there was precedent for something better.
In light of @johanbrandhorst's comment, I retract my review for now :) |
// Package lazy implements generic Lazy functions. Lazy functions are evaluated when first called, while | ||
// all subsequent calls return the results of the initial call. Call lazy functions in your code | ||
// as many times as you like knowing that the computation or side effects will only occur once. | ||
package lazy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to object to using lazy
and suggest memoized
as that's more likely to land with Go programmers who likely aren't coming from non-applicative languages. But, feel free to sustain my objection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went with memoized initially, but memoization takes into consideration the values of the arguments, which isn't the case here. A memoized function would re-evaluate if the values of the arguments were distinct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That said, that may be the more useful generalization, though not as simple an implementation and with some interesting memory usage considerations.
Just here to give a strong :+1 to @johanbrandhorst comment |
Will pull into the using repo for now. |
This PR adds the lazy pattern, which allows Producers (func() V), Functions (func(A) V), BiFunctions (func(A,B),V) and a version of each which also returns an error to be wrapped in logic which evaluates the underlying function such that when initially called the function calculates the result. All subsequent calls return the results of the first again.
This can be used to lazily compute a value or reach out to an expensive resource once, yet have the function be called throughout code without worrying about double-calculation or difficult coordinary across function boundaries.