private FuturePortValue unwrapPort(Value inputPortValue) {
   if (inputPortValue instanceof TupleValue) {
     TupleValue inputPortTuple = (TupleValue) inputPortValue;
     if (inputPortTuple.getSize() == 1) {
       // get "first" entry
       Iterator<Value> it = inputPortTuple.getEntries().values().iterator();
       // possibly wrapped again?
       return unwrapPort(it.next());
     } else {
       throw new UndefinedBehaviourError("cannot unwrap");
     }
   } else if (inputPortValue instanceof FuturePortValue) {
     return (FuturePortValue) inputPortValue;
   } else {
     throw new UndefinedBehaviourError("value not a port or 1-tuple of ports");
   }
 }
  @Override
  public void elaborate() throws Exception {
    // prevent infinite recursive elaboration
    if (futureOutputPorts != null) {
      return;
    }
    log.debug("type signature is " + signature.toString());
    // decompose signature into (input) -> (output)
    TupleTypeValue inputType = (TupleTypeValue) signature.getInputType();
    TupleTypeValue outputType = (TupleTypeValue) signature.getOutputType();
    // construct values for all future output ports
    // simultaneously build a collection of all input port names
    Set<String> inputPortNames = new HashSet<>();
    Set<String> outputPortNames = new HashSet<>();
    inputPortNames.addAll(nodeType.getPorts().keySet());
    MappedArray<String, Value> futurePortMap = new MappedArray<>();

    for (MappedArray<String, TypeValue>.Entry typeEntry : outputType.getSubtypes()) {
      String outputPortName = typeEntry.getKey();
      PortTypeValue outputPortType = nodeType.getPorts().get(outputPortName);
      FuturePortValue futurePort = new FuturePortValue(this, outputPortName, outputPortType);
      futurePortMap.put(outputPortName, futurePort);
      inputPortNames.remove(outputPortName);
      outputPortNames.add(outputPortName);
    }
    futureOutputPorts = new TupleValue(outputType, futurePortMap);

    inputEdge.getSource().elaborate();
    Value inputValue = inputEdge.getSource().getValue();
    TupleValue input = (TupleValue) inputValue;

    // now we have enough information to disassemble `input`
    // into attributes and (future) input ports

    Map<String, Value> nodeAttrs = new HashMap<>();
    Map<String, Map<String, Value>> portAttrs = new HashMap<>();

    for (String inputPortName : inputPortNames) {
      PortTypeValue inputPortType = nodeType.getPorts().get(inputPortName);
      Value inputPortValue = input.getEntry(inputPortName);
      // if the port has attributes, this is a tuple
      // (0: FuturePortValue, 1: (attributes))
      // otherwise, this is just a FuturePortValue
      FuturePortValue futurePort;
      Map<String, Value> inputPortAttrs;
      if (inputPortType.getAttributes().isEmpty()) {
        // in the case where a single output port is directly used as input,
        // this is a tuple of size 1 that needs to be "unwrapped" first
        futurePort = unwrapPort(inputPortValue);
        futureInputPorts.put(inputPortName, futurePort);
        inputPortAttrs = new HashMap<>(); // no attributes
      } else {
        TupleValue inputPortTuple = (TupleValue) inputPortValue;
        // same story here about unwrapping the port
        futurePort = unwrapPort(inputPortTuple.getEntry(0));
        TupleValue attributesValue = (TupleValue) inputPortTuple.getEntry(1);
        // TODO this can probably be done better, it assumes that all tuple values are named
        inputPortAttrs = MappedArray.toMap(attributesValue.getEntries());
      }
      futureInputPorts.put(inputPortName, futurePort);
      portAttrs.put(inputPortName, inputPortAttrs);
    }

    // we must also get output port attributes, which appear as function inputs
    for (String outputPortName : outputPortNames) {
      PortTypeValue outputPortType = nodeType.getPorts().get(outputPortName);
      Map<String, Value> outputPortAttrs;
      if (outputPortType.getAttributes().isEmpty()) {
        outputPortAttrs = new HashMap<>(); // no attributes
      } else {
        TupleValue attributesValue = (TupleValue) input.getEntry(outputPortName);
        outputPortAttrs = MappedArray.toMap(attributesValue.getEntries());
      }
      portAttrs.put(outputPortName, outputPortAttrs);
    }

    for (String attrName : nodeType.getAttributes().keySet()) {
      Value attrValue = input.getEntry(attrName);
      nodeAttrs.put(attrName, attrValue);
    }

    node = new NodeValue(nodeType, nodeAttrs, portAttrs);
  }