name: mps-aspect-behavior
description: Use when defining or editing MPS ConceptBehavior — per-concept methods (non-virtual / virtual / abstract / static / virtual static), constructors, virtual dispatch (MRO), super and interface-default calls (super<Interface>.method), overriding methods from lang.core.behavior interfaces such as ScopeProvider.getScope / INamedConcept.getName / BaseConcept.getPresentation, calling sibling methods (LocalBehaviorMethodCall) and behavior methods from other aspects via node.method(...). Reach for this skill whenever the task involves authoring or modifying <lang>/languageModels/behavior.mps.
type: reference
MPS Behavior Aspect
The behavior aspect attaches methods and a constructor to a concept, much like adding methods to a Java class. Bodies are written in BaseLanguage + smodel and are callable from any other aspect (editor, constraints, typesystem, generator, intentions, plugin) via node.methodName(...). Lives in <lang>/languageModels/behavior.mps, language jetbrains.mps.lang.behavior.
Critical Directives
- One
ConceptBehaviorroot per concept. Theconstructorchild is mandatory (an empty body is fine). - Constructors run before the node is attached to the model.
parent, ancestors, and all children/descendants evaluate tonull. Constructors can only set own property/reference defaults and add mandatory children — use aNodeFactory(actions aspect) for anything that needs the parent/model context. - Quotations (
<C()>) bypass behavior constructors. Usenew node<C>()oradd new initialized Cwhen you need the constructor to fire. - Constructors are not inherited — each concept has at most one, and they do not chain (no
super()). MPS runs each ancestor's constructor in ancestor-first order independently. - An overriding method must match the signature of the supertype/interface method exactly. When implementing an interface method (e.g.
ScopeProvider.getScope), setoverriddenMethodon the new method to the interface declaration — MPS uses it for dispatch and signature validation. - Behavior calls on a
nullnode do not NPE — MPS returnsnullfor reference/String returns and the default for primitives. Still handle thenullreturn for reference types. - Hoist shared methods to an abstract super-concept rather than duplicating per-sub-concept
ConceptBehaviorroots — virtual dispatch covers all non-abstract sub-concepts. (Seereferences/inheritance-and-dispatch.md.) - Methods are non-virtual by default: all modifier booleans (
isVirtual,isAbstract,isStatic,isFinal) default tofalse. Onlyvirtualmethods can be overridden; mark a methodvirtualup front if sub-concepts may ever need to specialise it. - When the same piece of node-handling logic repeats across several places or several aspects of one concept (editor, constraints, typesystem, generator, intentions, textgen), extract it into a behavior method and call it everywhere via
node.m(...)— this de-duplication is the behavior aspect's primary purpose. Pick the modifier with the table below. - Edit behavior models through MPS MCP tools (
mps_mcp_insert_root_node_from_json,mps_mcp_update_node,mps_mcp_parse_java_and_insert). Do not hand-edit.mpsfiles. - For MPS-typed return types (
sequence<node<X>>,list<node<X>>),mps_mcp_parse_java_and_insertproduces JavaList<SNode>— replacereturnTypeafterwards with the correct MPS blueprint (seemps-model-manipulation/references/variable-declarations.md). - After edits run
mps_mcp_check_root_node_problemsand rebuild the language.
Common-Path Workflow
- Create a
behaviormodel if missing (mps_mcp_create_modelwithmodelName: "<lang>.behavior"— aspect IDbehavior, case-sensitive, no@suffix; see aspect-model-stereotypes.md). Use languages:jetbrains.mps.lang.behavior, plus any languages referenced in bodies (smodel,collections,closures,baseLanguage). - Add a
ConceptBehaviorroot for the target concept; setconceptref. The minimal blueprint (with the mandatory emptyconstructor) is inreferences/json-blueprints.md. - Add
ConceptConstructorDeclaration(at most one) and/orConceptMethodDeclarationchildren. - Write bodies. For non-trivial logic prefer
mps_mcp_parse_java_and_insertwithfeatureKind: "STATEMENTS"or"METHOD", then fix MPS-typed return/parameter types. - Validate with
mps_mcp_check_root_node_problems, rebuild the language.
Method Modifier Quick Reference
| Modifier | Call shape | Dispatch / inheritance | Notes |
|---|---|---|---|
| (none) — non-virtual (default) | node.m(...) |
Inherited by sub-concepts but statically bound — cannot be overridden | A same-named method in a sub-concept's behavior shadows it (hazard) instead of overriding. Most Classifier_Behavior utilities in baseLanguage are non-virtual. |
virtual |
node.m(...) |
Dispatched at runtime by the node's actual concept; overridable in sub-concept behaviors | Set isVirtual: true explicitly. E.g. Expression.isLValue and every method of Type_Behavior in baseLanguage. |
abstract |
node.m(...) (virtually dispatched) |
No body; every non-abstract sub-concept must provide an implementation | Implies virtual — set both isAbstract and isVirtual. Declare on abstract/interface concepts (e.g. Classifier.findAncestor, IMemberContainer.getMembers in baseLanguage). |
final |
node.m(...) |
A virtual method that cannot be overridden further down | Rarely needed — a plain non-virtual method is already non-overridable. |
static |
ConceptName.m(...) (qualified by concept name) |
Belongs to the concept; no this; no dispatch |
Concept-wide utilities, e.g. Classifier.getContextClassifier in baseLanguage. Called as LocalBehaviorMethodCall when unqualified inside the same ConceptBehavior. |
virtual static |
conceptValue.m(...) on a concept<X> expression |
Dispatched by the runtime concept value; overridable in sub-concept behaviors (link overriddenMethod) |
Per-concept (not per-node) polymorphism — e.g. Expression.getPrecedenceLevel, Type.isValueType in baseLanguage, overridden across the smodel/collections behaviors. |
Choosing a modifier for an extracted method
- Invoked on a node (needs
this, properties, children)? → instance method:virtualif sub-concepts must be able to override it (or the logic varies by concept), non-virtual for a fixed helper. - Invoked on a concept (no node instance at hand, concept-wide utility, or nodes passed as parameters)? →
static; make itvirtual staticwhen the result must vary per concept (dispatch on aconcept<X>value). - Shared by several sub-concepts? → declare it once on their common (abstract) super-concept or interface; use
abstract(+virtual) when only sub-concepts can supply the body.
Related Skills
mps-aspect-actions—NodeFactoryis the right place for initialization that needs parent/model context (constructors run before the node is attached).mps-aspect-intentions,mps-aspect-constraints,mps-aspect-generator,mps-aspect-typesystem— call behavior methods vianode.m(...); behavior is one of the most-used aspects from these.mps-model-manipulation— full BaseLanguage / smodel / collections reference. Covers theLinkList_AddNewChildOperationfamily and theList<SNode>→sequence<node<X>>return-type fix.mps-aspect-constraints—ScopeProvider.getScopeis most often overridden in a behavior; constraints describe where the reference lives.mps-aspect-structure-concepts— when introducing the concept that the behavior attaches to.mps-language-inheritance— to design the abstract-super-concept refactors discussed inreferences/inheritance-and-dispatch.md.mps-quotations— quotations bypass behavior constructors; document the workaround in the generator.
Reference Index
- Open
references/method-declarations.mdwhen authoring aConceptMethodDeclaration— modifiers, parameters,overriddenMethod,BaseConceptoverrides (getPresentation/getSideIcon), implicit-return rule, and the MPS-typed return-type Java-parser caveat. - Open
references/local-and-super-calls.mdwhen calling sibling methods of the sameConceptBehavior(LocalBehaviorMethodCall), distinguishing unqualifiedm(args)fromthis.m(args)(which generatesDotExpression { ThisNodeExpression, Node_ConceptMethodCall }), or callingsuper<Interface>.method(...)— includes the verbatim Stateful and KajaCommentLine_Behaviorexamples and theStateful_Behavior.getScopesuper-call JSON. - Open
references/inheritance-and-dispatch.mdwhen designing or debugging virtual dispatch — the MRO algorithm (own → extended super → interfaces in definition order), how a method on an abstract super-concept covers every concrete sub-concept (Stateful pattern), and when to hoist shared behavior up vs. duplicate per concept. - Open
references/constructors.mdwhen writing aConceptConstructorDeclaration— what runs and what does not run a constructor, the "node not yet attached" constraint, scalar-default and seed-mandatory-child examples (ChemMasteryCompound_BehaviorandChemEquation_Behavior) with full JSON blueprints, and the surface→FQN mapping for the surface syntax involved. - Open
references/calling-from-other-aspects.mdwhen calling a behavior method from generator templates, typesystem rules, constraints, intentions, or hand-written Java — the descriptor invocation shape, the_idXXXXXstable suffix, and the null-safety behavior ofnode.method(). - Open
references/json-blueprints.mdwhen inserting a behavior root or a method via MCP — minimalConceptBehaviorwith the mandatory empty constructor, theConceptMethodDeclarationskeleton, and the validated concept ref. - Open
references/lang-core-behavior-overrides.mdfor the validated node refs to set asoverriddenMethodwhen implementing commonlang.core.behaviormethods (getTextualRepresentation,isTODOComment,getScope), plus theIGenericCommentinterface node ref used insuper<IGenericComment>.isTODOComment()calls. - Open
references/common-failures.mdwhen a behavior method isn't found, an override is ignored, a constructor's defaults don't stick, a descriptor returnsObject, the return type fights the Java parser, or quotations bypass the constructor.