{
    "app": {
        "name": "magik",
        "description": "This skill should be used when the user is writing, reviewing, or asking questions about Magik code — the programming language used in GE Smallworld GIS. Use this skill when the user mentions Magik, Smallworld, exemplars, def_slotted_exemplar, _method, _pragma, sw:rope, sw:property_list, or any Smallworld-specific constructs. Also use it when the user asks about GIS application development, Smallworld modules, or Java interop in a Magik context.",
        "mode": "advanced-chat",
        "model_config": {
            "provider": "deepseek",
            "model": "deepseek-chat",
            "parameters": {
                "temperature": 0.7,
                "max_tokens": 4096
            }
        }
    },
    "instructions": "name magik description This skill should be used when the user is writing, reviewing, or asking questions about Magik code — the programming language used in GE Smallworld GIS. Use this skill when the user mentions Magik, Smallworld, exemplars, def_slotted_exemplar, _method, _pragma, sw:rope, sw:property_list, or any Smallworld-specific constructs. Also use it when the user asks about GIS application development, Smallworld modules, or Java interop in a Magik context. What Is Magik? Magik is a dynamically typed, object-oriented programming language built for Smallworld GIS (now GE Smallworld Geo Network Management). It compiles to Java bytecode and runs on the JVM. It is strictly OO and imperative — similar in spirit to Smalltalk — and is used for enterprise GIS applications in utilities and telecoms. Core Syntax Comments # This is a single-line comment ## This is a method documentation comment (used by tooling) Assignment a << 1.234 # \"a becomes 1.234\" b +<< a # b << b + a (compound assignment) Never use = for assignment. = is equality comparison only. Output write(\"Hello, world!\") # prints with newline show(some_variable) # inspect/debug print Data Types # Integer x << 42 # Float y << 3.14 # String name << \"Alice\" # Symbol (unique token, like an interned string — used heavily for identifiers) sym << :my_symbol escaped_sym << :|hello world| # Boolean — NOTE: _true and _false, NOT true/false flag << _true flag << _false # Null equivalent nothing << _unset # like null/nil in other languages # Character literal ch << %A # the character 'A' # Simple vector (array literal) v << {1, 2, 3} # Property list (ordered key-value, preserves insertion order) pl << sw:property_list.new_with(:key1, \"val1\", :key2, \"val2\") # Hash table (unordered) ht << sw:hash_table.new() # Concurrent hash map (preferred in Smallworld 5 — thread-safe and fastest) chm << sw:concurrent_hash_map.new() Boolean Keywords Magik uses keyword booleans — NEVER use bare true / false : _true _false _maybe # tri-state Kleenean: _true, _maybe, or _false Methods returning a Kleenean result are named with ?? suffix (e.g. inside??() ). Do not use the result directly as a boolean condition — compare explicitly with _is _true / _is _false / _is _maybe . Logical operators: _and _or _not _xor # always evaluate both sides _andif _orif _xorif # short-circuit — skip RHS if LHS decides the result Use the short-circuit forms whenever the RHS depends on the LHS being safe, e.g. a null-check before a method call: _if x _isnt _unset _andif x.valid? _then ... _endif Variables Local variables must be declared with _local : _local my_count << 0 _local result << _unset Global variables use _global (avoid in production — causes namespace pollution): _global my_global << \"some value\" Dynamic (thread-local) variables: _dynamic !my_dynamic! Convention: use snake_case with descriptive nouns. No camelCase. No type prefixes. Naming Conventions Methods and Procedures Consistency: The same function should have the same name across classes; different functions should have different names. Get/set pairs: If an attribute is read via foo , its setter must be named foo<< . Boolean-returning routines end in ? and can be used directly as conditions: _if i.odd? _then ... _endif Kleenean-returning routines (those that may return _true , _false , or _maybe ) end in ?? . Compare explicitly rather than using as a bare condition: # inside?? returns _true if strictly inside, _false if any part outside, _maybe if on edge _if rect.inside??(other_rect) _is _true _then ... _endif Friend / internal-public methods that cannot be strictly private should contain ! within their names (e.g. int!method() ). This makes them easy to identify and allows special treatment by method browsers. All such methods must use classify_level=restricted . Slot-like methods (no side effects, always returns the same single result on an unmodified object) must be defined without brackets. Methods whose primary purpose is not slot-like access must include brackets. Note that brackets are part of the method name — sqrt and sqrt() are entirely different methods: # Slot-like — no brackets (behaves like reading a property): my_obj.name my_obj.size # Not slot-like — with brackets (does work, has side effects, or returns multiple values): my_obj.calculate() my_obj.open_stream() Arguments Argument names should indicate role or type. Use an indefinite article prefix for names indicating type: a_coordinate , a_rope , an_integer . If multiple arguments have the same type, append numbers: new_with_corners(coord1, coord2) . Dynamic Variables Dynamic variable names must start and end with ! : !output! , !print_length! . Class Design Single initialisation method: Define only one init() method per class. Multiple initialisers make subclassing much harder, as every subclass must find and override each one. Link related classes via shared constants rather than accessing globals — this improves efficiency and simplifies subclassing: my_object.define_shared_constant(:related_class, other_exemplar, :public) $ Conditionals _if condition _then # ... _endif _if x > 0 _then write(\"positive\") _elif x = 0 _then write(\"zero\") _else write(\"negative\") _endif Note: Use _is for identity (same object reference), = for value equality: _if a _is _unset _then write(\"a is null\") _endif _if a = b _then write(\"a equals b\") _endif Formatting: In a multi-line _if , place a newline after each _then , _else , _and , _or , _andif , _orif , _xorif . Expression form / method chaining: _if can be used as an expression and a method called directly on _endif : result << _if cond _then >> a _else >> b _endif.write_string Loops # While loop _local i << 0 _while i < 10 _loop i +<< 1 _endloop # For-over loop (iterate a collection) _for item _over my_collection.elements() _loop write(item) _endloop # For-over with index and value _for k, v _over my_property_list.fast_keys_and_elements() _loop write(k, \" => \", v) _endloop # General loop with _leave to break, _continue to skip _loop _if some_condition _then _leave _endif _if skip_condition _then _continue _endif _endloop Procedures (Standalone Functions) Procedures are first-class objects, assigned to variables: my_procedure << _proc @my_procedure(a, b, c) _return a + b + c _endproc x << my_procedure(1, 2, 3) # x = 6 >> sets the value of its enclosing code block. It is not a short form of _return : >> is positionally restricted — it must be the last statement of its block, and there is at most one per block. The block's value flows outward; it becomes the method's result only when the block in question is the method body itself. _return val , by contrast, is unrelated to block structure: it exits the enclosing method or procedure immediately, cutting through any nested _if / _block / _loop . _pragma(classify_level=basic) _method my_obj.classify(x) ## Returns a label for X. _local label << _if x > 0 _then >> \"positive\" # value of the _then arm; the _if evaluates to this _else >> \"non-positive\" # value of the _else arm _endif _if x.is_nan?() _is _true _then _return \"not a number\" # immediate exit — skips everything below _endif >> label # last statement of the method body — method's result _endmethod $ Use >> when the result falls out naturally at the end of a block; use _return to exit a method early from anywhere inside it. Multiple Return Values A method or procedure can yield a tuple of values — either as the final >> of its body, or via an early _return : _method my_obj.split_name(full) ## Splits FULL into first and last. Returns first, last. _local idx << full.index_of(% ) >> full.slice(1, idx - 1), full.slice(idx + 1) _endmethod $ The caller has three ways to consume a multi-valued return: # 1. Take the first value, drop the rest first << obj.split_name(\"Ada Lovelace\") # 2. _scatter — spread the tuple into positional locals on the LHS (first, last) << (_scatter obj.split_name(\"Ada Lovelace\")) # 3. _allresults — collect the whole tuple into a simple_vector parts << _allresults obj.split_name(\"Ada Lovelace\") # → {\"Ada\", \"Lovelace\"} _scatter also works in the other direction — spread a vector into positional arguments of a call: args << {1, 2, 3} obj.configure(_scatter args) # same as obj.configure(1, 2, 3) Named Blocks ( _block / _endblock ) A _block is a scoped expression — it evaluates to whatever its >> returns and lets you introduce locals without writing a procedure. Often useful when you need a short inline computation with a few intermediate variables: result << _block _local tmp << compute_something() _local clean << tmp.normalised >> clean _endblock Unlike a _proc , a block runs immediately; there is no separate invocation step. Use _leave to exit early from a block. Thread Synchronisation ( _lock / _endlock ) Acquires the monitor on an object for the duration of the block — only one thread at a time can hold it. Required when mutating shared state in Smallworld 5, which is heavily multithreaded. _lock my_obj my_obj.counter +<< 1 my_obj.last_updated << date_time_now() _endlock Lock on the object whose invariants you are protecting — typically _self . Keep lock bodies short; long-held locks are a common source of contention. Pair with concurrent_hash_map where possible to avoid locking entirely. Thread-Local Access ( _thisthread ) _thisthread is the currently executing thread object. Common uses: _thisthread.sleep(100) # block this thread for 100 ms _thisthread.as_oop # integer unique to this thread — handy as a map key _thisthread.vm_priority # current priority; e.g. spawn child at priority-1 Treat _thisthread as the entry point for thread-local state and scheduling. Do not cache it across method calls — always re-read. Exemplars (Classes) Magik does not have classes. It uses exemplars — a prototype-based system where new instances are clones of the exemplar. Define an Exemplar def_slotted_exemplar( :my_object, { {:slot_a, _unset}, {:slot_b, \"default_value\"} }, {@user:parent_exemplar_a, @user:parent_exemplar_b} # inheritance list ) $ Namespace prefixes in exemplar names Exemplar symbols carry a short prefix that tells you where and how they may be used: Package-qualified — pkg:name . A colon separates a package identifier from the exemplar name. sw:rope , sw:property_list , user:my_demo . Use this form for any public cross-package reference. Package-private — xx!name . An exemplar whose name contains ! is package-private. Only code within the same package should reference it. The owning package is free to rename or remove ! -named exemplars without notice, so external code must never depend on them. This parallels the ! -in-method-name rule: ! in an identifier is always a \"don't rely on this from outside\" marker, whether the identifier is a method or an exemplar. # Fine — same-package reference to a package-private exemplar sw:def_slotted_exemplar(:my_thing, {}, {@user:internal!base_thing}) $ # Also fine — public package-qualified reference sw:def_slotted_exemplar(:my_thing, {}, {@sw:rope}) $ Slot Accessors Always define slot accessors using slotted_format_mixin.define_slot_access() rather than writing accessor methods by hand. Slots must only be accessed directly ( .slot_name ) inside init() and the generated accessor methods — all other code must go through the accessors. my_object.define_slot_access(:slot_a, :writable, :public) my_object.define_slot_access(:slot_b, :readable, :public) $ Constructor Pattern (new/init) _method my_object.new(val_a, val_b) ## Creates a new MY_OBJECT with VAL_A and VAL_B. ## Returns a new my_object instance. >> _clone.init(val_a, val_b) _endmethod $ _private _method my_object.init(val_a, val_b) ##",
    "variables": [],
    "opening_statement": "你好，我是 magik，This skill should be used when the user is writing...",
    "suggested_questions": [],
    "source": "DeepseekModel",
    "source_url": "https://deepseekmodel.com/skill?id=krn-robin-claude-magik-skills-magik-skill-md"
}