Skip to content
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

Count nested operations in StatusCounts #245

Merged
merged 4 commits into from
Oct 10, 2022
Merged

Conversation

groovemonkey
Copy link
Collaborator

This change adds op.WalkStatuses(), which recursively walks a tree of result ops to get statuses.

@groovemonkey groovemonkey self-assigned this Oct 7, 2022
op/op.go Outdated
}

// Create our accumulator and Walk ops
acc := make(map[Status]int, 0)
Copy link
Collaborator

@nwchandler nwchandler Oct 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm unclear on why this map would be initialized with a size of 0. 🤔 Would it be better to omit the size so that it doesn't immediately need resized?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally right; thanks for catching that!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed this to initialize to len(ops), which is the minimum length this will have (if there are no nested ops).

op/op.go Outdated
}
return statuses, nil
}

// WalkStatuses performs a depth-first search of a tree of results and accumulates a map of status counts
func WalkStatuses(statuses map[Status]int, results map[string]any) map[Status]int {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're mutating statuses here, and then returning it at the end. Perhaps we could avoid the mutation, though. What if WalkStatuses took only a results map, and it had an inner function defined, which would take a status map. We could recurse on the inner function and then return what it built up at the end of WalkStatuses. This would, I think, both make the calling of the WalkStatuses easier (no need to create an accumulator) and also avoid accidental mutation.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this, perhaps:

func WalkStatuses(results map[string]any) map[Status]int {
	statuses := make(map[Status]int)

	var walkStatuses func(map[Status]int, map[string]any)
	walkStatuses = func(accumulator map[Status]int, results map[string]any) {
		for _, res := range results {
			switch res := res.(type) {

			case map[string]any:
				walkStatuses(statuses, res)

			case Op:
				// Increment and recur
				statuses[res.Status]++
				walkStatuses(statuses, res.Result)

			// End of branch reached
			default:
				continue
			}
		}
	}
	walkStatuses(statuses, results)
	return statuses
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

walkStatuses could also just be an unexported function as well - the main idea is, can we avoid mutation in the public API?

Copy link
Contributor

@mkcp mkcp Oct 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not certain it's the ideal implementation, but it does let us pass in partially constructed accumulators for testing or for more elaborate concurrent reducers. You could reasonably do the same thing without passing in an accumulator though, treating it as the steps: split/partition results -> map over result subset and return map[Status]int -> merge all map[Status]int. Also, the accumulator is passed by value so we don't have to worry about changes from outside goroutines.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was mistaken! maps are like channels not slices and get passed by reference even without an explicit pointer!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks -- used @nwchandler 's inner-closure version of this function. Let me know if that's good to go!

Copy link
Collaborator

@nwchandler nwchandler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me!

@groovemonkey groovemonkey merged commit ad0c750 into main Oct 10, 2022
@groovemonkey groovemonkey deleted the dcohen/op-walkstatuses branch October 10, 2022 20:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants