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.

MethodArgumentsDescriptionExample Use
initanyConstructorObject initialization
getfieldField accessobj\field
setfield, valueField assignmentobj\field: value
changing-Before change hookPre-modification
changed-After change hookPost-modification
comparethatCustom comparisonSort ordering
equal?thatEquality checkobj1 = obj2
less?thatLess thanobj1 < obj2
greater?thatGreater thanobj1 > obj2
addthatAdditionobj1 + obj2
subthatSubtractionobj1 - obj2
multhatMultiplicationobj1 * obj2
divthatDivisionobj1 / obj2
fdivthatFloat divisionobj1 // obj2
modthatModuloobj1 % obj2
powthatPowerobj1 ^ obj2
inc-Incrementinc 'obj
dec-Decrementdec 'obj
neg-Negationneg obj
key?keyKey existencekey? obj "field"
contains?whatContains checkcontains? obj item
appendvalueAdd item'obj ++ value
removevalueRemove item'obj -- value
string-String conversionto :string obj
integer-Integer conversionto :integer obj
floating-Float conversionto :floating obj
rational-Rational conversionto :rational obj
complex-Complex conversionto :complex obj
quantity-Quantity conversionto :quantity obj
logical-Logical conversionto :logical obj
block-Block conversionto :block obj
dictionary-Dictionary conversionto :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]
    ]
]