Janet 1.32.1-cc5beda Documentation
(Other Versions: 1.31.0 1.29.1 1.28.0 1.27.0 1.26.0 1.25.1 1.24.0 1.23.0 1.22.0 1.21.0 1.20.0 1.19.0 1.18.1 1.17.1 1.16.1 1.15.0 1.13.1 1.12.2 1.11.1 1.10.1 1.9.1 1.8.1 1.7.0 1.6.0 1.5.1 1.5.0 1.4.0 1.3.1 )

Object-Oriented Programming

Although Janet is primarily a functional language, it has support for object-oriented programming as well, where objects and classes are represented by tables. Object-oriented programming can be very useful in domains that are less analytical and more about modeling, such as some simulations, GUIs, and game scripting. More specifically, Janet supports a form of object-oriented programming via prototypical inheritance, which was pioneered in the Self language and is used in languages like Lua and JavaScript. Janet's implementation of object-oriented programming is designed to be simple, terse, flexible, and efficient in that order of priority.

Please see the prototypes section for more information on table prototypes.

For example:

# Create a new object called Car with two methods, :say and :honk.
(def Car
 @{:type "Car"
   :color "gray"
   :say (fn [self msg] (print "Car says: " msg))
   :honk (fn [self] (print "beep beep! I am " (self :color) "!"))})

# Red Car inherits from Car
(def RedCar
 (table/setproto @{:color "red"} Car))

(:honk Car) # prints "beep beep! I am gray!"
(:honk RedCar) # prints "beep beep! I am red!"

# Pass more arguments
(:say Car "hello!") # prints "Car says: hello!"

In the above example, we could replace the method call (:honk Car) with ((get Car :honk) Car), and this is exactly what the runtime does when it sees a keyword called as a function. In any method call, the object is always passed as the first argument to the method. Since functions in Janet check that their arity is correct, make sure to include a self argument to methods, even when it is not used in the function body.

Factory functions

Rather than constructors, creating objects in Janet can usually be done with a factory function. One can wrap the boilerplate of initializing fields and setting the table prototype using a single function to create a nice interface.

(defn make-car
 []
 (table/setproto @{:serial-number (math/random)} Car))

(def c1 (make-car))
(def c2 (make-car))
(def c3 (make-car))

(c1 :serial-number) # 0.840188
(c2 :serial-number) # 0.394383
(c3 :serial-number) # 0.783099

One could also define a set of macros for creating objects with useful factory functions, methods, and properties. The core library does not currently provide such functionality, as it should be fairly simple to customize.

Structs

In addition to tables, Structs may also be used as objects. As of Janet version 1.19.0, structs can have prototypes as well as tables, although in prior version structs could still be used as objects, just without any inheritance. The above example would work for the first object Car, but RedCar could not inherit from Car.

# Create a new struct object called Car with two methods, :say and :honk.
(def Car
 {:type "Car"
  :color "gray"
  :say (fn [self msg] (print "Car says: " msg))
  :honk (fn [self] (print "beep beep! I am " (self :color) "!"))})

(:honk Car) # prints "beep beep! I am gray!"

# Pass more arguments
(:say Car "hello!") # prints "Car says: hello!"

To create a struct that inherits fields from another struct, we need to use the constructing function struct/with-proto. There is no analog to table/setproto for structs because they are immutable, and that goes for prototypes as well. Structs can only inherit from other structs, not tables.

(def Car
 {:_name "Car"
  :honk (fn [self msg] (print "Car says: " msg))})

(def my-car (struct/with-proto Car :color :silver))
(:honk my-car "beep")

Abstract types

While tables make good objects, Janet also strives for good interoperation with C. Many C libraries expose pseudo object-oriented interfaces to their libraries, so Janet allows methods to be added to abstract types. The built in abstract type :core/file can be manipulated with methods as well as the file/ functions.

(file/read stdin :line)

# or

(:read stdin :line)