1. # Iterable is done by anything that we should be able to get an iterator
  2. # from. Things that are Iterable will flatten in flattening contexts, so a
  3. # default implementation of .flat is provided by this role. As itemization is
  4. # what defeats flattening, this role also provides a default .item method.
  5. # Additionally, as .lazy and .eager are about iterator behavior, they are
  6. # provided by this role. Overriding those is not likely to be needed, and
  7. # discouraged to maintain predictable semantics. Finally, both .hyper() and
  8. # .race() are methods to enter the hyper and race paradigm and implemented
  9. # here, so they can use any Iterable as a source.
  10. my class HyperSeq { ... }
  11. my class RaceSeq { ... }
  12. my class Rakudo::Internals::HyperIteratorBatcher { ... }
  13. my role Iterable {
  14. method iterator() { ... }
  15. method item() {
  16. nqp::p6bindattrinvres(nqp::create(Scalar), Scalar, '$!value', self)
  17. }
  18. method flat(Iterable:D:) {
  19. Seq.new(class :: does Iterator {
  20. has Iterator $!source;
  21. has Iterator $!nested;
  22. method new(\source) {
  23. nqp::p6bindattrinvres(nqp::create(self),self,'$!source',source)
  24. }
  25. method pull-one() is raw {
  26. nqp::if(
  27. $!nested,
  28. nqp::if(
  29. nqp::eqaddr((my $got := $!nested.pull-one),IterationEnd),
  30. nqp::stmts(
  31. ($!nested := Iterator),
  32. self.pull-one
  33. ),
  34. $got
  35. ),
  36. nqp::if(
  37. nqp::iscont($got := $!source.pull-one),
  38. $got,
  39. nqp::if(
  40. nqp::istype($got,Iterable),
  41. nqp::stmts(
  42. ($!nested := $got.flat.iterator),
  43. self.pull-one
  44. ),
  45. $got
  46. )
  47. )
  48. )
  49. }
  50. method push-all($target --> IterationEnd) {
  51. nqp::stmts(
  52. nqp::if(
  53. $!nested,
  54. nqp::stmts(
  55. $!nested.push-all($target),
  56. ($!nested := Iterator)
  57. )
  58. ),
  59. nqp::until(
  60. nqp::eqaddr((my $got := $!source.pull-one), IterationEnd),
  61. nqp::if(
  62. nqp::iscont($got),
  63. $target.push($got),
  64. nqp::if(
  65. nqp::istype($got,Iterable),
  66. $got.flat.iterator.push-all($target),
  67. $target.push($got)
  68. )
  69. )
  70. )
  71. )
  72. }
  73. method is-lazy() { $!source.is-lazy }
  74. }.new(self.iterator))
  75. }
  76. method lazy-if($flag) { $flag ?? self.lazy !! self }
  77. method lazy() {
  78. # Return a Seq with an iterator wrapping this Iterable, claiming to
  79. # be lazy, and implicitly preventing working ahead (by hiding any
  80. # push-at-least-n of the source iterator).
  81. Seq.new(class :: does Iterator {
  82. has $!iterable;
  83. has $!iterator;
  84. method new(\iterable) {
  85. nqp::p6bindattrinvres(
  86. nqp::create(self),self,'$!iterable',iterable
  87. )
  88. }
  89. method pull-one() is raw {
  90. $!iterator := $!iterable.iterator unless $!iterator.DEFINITE;
  91. $!iterator.pull-one
  92. }
  93. method push-exactly($target, int $n) {
  94. $!iterator := $!iterable.iterator unless $!iterator.DEFINITE;
  95. $!iterator.push-exactly($target, $n);
  96. }
  97. method is-lazy() { True }
  98. }.new(self))
  99. }
  100. method hyper(Int(Cool) :$batch = 64, Int(Cool) :$degree = 4) {
  101. HyperSeq.new:
  102. configuration =>
  103. HyperConfiguration.new(:$degree, :$batch, :method<hyper>),
  104. work-stage-head =>
  105. Rakudo::Internals::HyperIteratorBatcher.new(:$.iterator)
  106. }
  107. method race(Int(Cool) :$batch = 64, Int(Cool) :$degree = 4) {
  108. RaceSeq.new:
  109. configuration =>
  110. HyperConfiguration.new(:$degree, :$batch, :method<race>),
  111. work-stage-head =>
  112. Rakudo::Internals::HyperIteratorBatcher.new(:$.iterator)
  113. }
  114. sub MIXIFY(\iterable, \type) {
  115. nqp::if(
  116. (my $iterator := iterable.flat.iterator).is-lazy,
  117. Failure.new(X::Cannot::Lazy.new(:action<coerce>,:what(type.^name))),
  118. nqp::if(
  119. nqp::elems(my $elems := Rakudo::QuantHash.ADD-PAIRS-TO-MIX(
  120. nqp::create(Rakudo::Internals::IterationSet),$iterator
  121. )),
  122. nqp::create(type).SET-SELF($elems),
  123. nqp::if(
  124. nqp::eqaddr(type,Mix),
  125. mix(),
  126. nqp::create(type)
  127. )
  128. )
  129. )
  130. }
  131. multi method Mix(Iterable:D:) { MIXIFY(self, Mix) }
  132. multi method MixHash(Iterable:D:) { MIXIFY(self, MixHash) }
  133. sub BAGGIFY(\iterable, \type) {
  134. nqp::if(
  135. (my $iterator := iterable.flat.iterator).is-lazy,
  136. Failure.new(X::Cannot::Lazy.new(:action<coerce>,:what(type.^name))),
  137. nqp::if(
  138. nqp::elems(my $elems := Rakudo::QuantHash.ADD-PAIRS-TO-BAG(
  139. nqp::create(Rakudo::Internals::IterationSet),$iterator
  140. )),
  141. nqp::create(type).SET-SELF($elems),
  142. nqp::if(
  143. nqp::eqaddr(type,Bag),
  144. bag(),
  145. nqp::create(type)
  146. )
  147. )
  148. )
  149. }
  150. multi method Bag(Iterable:D:) { BAGGIFY(self, Bag) }
  151. multi method BagHash(Iterable:D:) { BAGGIFY(self, BagHash) }
  152. sub SETIFY(\iterable, \type) {
  153. nqp::if(
  154. (my $iterator := iterable.flat.iterator).is-lazy,
  155. Failure.new(X::Cannot::Lazy.new(:action<coerce>,:what(type.^name))),
  156. nqp::if(
  157. nqp::elems(my $elems := Rakudo::QuantHash.ADD-PAIRS-TO-SET(
  158. nqp::create(Rakudo::Internals::IterationSet),$iterator
  159. )),
  160. nqp::create(type).SET-SELF($elems),
  161. nqp::if(
  162. nqp::eqaddr(type,Set),
  163. set(),
  164. nqp::create(type)
  165. )
  166. )
  167. )
  168. }
  169. multi method Set(Iterable:D:) { SETIFY(self,Set) }
  170. multi method SetHash(Iterable:D:) { SETIFY(self,SetHash) }
  171. }