Skip to content

Attributes and Subscript expressions#

Code Example

Runnable Example in Jac and JacLib

"""Attributes and Subscript expressions: Index access, slicing, and member access."""

obj Sample {
    has items: list = [10, 20, 30, 40, 50],
        data: dict = {"name": "Alice", "age": 30},
        value: int = 42;
}

with entry {
    s = Sample();

    # Dot notation for member access
    val = s.value;

    # Subscripted access for lists and dicts
    item1 = s.items[0];
    item2 = s.items[-1];
    name = s.data["name"];

    # Slicing
    slice1 = s.items[1:4];
    slice2 = s.items[:3];
    slice3 = s.items[2:];

    # Chained access
    first_char = s.data["name"][0];

    # Null-safe access with ?
    safe_val = s?.value;

    # More null-safe access examples
    optional_obj = None;
    null_safe1 = optional_obj?.value;  # Returns None instead of error
    null_safe2 = optional_obj?.items;  # Returns None instead of error

    # Chained null-safe access
    obj_with_data = Sample();
    nested_val = obj_with_data?.data;  # Safe access returns the dict

    # Multiple null-safe operators in a chain
    another_none = None;
    safe_chain = another_none?.value?.items;  # Returns None at first ?

    # Null-safe on valid object
    valid_obj = Sample();
    safe_on_valid = valid_obj?.items;  # Returns [10, 20, 30, 40, 50]

    print(val, item1, item2, name, slice1, slice2, slice3, first_char, safe_val);
    print("Null-safe:", null_safe1, null_safe2, nested_val, safe_chain, safe_on_valid);
}
"""Attributes and Subscript expressions: Index access, slicing, and member access."""

obj Sample {
    has items: list = [10, 20, 30, 40, 50],
        data: dict = {"name": "Alice", "age": 30},
        value: int = 42;
}

with entry {
    s = Sample();

    # Dot notation for member access
    val = s.value;

    # Subscripted access for lists and dicts
    item1 = s.items[0];
    item2 = s.items[-1];
    name = s.data["name"];

    # Slicing
    slice1 = s.items[1:4];
    slice2 = s.items[:3];
    slice3 = s.items[2:];

    # Chained access
    first_char = s.data["name"][0];

    # Null-safe access with ?
    safe_val = s?.value;

    # More null-safe access examples
    optional_obj = None;
    null_safe1 = optional_obj?.value;  # Returns None instead of error
    null_safe2 = optional_obj?.items;  # Returns None instead of error

    # Chained null-safe access
    obj_with_data = Sample();
    nested_val = obj_with_data?.data;  # Safe access returns the dict

    # Multiple null-safe operators in a chain
    another_none = None;
    safe_chain = another_none?.value?.items;  # Returns None at first ?

    # Null-safe on valid object
    valid_obj = Sample();
    safe_on_valid = valid_obj?.items;  # Returns [10, 20, 30, 40, 50]

    print(val, item1, item2, name, slice1, slice2, slice3, first_char, safe_val);
    print("Null-safe:", null_safe1, null_safe2, nested_val, safe_chain, safe_on_valid);
}
"""Subscripted and dotted expressions: Index access, slicing, and member access."""
from __future__ import annotations
from jaclang.runtimelib.builtin import *
from jaclang import JacMachineInterface as _jl

class Sample(_jl.Obj):
    items: list = _jl.field(factory=lambda: [10, 20, 30, 40, 50])
    data: dict = _jl.field(factory=lambda: {'name': 'Alice', 'age': 30})
    value: int = 42
s = Sample()
val = s.value
item1 = s.items[0]
item2 = s.items[-1]
name = s.data['name']
slice1 = s.items[1:4]
slice2 = s.items[:3]
slice3 = s.items[2:]
first_char = s.data['name'][0]
safe_val = __jac_tmp.value if (__jac_tmp := s) else None
optional_obj = None
null_safe1 = __jac_tmp.value if (__jac_tmp := optional_obj) else None
null_safe2 = __jac_tmp.items if (__jac_tmp := optional_obj) else None
obj_with_data = Sample()
nested_val = __jac_tmp.data if (__jac_tmp := obj_with_data) else None
another_none = None
safe_chain = __jac_tmp.items if (__jac_tmp := (__jac_tmp.value if (__jac_tmp := another_none) else None)) else None
valid_obj = Sample()
safe_on_valid = __jac_tmp.items if (__jac_tmp := valid_obj) else None
print(val, item1, item2, name, slice1, slice2, slice3, first_char, safe_val)
print('Null-safe:', null_safe1, null_safe2, nested_val, safe_chain, safe_on_valid)
Jac Grammar Snippet
atomic_chain: atomic_chain NULL_OK? (filter_compr | assign_compr | index_slice)
            | atomic_chain NULL_OK? (DOT_BKWD | DOT_FWD | DOT) named_ref
            | (atomic_call | atom | edge_ref_chain)

index_slice: LSQUARE                                                             \
                     expression? COLON expression? (COLON expression?)?          \
                     (COMMA expression? COLON expression? (COLON expression?)?)* \
              RSQUARE
           | list_val

Description

Subscripted and Dotted Expressions

Subscripted and dotted expressions provide syntax for accessing collection elements and object attributes, forming the foundation of data access in Jac.

Object Definition

Lines 3-7 define a Sample object with three attributes: - items: a list initialized to [10, 20, 30, 40, 50] - data: a dictionary initialized to {"name": "Alice", "age": 30} - value: an integer initialized to 42

Line 10 creates an instance of this object for demonstration.

Dotted Expressions (Attribute Access)

Line 13 demonstrates dot notation: val = s.value;

The dot operator . accesses an attribute on an object: - s - The object instance - .value - The attribute name - Result: 42

This is the standard way to access object attributes in Jac.

Subscripted Expressions (Index/Key Access)

Lines 16-18 demonstrate subscript notation with square brackets [...]:

Line Expression Type Result
16 s.items[0] List index 10 (first element)
17 s.items[-1] Negative index 50 (last element)
18 s.data["name"] Dictionary key "Alice"

For lists, subscripts use zero-based integer indexing. Negative indices count from the end (-1 is the last element).

For dictionaries, subscripts use keys to access associated values.

Slicing Syntax

Lines 21-23 demonstrate slice operations on lists:

Line Slice Start End Result
21 s.items[1:4] 1 4 [20, 30, 40] (indices 1, 2, 3)
22 s.items[:3] 0 3 [10, 20, 30] (beginning to index 3)
23 s.items[2:] 2 end [30, 40, 50] (index 2 to end)

Slice syntax is [start:end] where: - start is inclusive (included in result) - end is exclusive (not included in result) - Omitting start defaults to beginning (0) - Omitting end defaults to end of list

Chained Access

Line 26 shows chaining operations: first_char = s.data["name"][0];

Execution left-to-right: 1. s.data - Access data attribute (returns dictionary) 2. ["name"] - Get value for key "name" (returns "Alice") 3. [0] - Get first character (returns "A")

Chaining allows you to access nested data structures in a single expression.

Null-Safe Access

Line 29 demonstrates null-safe access: safe_val = s?.value;

The ?. operator: - Accesses the attribute if the object is not None - Returns None if the object is None (instead of raising an error) - Useful for optional attributes or nullable objects

This prevents AttributeError when the object might be None.

More Null-Safe Access Examples

Lines 31-46 provide additional null-safe access scenarios:

Lines Example Description Result
32-33 optional_obj = None;
null_safe1 = optional_obj?.value;
Null-safe on None object None (no error)
34 null_safe2 = optional_obj?.items; Accessing different attribute None (no error)
37-38 obj_with_data = Sample();
nested_val = obj_with_data?.data;
Null-safe on valid object {"name": "Alice", "age": 30}
41-42 another_none = None;
safe_chain = another_none?.value?.items;
Chained null-safe operators None (stops at first ?)
45-46 valid_obj = Sample();
safe_on_valid = valid_obj?.items;
Null-safe returns value when valid [10, 20, 30, 40, 50]

Chained Null-Safe Behavior

When using multiple ?. operators in a chain (line 42): - Evaluation proceeds left-to-right - If any object in the chain is None, the entire expression returns None - No error is raised; execution continues safely - Example: another_none?.value?.items returns None because another_none is None

Subscript Type Behaviors

Different types handle subscripts differently:

Type Subscript Example Result
List Integer index [10, 20, 30][1] 20
Tuple Integer index (10, 20, 30)[1] 20
String Integer index "hello"[1] "e"
Dictionary Key {"a": 1}["a"] 1

Negative indices work for ordered sequences (lists, tuples, strings) but not dictionaries.

Complete Example Flow

Lines 48-49 print all the values extracted throughout the example:

First print statement (line 48): - val = 42 - item1 = 10 - item2 = 50 - name = "Alice" - slice1 = [20, 30, 40] - slice2 = [10, 20, 30] - slice3 = [30, 40, 50] - first_char = "A" - safe_val = 42

Second print statement (line 49) - Null-safe examples: - null_safe1 = None (accessing .value on None) - null_safe2 = None (accessing .items on None) - nested_val = {"name": "Alice", "age": 30} (safe access on valid object) - safe_chain = None (chained null-safe stops at first None) - safe_on_valid = [10, 20, 30, 40, 50] (null-safe on valid object)

Common Patterns

Combining dot and subscript notation enables powerful data access:

These expressions form the basis for navigating complex data structures in Jac programs.