You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The proposal is to allow any defined type to be iterated on using the for with range clause if that type loosely implements the following interface. Its Do function is called with the body of the for block as the argument of the function parameter, where breaks and returns inside the for block are translated into returning false inside the function, and continues are translated into returning true.
typeRangeableinterface {
Do(ffunc(...) bool)
}
The parameters of f are not defined because they can be zero or more parameters of any types, and those parameters would map to being iteration variables of the for block.
This would allow user-defined types to be iterated with a for block. The most relevant and useful example is the container/set type currently being discussed in #47331:
User-defined types cannot be iterated in a way that retains control of the current scope using the return, break, continue keywords. This results in two approaches to iteration being used for built-ins vs defined types. This is already evident in discussions about how the containers/set package may be used and will become more relevant as folks write more collection types once generics are released.
For example, to search a Set type for an element and exit early, code like the below would need to be written:
I can't take credit for the ideas that form this proposal. As far as I know the ideas were first suggested by @rsc and further elaborated on by @Merovius. The ideas were shared in response to the problem of how do we make iteration of new generic data structure types, like container/set, as simple as built-in types that can use forrange.
That said, if we were going to allow the use of range syntax on user-defined types, the best way I can see to do it would be to compile the syntax into a call to the Do function.
This proposal changes the Go spec's description of the range expression so that in addition to the range expression being allowed to be an array, pointer to an array, slice, string, map, or channel permitting receive operations, it may also be any defined type that has a Do function with one func parameter discussed below, and a single bool return value.
If a value that is a defined type is provided as a range expression and does not have a Do function, it is a compiler error.
If a value that is a defined type is provided as a range expression and it has a Do function, the one func argument of the Do function is called for each iteration. The body of the for with range block becomes the func argument given for the one func parameter of the Do function.
In the same way that a for with range may have zero or more iteration variables up to the number of iteration values for the built-in types, a for with range of a defined type may also have zero or more iteration variables up to the number of parameters that the Do function's signature defines in its one func parameter.
In the same way that iterations are defined by the built-in types to mean different things for slices, maps, channels, etc, a defined type also defines an iteration for itself the spec doesn't limit or constrain how a defined type might iterate. For example, a container/set-like type may iterate for each element of the set. Or, a type representing a numerical range or interval, may iterate for each interval in the range with some defined step.
The bool return value of the Do function signals if iteration should continue. breaks or returns in a for with range block are translated into returning false inside the function passed to the Do function and setting necessary temporary variables to communicate back the intention to return. See @Merovius's example above.
Proposal template
Would you consider yourself a novice, intermediate, or experienced Go programmer?
Experienced
What other languages do you have experience with?
Java, Ruby, C#, C, JavaScript
Would this change make Go easier or harder to learn, and why?
A little harder since there's one more thing to learn, that implementing the interface supports iteration.
Has this idea, or one like it, been proposed before? If so, how does this proposal differ?
I couldn't find a proposal issue that was identical. There are some proposals that attempt to do similar things:
proposal: Go 2: iterators #40605 - Provides a Next function that can be called repeatedly, but it is less adaptable to different types of iterables since it is limited to a single element/item being returned on each iteration.
proposal: Go 2: function values as iterators #43557 - Allow functions to be a range expression and used for iteration, however if a type is iterable, it seems clearer and easier to understand if the type itself can be passed to the range expression rather than a function generated by the type.
Who does this proposal help, and why?
Anyone building data structures that can be iterated, and anyone using them so that their iteration code is simple and much the same to iterating any built-in type.
What is the proposed change?
See above.
Please describe as precisely as possible the change to the language.
See above.
Show example code before and after the change.
See above.
What is the cost of this proposal? (Every language change has a cost).
How many tools (such as vet, gopls, gofmt, goimports, etc.) would be affected?
I'm not sure.
What is the compile time cost?
Little.
What is the run time cost?
None.
Can you describe a possible implementation?
See above.
Do you have a prototype? (This is not required.)
No.
How would the language spec change?
See above.
Orthogonality: how does this change interact or overlap with existing features?
It builds on the existing for range semantics without changing existing semantics. It supports new data structures such as container/set such that they may be iterated in the same way as built in types, maintaining consistency for use.
Is the goal of this change a performance improvement?
No.
If so, what quantifiable improvement should we expect?
The proposal is to allow any defined type to be iterated on using the
forwithrangeclause if that type loosely implements the following interface. ItsDofunction is called with the body of theforblock as the argument of the function parameter, where breaks and returns inside the for block are translated into returning false inside the function, and continues are translated into returning true.The parameters of
fare not defined because they can be zero or more parameters of any types, and those parameters would map to being iteration variables of theforblock.This would allow user-defined types to be iterated with a
forblock. The most relevant and useful example is thecontainer/settype currently being discussed in #47331:Another trivial example is types such as a numerical range or interval:
Why
User-defined types cannot be iterated in a way that retains control of the current scope using the
return,break,continuekeywords. This results in two approaches to iteration being used for built-ins vs defined types. This is already evident in discussions about how thecontainers/setpackage may be used and will become more relevant as folks write more collection types once generics are released.For example, to search a Set type for an element and exit early, code like the below would need to be written:
That code is significantly harder to understand than if the Set type supported iteration using this proposal:
Prior Discussion
I can't take credit for the ideas that form this proposal. As far as I know the ideas were first suggested by @rsc and further elaborated on by @Merovius. The ideas were shared in response to the problem of how do we make iteration of new generic data structure types, like
container/set, as simple as built-in types that can useforrange.-- @rsc #47331 (reply in thread)
-- @Merovius #47331 (reply in thread)
Further Details
This proposal changes the Go spec's description of the
range expressionso that in addition to the range expression being allowed to be an array, pointer to an array, slice, string, map, or channel permitting receive operations, it may also be any defined type that has aDofunction with onefuncparameter discussed below, and a singleboolreturn value.If a value that is a defined type is provided as a range expression and does not have a
Dofunction, it is a compiler error.If a value that is a defined type is provided as a range expression and it has a
Dofunction, the onefuncargument of theDofunction is called for each iteration. The body of theforwithrangeblock becomes thefuncargument given for the onefuncparameter of theDofunction.In the same way that a
forwithrangemay have zero or more iteration variables up to the number of iteration values for the built-in types, aforwithrangeof a defined type may also have zero or more iteration variables up to the number of parameters that theDofunction's signature defines in its onefuncparameter.In the same way that iterations are defined by the built-in types to mean different things for slices, maps, channels, etc, a defined type also defines an iteration for itself the spec doesn't limit or constrain how a defined type might iterate. For example, a
container/set-like type may iterate for each element of the set. Or, a type representing a numerical range or interval, may iterate for each interval in the range with some defined step.The
boolreturn value of theDofunction signals if iteration should continue.breaks orreturns in aforwithrangeblock are translated into returning false inside the function passed to theDofunction and setting necessary temporary variables to communicate back the intention to return. See @Merovius's example above.Proposal template
Would you consider yourself a novice, intermediate, or experienced Go programmer?
Experienced
What other languages do you have experience with?
Java, Ruby, C#, C, JavaScript
Would this change make Go easier or harder to learn, and why?
A little harder since there's one more thing to learn, that implementing the interface supports iteration.
Has this idea, or one like it, been proposed before? If so, how does this proposal differ?
I couldn't find a proposal issue that was identical. There are some proposals that attempt to do similar things:
Nextfunction that can be called repeatedly, but it is less adaptable to different types of iterables since it is limited to a single element/item being returned on each iteration.Who does this proposal help, and why?
Anyone building data structures that can be iterated, and anyone using them so that their iteration code is simple and much the same to iterating any built-in type.
What is the proposed change?
See above.
Please describe as precisely as possible the change to the language.
See above.
What would change in the language spec?
See above.
Please also describe the change informally, as in a class teaching Go.
See above.
Is this change backward compatible?
Yes
Show example code before and after the change.
See above.
What is the cost of this proposal? (Every language change has a cost).
I'm not sure.
Little.
None.
Can you describe a possible implementation?
See above.
No.
How would the language spec change?
See above.
Orthogonality: how does this change interact or overlap with existing features?
It builds on the existing for range semantics without changing existing semantics. It supports new data structures such as
container/setsuch that they may be iterated in the same way as built in types, maintaining consistency for use.Is the goal of this change a performance improvement?
No.
Does this affect error handling?
No.
Is this about generics?
No. But related, because generics appear to be driving general data structures, and that's raising the issue of iteration.