Types

built-in types, custom user types/objects and related helpers


Functions

Predicates


The Types module provides the foundation for Arturo's type system, including type checking, custom type definitions, and object-oriented programming capabilities.

Key Concepts

  • Basic type checking
  • Custom type definitions
  • Object creation and manipulation
  • Type conversion
  • Magic methods for operator overloading
  • Inheritance and composition

Basic Usage

Type Checking

; basic type checks
print type 42               ; :integer
print type "hello"          ; :string
print type [1 2 3]          ; :block

; check specific type
print integer? 42           ; true
print string? 42            ; false

; check if value is of type
print is? :integer 42       ; true
print is? :string "hi"      ; true

Custom Types

; define new type
define :person [
    ; constructor
    init: method [name age][
        this\name: name
        this\age: age
    ]

    ; string representation
    string: method [][
        ~{|this\name| (|this\age| years old)}
    ]
]

; create instance
john: to :person ["John" 38]!

print john        
; John (38 years old)

Tip
Within methods, you can use \field as a shortcut for this\field. This makes code more concise and readable. For example:

string: method [][
    ; these are equivalent:
    print this\name        ; explicit
    print \name           ; shortcut
]

Inheritance

; base type
define :animal [
    init: method [species][
        \species: species
    ]
    
    speak: method [][
        print "..."
    ]
]

; derived type
define :dog is :animal [
    init: method [name][
        ; call parent constructor
        super "canis"
        \name: name
    ]
    
    speak: method [][
        print "woof!"
    ]
]

; usage
fido: to :dog ["Fido"]!

print fido\species    ; canis
fido\speak           ; woof!

Type Conversions

; basic conversions
str: to :string 42          ; "42"
num: to :integer "42"       ; 42
flt: to :floating "3.14"    ; 3.14

; with custom format
date: to :date .format:"yyyy-MM-dd" "2024-01-15"

Magic Methods

Magic methods allow you to customize how your types behave with various operations.

Method Arguments Description Example Use
init any Constructor Object initialization
get field Field access obj\field
set field, value Field assignment obj\field: value
changing - Before change hook Pre-modification
changed - After change hook Post-modification
compare that Custom comparison Sort ordering
equal? that Equality check obj1 = obj2
less? that Less than obj1 < obj2
greater? that Greater than obj1 > obj2
add that Addition obj1 + obj2
sub that Subtraction obj1 - obj2
mul that Multiplication obj1 * obj2
div that Division obj1 / obj2
fdiv that Float division obj1 // obj2
mod that Modulo obj1 % obj2
pow that Power obj1 ^ obj2
inc - Increment inc 'obj
dec - Decrement dec 'obj
neg - Negation neg obj
key? key Key existence key? obj "field"
contains? what Contains check contains? obj item
append value Add item 'obj ++ value
remove value Remove item 'obj -- value
string - String conversion to :string obj
integer - Integer conversion to :integer obj
floating - Float conversion to :floating obj
rational - Rational conversion to :rational obj
complex - Complex conversion to :complex obj
quantity - Quantity conversion to :quantity obj
logical - Logical conversion to :logical obj
block - Block conversion to :block obj
dictionary - Dictionary conversion to :dictionary obj

Tip
Magic methods are optional - implement only those that make sense for your type's behavior. Also, remember that when implementing magic methods, they affect how your type behaves with standard operators and functions throughout Arturo.

Common Patterns

Implementing Comparable Objects

define :score [
    init: method [value][
        \value: value
    ]

    ; custom comparison
    compare: method [that][
        if \value < that\value -> return neg 1
        if \value > that\value -> return 1
        return 0
    ]
]

; usage
scores: @[
    to :score [42]!
    to :score [18]!
    to :score [73]!
]

print sort scores
; [value:18] [value:42] [value:73] 

Tip
For simple comparisons based on a single field, you can use the sortable helper instead of implementing a full compare method. In the above example, for instance:

;...
compare: sortable 'value
;...

Custom Collections

define :stack [
    init: method [][
        \items: []
    ]

    push: method [item][
        'this\items ++ item
    ]

    pop: method [][
        if empty? \items -> return null
        return pop 'this\items
    ]

    ; custom conversion
    string: method [][
        ~{Stack(|size \items| items)}
    ]

    ; support iteration
    contains?: method [item][
        contains? \items item
    ]
]

; usage
s: to :stack []!
s\push 1
s\push 2
print s\pop     ; 2

Property Change Notifications

define :observable [
    init: method [value][
        \value: value
        \observers: []
    ]

    changing: method [][
        loop \observers 'obs [
            obs\beforeChange \value
        ]
    ]

    changed: method [][
        loop observers 'obs [
            obs\afterChange \value
        ]
    ]

    addObserver: method [observer][
        'this\observers ++ observer
    ]
]

; usage
value: to :observable [42]!
value\addObserver #[
    beforeChange: function [val][
        print ["About to change from:" val]
    ]
    afterChange: function [val][
        print ["Changed to:" val]
    ]
]