-
Notifications
You must be signed in to change notification settings - Fork 113
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
Make public functions of Project type immutable #518
Conversation
c5a8d95
to
4182b0a
Compare
4182b0a
to
a8f6551
Compare
types/project.go
Outdated
return newProject, nil | ||
} | ||
|
||
func (p *Project) projectDeepCopy() (*Project, 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.
why not just deepCopy
?
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.
Signed-off-by: Guillaume Lours <[email protected]>
a8f6551
to
77f5c14
Compare
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, some comments but nothing majorly concerning.
Two other high-level thoughts:
-
mitchellh
is no longer doing Go dev - he recently archived a bunch of libs; I'm not super concerned since deep copying is an internal detail and doesn't pollute the API, more of an FYI -
The naming & arguments of all these functions is pretty inconsistent, since this is all breaking API changes anyway, it might make sense to do a pass for consistency, e.g. make them all
WithFoo()
style.
types/project.go
Outdated
func (p *Project) WithServices(names []string, fn ServiceFunc, options ...DependencyOption) error { | ||
// WithServices runs ServiceFunc on each service and dependencies according to DependencyPolicy | ||
// It returns a new Project instance with the changes and keep the original Project unchanged | ||
func (p *Project) WithServices(names []string, fn ServiceFunc, options ...DependencyOption) (*Project, 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.
This method is the only one I think is odd - the returned project option is always identical to the original.
I think it might make sense to have a pair of functions:
ForEach(func(name string, svc ServiceConfig) error) error
- functional loop helper, does not copy projectMutateServices(func(name string, svc *ServiceConfig) error) (*Project, error)
- clone project, allowing caller to get a mutable reference to each service in the process
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.
Agree. As we now use WithXX
fr mutating functions, this name is not relevant anymore. ForEach
would be a better one. We then can have a new WithServiceFn(func(ServiceConfig) ServiceConfig)
for service mutation, if relevant (afaik we don't use this in docker/compose)
types/project.go
Outdated
func (p *Project) DisableService(service ServiceConfig) { | ||
// DisableService removes from the project model the given service and its references in all dependencies | ||
// It returns a new Project instance with the changes and keep the original Project unchanged | ||
func (p *Project) DisableService(service ServiceConfig) (*Project, 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.
minor: it's kind of weird that this one takes a ServiceConfig
instead of a name like all the others (but that's not new)
types/project.go
Outdated
instance, err := copystructure.Copy(p) | ||
if err != nil { | ||
return nil, err | ||
} |
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.
In theory, this should not fail, right? It might make sense for this to panic()
to avoid carrying the error
everywhere.
types/project.go
Outdated
func (p *Project) ForServices(names []string, options ...DependencyOption) error { | ||
// ForServices restricts the project model to selected services and dependencies | ||
// It returns a new Project instance with the changes and keep the original Project unchanged | ||
func (p *Project) ForServices(names []string, options ...DependencyOption) (*Project, 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.
(see other comment re: getting rid of error
from deepCopy()
)
I am wondering if it makes sense to ignore unknown names here, so that this method can return *Project
, which would make it much more ergonomic to use: p = p.ForServices(svc)
.
In particular, this could allow chaining, e.g.
p.ForServices(svcs).ForEach(...)
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.
Or we can introduce an Either
type 😇 , WDYT?
edit: maybe a bad idea, this kind of structure isn't used in the Golang ecosystem.
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 am wondering if it makes sense to ignore unknown names here
this would be a bug then if we silently ignore incorrect parameter being set, especially if this is "only" for code convenience.
.. which is sad as we rely on mapstructure for parsing. Maybe we should revisit this to only rely on yaml.Unmarshall - unfortunately go-yaml is not open to such hybrid approach and my PR didn't received much attention. |
Yeah...at least |
5ac1ba3
to
06666e2
Compare
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!
eb908b2
to
be5b2dd
Compare
Signed-off-by: Guillaume Lours <[email protected]>
be5b2dd
to
8d9c164
Compare
What I did
github.com/mitchellh/copystructure
to make deep copy ofProject
structProject
public functions which mutateProject
state immutabl, returning a new instance ofProject
and usage a commonWith
prefix for each of themProject.WithProject
function by aProject.ForEachProject
functiontypes.set
typeRelated issue
https://docker.atlassian.net/browse/COMP-380