StringBuilder appendTo(StringBuilder builder) {
   if (!hasNonPrototypeProperties()) {
     if (fn != null) {
       return fn.appendTo(builder);
     } else if (getNominalType() != null) {
       return getNominalType().appendTo(builder);
     }
   }
   if (nominalType != null && !nominalType.getName().equals("Function")) {
     nominalType.appendTo(builder);
   } else if (isStruct()) {
     builder.append("struct");
   } else if (isDict()) {
     builder.append("dict");
   }
   if (fn != null) {
     builder.append("<|");
     fn.appendTo(builder);
     builder.append("|>");
   }
   if (nominalType == null || !props.isEmpty()) {
     builder.append('{');
     boolean firstIteration = true;
     for (String pname : new TreeSet<>(props.keySet())) {
       if (firstIteration) {
         firstIteration = false;
       } else {
         builder.append(", ");
       }
       builder.append(pname);
       builder.append(':');
       props.get(pname).appendTo(builder);
     }
     builder.append('}');
   }
   if (isLoose) {
     builder.append(" (loose)");
   }
   return builder;
 }