Visibility (4 niveles), Multiplicity (4 constructores) y una RELATION SCHEMA ParticipatesSchema que combina ambos sobre el caso Person × Meeting.
Validación: las tres specs pasan el analyzer con 0 errores. Solo queda 1 warning V1 sobre schemaConforms (operación derivada con LHS de variable libre — falso positivo conocido del workspace).
3. ParticipatesSchema — RELATION SCHEMA Person × Meeting
Esta spec aplica los dos tipos anteriores al caso Person&Meeting del libro. Mantiene los axiomas algebraicos estándar (create, addLink, isRelated, get_meetings, get_participants, etc.) y agrega metadata de schema que se observa con operaciones a nivel-asociación: role1/role2, mult1/mult2, visibility1/visibility2, isFrozen/isChangeable/isAddOnly.
ParticipatesSchema.nereus (extracto)
RELATION SCHEMA ParticipatesSchema
IMPORTS Person, Meeting, Multiplicity, Visibility,
Set[Person:ANY], Set[Meeting:ANY]
BASIC CONSTRUCTORS create, addLink
EFFECTIVE
TYPE ParticipatesSchema
OPERATIONS
-- Constructores algebraicos.
create: -> ParticipatesSchema;
addLink: ParticipatesSchema(a) * Person(p) * Meeting(m)
-> ParticipatesSchema
pre: not isRelated(a, p, m);
-- Metadata del schema.
role1, role2: ParticipatesSchema -> String;
mult1, mult2: ParticipatesSchema -> Multiplicity;
visibility1, visibility2: ParticipatesSchema -> Visibility;
isFrozen, isChangeable, isAddOnly: ParticipatesSchema -> Boolean;
-- Predicados y cardinalidades observadas.
isRelated: ParticipatesSchema * Person * Meeting -> Boolean;
isRightLinked: ParticipatesSchema * Person -> Boolean;
isLeftLinked: ParticipatesSchema * Meeting -> Boolean;
rightCardinality: ParticipatesSchema * Person -> Integer;
leftCardinality: ParticipatesSchema * Meeting -> Integer;
-- Navegación.
get_meetings: ParticipatesSchema * Person -> Set[Meeting];
get_participants: ParticipatesSchema * Meeting -> Set[Person];
-- Remover y verificar conformidad al schema.
remove: ParticipatesSchema(a) * Person(p) * Meeting(m)
-> ParticipatesSchema
pre: isRelated(a, p, m) and not isFrozen(a) and not isAddOnly(a);
schemaConforms: ParticipatesSchema * Person * Meeting -> Boolean;
AXIOMS a: ParticipatesSchema; p, p1: Person; m, m1: Meeting;
-- ─── Metadata del schema (constantes por instancia) ──
role1(create()) = "participant";
role1(addLink(a, p, m)) = role1(a);
role2(create()) = "scheduled_meeting";
role2(addLink(a, p, m)) = role2(a);
mult1(create()) = Mult_AtLeast(1); -- 1..*
mult1(addLink(a, p, m)) = mult1(a);
mult2(create()) = Mult_Star(); -- 0..*
mult2(addLink(a, p, m)) = mult2(a);
visibility1(create()) = Vis_Public();
visibility1(addLink(a, p, m)) = visibility1(a);
visibility2(create()) = Vis_Public();
visibility2(addLink(a, p, m)) = visibility2(a);
isFrozen(create()) = False;
isFrozen(addLink(a, p, m)) = isFrozen(a);
isChangeable(create()) = True;
isChangeable(addLink(a, p, m))= isChangeable(a);
isAddOnly(create()) = False;
isAddOnly(addLink(a, p, m)) = isAddOnly(a);
-- ─── Conformidad al schema ────────────────────────────
-- schemaConforms(a, p, m): la cardinalidad observada de p
-- en a cumple mult2, y la de m en a cumple mult1.
schemaConforms(a, p, m) =
mult_accepts(mult2(a), rightCardinality(a, p))
and
mult_accepts(mult1(a), leftCardinality(a, m));
-- (Los axiomas de isRelated, get_meetings, get_participants,
-- cardinalidades, remove, etc. siguen el patrón estándar
-- de ASSOCIATION; ver el archivo completo.)
END-CLASS
4. Ejemplo concreto: tres Persons, dos Meetings, tres links
Para ilustrar cómo se usa schemaConforms, simulo una sesión:
Construcción
Valor algebraico
Visión semántica
Empty
a0 = create()
{}
Insert (Alice, M1)
a1 = addLink(a0, Alice, M1)
{ (Alice, M1) }
Insert (Bob, M1)
a2 = addLink(a1, Bob, M1)
{ (Alice, M1), (Bob, M1) }
Insert (Alice, M2)
a3 = addLink(a2, Alice, M2)
{ (Alice, M1), (Bob, M1), (Alice, M2) }
Evaluación de metadata y conformidad sobre a3
Expresión
Resultado
Por qué
role1(a3)
"participant"
Constante propagada por axiomas; no depende de los links.
mult1(a3)
Mult_AtLeast(1)
Constante. Cada reunión necesita ≥1 participante.
mult2(a3)
Mult_Star()
Constante. Una persona puede tener 0 o más reuniones.
visibility1(a3)
Vis_Public()
Constante. Se puede leer la asociación libremente.
rightCardinality(a3, Alice)
2
Alice está en M1 y M2.
rightCardinality(a3, Bob)
1
Bob solo en M1.
leftCardinality(a3, M1)
2
M1 tiene a Alice y Bob.
leftCardinality(a3, M2)
1
M2 solo tiene a Alice.
mult_accepts(Mult_AtLeast(1), 2)
True
M1 cumple mult1 (≥1 participante). ✓
mult_accepts(Mult_Star(), 2)
True
Alice cumple mult2 (0..*). ✓
schemaConforms(a3, Alice, M1)
True
Ambas cardinalidades dentro del rango. ✓
schemaConforms(a3, Alice, M2)
True
1 ∈ [1..∞], 2 ∈ [0..∞]. ✓
Caso de violación: agregar (Carol, M3) donde M3 no tiene a nadie aún
Si hubiéramos construido a4 = addLink(create(), Carol, M3) directamente (sin más links), la cardinalidad izquierda de M3 es 1, lo que cumplemult1 = 1..*. Pero si por algún motivo se removiera ese único link (remove(a4, Carol, M3)) y M3 quedara con 0 participantes, leftCardinality daría 0, y mult_accepts(Mult_AtLeast(1), 0) = False — el schema dejaría de conformar. Ese chequeo se hace en runtime con schemaConforms.
5. Limitaciones del parser detectadas
1. Literales enteros negativos no soportados.-1 falla en el parser; usamos 0 - 1 como workaround para la convención de "cota superior infinita". El significado es el mismo (el evaluador computa la resta antes de comparar).
2. END-RELATION no reconocido. Aunque el parser sí acepta el header RELATION SCHEMA ..., no acepta el cierre simétrico END-RELATION. Como workaround, las specs RELATION SCHEMA deben cerrar con END-CLASS. Esta es una asimetría sintáctica del analyzer que vale la pena reportar — la misma asimetría aparece en Bidirectional_3.nereus con el mismo error.
3. Concatenación de strings (+ sobre String). No está soportada. mult_render(Mult_Range(1, 5)) = intToString(1) + ".." + intToString(5) falla. Por eso eliminé mult_render: el rendering UML se hace en el target (Haxe/C++/etc.) si hace falta.