Expression Language

LinkML Map uses a safe subset of Python for expressions in slot derivations. Expressions are evaluated using simpleeval, which provides sandboxed evaluation of Python-like expressions.

Basic Usage

Use expr in a slot derivation to compute a value from source data:

class_derivations:
  Individual:
    populated_from: Person
    slot_derivations:
      full_name:
        expr: "{given_name} + ' ' + {family_name}"
      is_adult:
        expr: "age >= 18"
      label:
        expr: "str(id) + ': ' + name"

Null Propagation with {variable}

Wrapping a variable in curly braces {x} enables null propagation: if the variable is None, the entire expression returns None instead of raising an error.

slot_derivations:
  full_name:
    expr: "{given_name} + ' ' + {family_name}"
given_name family_name Result
"Jane" "Doe" "Jane Doe"
None "Doe" None
None None None

Bare variables (without braces) do not trigger null propagation, which is useful for conditional expressions:

slot_derivations:
  status:
    expr: "'active' if start_date else 'pending'"

Available Functions

Type Conversion

These functions distribute over lists — applying them to a list applies them to each element.

Function Description Example
str(x) Convert to string str(42) → "42"
int(x) Convert to integer int("42") → 42
float(x) Convert to float float("3.14") → 3.14
bool(x) Convert to boolean bool(1) → True

Math and String

Function Description Example
abs(x) Absolute value abs(-5) → 5
round(x) Round to nearest integer round(3.7) → 4
strlen(x) String length strlen("hello") → 5

Aggregation

These operate on lists as a whole (they do not distribute):

Function Description Example
max(items) Maximum value max([1, 3, 2]) → 3
min(items) Minimum value min([1, 3, 2]) → 1
len(items) Number of items len([1, 2, 3]) → 3

Conditional

slot_derivations:
  category:
    expr: "case((age < 18, 'minor'), (age < 65, 'adult'), (True, 'senior'))"

The case() function takes pairs of (condition, value) and returns the value for the first true condition.

ID Generation

slot_derivations:
  uuid_id:
    expr: 'uuid5("https://example.org/Person", {id})'

uuid5(namespace, name) generates a deterministic UUID v5 (RFC 4122). The same namespace and name always produce the same UUID, making it suitable for generating stable identifiers during transformation. The name argument must be a string — use str() to convert if needed (e.g. uuid5("https://example.org/Person", str({id}))).

Collection Distribution

When accessing attributes on a collection, the access distributes over each element:

slot_derivations:
  all_person_names:
    expr: "persons.name"          # Returns list of names
  name_lengths:
    expr: "strlen(persons.name)"  # Returns list of string lengths

Numeric Coercion

When comparing values from tabular sources (TSV/CSV), string values that look like numbers are automatically coerced for comparison operators. This means expressions like age > 18 work correctly even when age arrives as the string "25" from a TSV file.

Unrestricted Eval Mode

By default, expressions are evaluated in a sandboxed environment that supports single-expression Python. For complex multi-statement logic (list comprehensions with filters, if/else blocks, variable assignment), you can enable unrestricted_eval mode:

transformer = ObjectTransformer(unrestricted_eval=True)

This falls back to asteval for expressions that exceed simpleeval's capabilities. In unrestricted mode, complex expressions use a target = ... convention to set the result, and src to access the full source object:

slot_derivations:
  driving_test_date:
    expr: |
      d_test = [x.important_event_date for x in src.has_important_life_events
                if str(x.event_name) == "PASSED_DRIVING_TEST"]
      if len(d_test):
          target = d_test[0]

Security

Unrestricted eval allows arbitrary Python execution within asteval's sandbox. Only use this with trusted transformation specifications. It can be enabled programmatically (e.g. ObjectTransformer(unrestricted_eval=True)) or via the --unrestricted-eval / --no-unrestricted-eval flags on linkml-map map-data.

Operators

Standard Python operators are supported: