My problem occured while trying to write an efficient iterator for permutations groups: they can be efficient generated by running throught the products of some lists of permutations (some transversals).
I give here a simple MWE which mimics that situation, using concatenation of strings instead of multiplication of permutations to mimic a non-commutative product.
So imagine that a group is:
struct Group
  s::Vector{String}
end
G=Group(["one","happy","man"])
and  the “elements” of G are all the words made by taking one letter from the  first string,  one from  the second,  etc…
I can make an efficient iterator for this
I=(prod(x) for x in Iterators.product(G.s...));
and the timing is not bad.
julia> @btime collect(I)
  902.024 ns (46 allocations: 1.84 KiB)
3Ă—5Ă—3 Array{String,3}:
[:, :, 1] =
 "ohm"  "oam"  "opm"  "opm"  "oym"
 "nhm"  "nam"  "npm"  "npm"  "nym"
 "ehm"  "eam"  "epm"  "epm"  "eym"
[:, :, 2] =
 "oha"  "oaa"  "opa"  "opa"  "oya"
 "nha"  "naa"  "npa"  "npa"  "nya"
 "eha"  "eaa"  "epa"  "epa"  "eya"
[:, :, 3] =
 "ohn"  "oan"  "opn"  "opn"  "oyn"
 "nhn"  "nan"  "npn"  "npn"  "nyn"
 "ehn"  "ean"  "epn"  "epn"  "eyn"
But  now I would like  G itself to be  able to iterate. I  do not want to write  for s in I or  for s in iterator(G) but  for s in G. So I want G itself to iterate through I.
The only way I found is:
Base.length(G::Group)=prod(length.(G.s))
Base.eltype(::Type{Group})=String
function Base.iterate(G::Group)
  I=(prod(x) for x in Iterators.product(G.s...))
  p=iterate(I)
  if isnothing(p) return end
  str,state=p
  str,(I,state)
end
function Base.iterate(G::Group,(I,state))
  p=iterate(I,state)
  if isnothing(p) return end
  str,state=p
  str,(I,state)
end
This iterates OK but the performance is terrible:
julia> @btime collect(G);
  5.672 ÎĽs (241 allocations: 13.92 KiB)
So my question is: is there a better way to do that, that is to delegate efficiently iteration from G to a dynamically computed I?