Advent of SysML v2 | Lesson 8 – Packages and Names

Hello and welcome back to the Advent of SysML v2 – a daily, hands-on mini-course designed to bring you up to speed with the new SysML v2 standard and the modern Syside tooling that makes you productive from day one.

By the end of this lesson, you will:

  • Learn how to use packages to organize your SysML models
  • Learn about element names and how to refer to elements unambiguously

If you want to dive deeper into the world of SysML v2, check out The SysML v2 Book, where this topic is also discussed in more detail.

Packages

As your SysML v2 models grow larger, you will soon encounter two challenges:

  • How to organize models?
  • How to avoid name collisions?

The answer to both is packages. A SysML package works similarly to packages in other common languages: it provides a namespace and contains elements that belong together. Packages can contain other packages and any named element, so virtually anything you can declare.

Let us organize our model a bit into meaningful packages.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package SleighDesign {
part def SantaSleigh {
//…
}
}

package Entities {
part def Reindeer {
//…
}
}

package Datatypes {
enum def Color {
//…
}
}

package BaseComponents {
part def NaughtyNiceDashboard;

abstract part def CargoBay {
//…
}

abstract item def Bag {
//…
}
}

Already much nicer, right? In Syside, you can collapse and expand packages just like any other declaration with a body, which is already very useful. However, it is also good practice to keep packages in separate files, preferably with a matching name. Hierarchical packages are also okay and can be very useful. However, there is one limitation: a package must reside in a single file. Currently, there is no way to split it into multiple files.

Name Resolution and Imports

The reason to give something a name is to be able to refer to it. This is especially important in the textual notation, where there is no other accessible identifier. You will probably find yourself going back and giving names to previously unnamed elements when you want to refer to them – and this is fine.

So how can we refer to an element? This is where namespaces come into play. A namespace is a container for names, in which every name must be unique. As a good rule of thumb, every pair of braces (“{ }”) delineates a namespace.

Namespaces are hierarchical as they can contain other namespaces. The scope at a given point in the model can be defined as the union of the namespace in which we are and all the namespaces that contain it. If a name is present in multiple namespaces, our reference will be resolved to the innermost occurrence of it. So how do we refer to elements? If the target element is in the scope of the reference, and there is no element in an inner namespace with the same name, then it is sufficient to use its declared name as it appears in the file. In any other cases, we can use the element’s qualified name. This is obtained by joining a path of names with the “::” symbol, where the first name can be resolved in the current scope, and then each subsequent name is in the namespace belonging to the previous element. The last name will be the name of the element we want to refer to. A fully qualified name starts from the root namespace, which belongs to the file and has no name.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package SleighDesign {
private import BaseComponents::*;
part def SantaSleigh {
part cargoBay : CargoBay;
part dashboard : NaughtyNiceDashboard;

ref item santa;
abstract ref part reindeer : Entities::Reindeer[9];
}
}

package Entities {
public import ScalarValues::*;
part def Reindeer {
public import Datatypes::Color;
attribute energyLevel : Real;
constant attribute noseColor : Color;
}
}

package Datatypes {
enum def Color {
//…
}
}

package BaseComponents {
part def NaughtyNiceDashboard;

abstract part def CargoBay {
item payload [*] : Bag;
}

abstract item def Bag {
item gifts [*];
}
}

Looking at the example above, you can see a fully qualified name in line 8, where we say the “reindeer” usage is defined by the “Reindeer” definition in the “Entities” package.

While qualified names are definitely nice and useful (and they can resolve ambiguity when there are name collisions), they make the model very verbose. If we want to use the names from another namespace (typically a package) frequently, we can import names from it. You can see this in lines 2, 13, and 15. Each of these is interesting for a different reason.

The import in line 2 is one of the most common ways to import things. It specifies something like a qualified name, but uses a star (“*”) as a wildcard to mean that we want to import all the names from the namespace specified before it. In this case, we can use all the names from the package “BaseComponents” as if they were declared locally. You can see this in lines 4 and 5. A drawback of this is that we may have a lot of name collisions if the imported namespace has a large number of names.

The import in line 13 does the same, but it says “public” instead of “private”. This means that the names we import into our namespace will also be visible in namespaces that import it. This again leads to pollution of the namespace, so it is better to use private by default – that will make the imported names available only in the importing namespace, and not in the ones importing it. Furthermore, you can also see that we could refer to the imported names from a nested namespace in line 16, where we use “Real” from the “ScalarValues” package.

Finally, the import in line 15 demonstrates how to import a specific name. This is also a very good practice to avoid polluting namespaces. With this import, only “Color” will be visible from “Datatypes” (imagine how many elements a real “Datatypes” package may have, such as “ScalarValues” that we keep importing). This line also highlights the fact that not only packages define a namespace – the definition “Reindeer” also has one, and we import only into that. This is again a good practice to avoid name pollution.

Names and Aliases

Almost every element in SysML may have a name. Actually, they can have many. We will look at some tricks with names – but be careful when you do these at home, as abusing name resolution may lead to brittle models.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package MagicalBagVariant {
private import BaseComponents::*;
part def <CargoBay> ‘Ordinary Cargo Bay’ specializes BaseComponents::CargoBay {
item redefines payload [0..1] : BottomlessGiftBag;
}
item def BottomlessGiftBag specializes Bag {
item redefines gifts;
}
}

package MagicalCargoBayVariant {
private import BaseComponents::*;
part def ‘Magical Cargo Bay’ specializes BaseComponents::CargoBay {
item redefines payload : OrdinaryBag;
}

alias CargoBay for ‘Magical Cargo Bay’;

item def OrdinaryBag specializes Bag {
item redefines gifts [100];
}
}

Take a look at the model above. In line 4, we can see two new things: a short name between “< >”, and an unrestricted name between single quotes. The short name is used to provide a more concise ID or abbreviation, making references less verbose. It is often used in conjunction with unrestricted names, which can contain any character, but special characters like newlines have to be escaped (for example, \n is a newline).

Another way to provide alternative names is through an alias, such as the one in line 20. Aliases are effective when they are in the scope, and they can refer to the original element by its fully qualified name. This way, we can even use them to import something and rename it for the current namespace, for example, to avoid name collisions.

Here comes the trick. Notice that both in the “MagicalBagVariant” and “MagicalCargoBayVariant” packages, we defined the alternative name of “CargoBay” for the special variant. This means that referring to “CargoBay” somewhere will be resolved depending on which package we import.

1
2
3
4
5
6
7
package ConfiguredSleigh {
part def SantaSleigh specializes SleighDesign::SantaSleigh {
private import MagicalBagVariant::*;
//private import MagicalCargoBayVariant::*;
part redefines cargoBay : CargoBay;
}
}

This is exactly what we exploit in the model above. The “ConfiguredSleigh” redefines the cargoBay usage to have the type “CargoBay” – and we import either one of the two packages where it is defined. 

This kind of “configuration management” can be convenient in the textual notation, but it will not work in any other format. Before using it, ensure that it fits your use case and that you do not have a better alternative.

Challenge for Tomorrow

Having mastered packages and names, you can now organize your models much better. You should already have all your models from the previous lessons merged into a single file – now it is time to do it better. Go to Syside Cloud and do the following tasks:

  1. Reorganize your model into packages. You may place packages in different files.
  2. Revise the names in your model. If you prefer, introduce unrestricted names and corresponding short names.
  3. (Optional) Experiment with using imports to resolve a name to different elements.

Summary

In this episode, you learned about names and packages, which provide a way to efficiently organize your model. If you haven’t yet done so, it is now time to go to Syside Cloud to do the challenge – and don’t forget to come back tomorrow for another episode of the Advent of SysML v2!

Cookies