Fields are defined by a Field Transmitter and a Field Receiver. Any nodes between these will be part of the Field and will be iterated until either the Transmitter runs out of data or the Receiver decides its conditions are met.

To mark a Transmitter or Reciever as a Feild, set is_field=True on the attribute decorator.

@wob.transmitter("value", "name", is_field=True)
def transmit_values(self):
  ...
@wob.receiver("value", "name", is_field=True)
def receiver_values(self):
  ...

A Field Transmitter is expected to return a tuple of two iterators. The first iterator is used as “peek ahead” to determine if there is more data available. The second iterator is the actual data. An easy way to do this is to use the tee function from the itertools module to turn an array into two iterators.

Here’s an example of a node that splits a string by commas and returns the strings as a Field.

from mirmod.controls import Textbox
import itertools

@wob.init()
def init(self):
  self.values = None
  self.iterator = None

@wob.receiver("value","input", control=Textbox(placeholder="a,b,c,..."))
def receive_input(self, i):
  self.values = i.split(',')

@wob.transmitter("value", "output", is_field=True)
def transmit_values(self):
  return self.iterator

@wob.execute()
def execute(self):
  self.iterator = itertools.tee(self.values)

These values can then be collected using the miranda.collector Prefab.

This pattern will output the following:

== Collector ==
++ input_2:
['a', 'b', 'c']
++ output_2: a b c

Writing a Field Receiver is similar to writing a normal Receiver, it will just be called multiple times until the Transmitter runs out of data. Here’s an example of a node that prints each value in a Field.

@wob.receiver("value", "input", is_field=True)
def receive_values(self, i):
	print(i)

After all values have been received, execution will continue like normal. However, the Reciever can also use one of the Execution Context hooks to break out of the loop early. Here’s an example of a node that will only recieve the first two values in a Field, then break back into normal execution.

@wob.receiver("value", "input", is_field=True)
def receive_values(self, i):
	print(i)
	self.count_received += 1
	if self.count_received >= 2:
		# Break out of the current Field and continue normal execution
		miranda.get_execution_context().stop_current_iterator()

== Advanced setup ==

The following examples shows a nested example of field iterators. The expected execution plan from this graph is the following: S1, N1, S2, N2, N3, E2, E1

N1 is an inbound node to a node in the directed subgraph which exists within the field iterator. This means that it will be sorted before the start node of the field iterator, which in this case is S2. N3 is a leaf node in the subgraph and an inplicit support edge will be created between any such leaf and the end node of the field iterator, which in this case is E2.

These rules apply recursively, so if N3 would continue with an iterator pair S3-E3 then that subgraph would be treated the same.