Skip to content
Ken Gorab edited this page May 28, 2024 · 3 revisions

Basic usage

A "trait" is a way to define behavior or attributes that are meant to be shared across different datatypes. This is basically like an interface in other languages. We do not have inheritance per se in Abra because Abra is not necessarily an object-oriented language.

A trait is defined using the trait keyword, and can include method definitions. These methods can be static methods or instance methods, and these methods can also have default implementations.

For example, here's an imagined trait which would allow serialization/deserialization to/from json:

trait Jsonify {
  func toJson(self): String
  func fromJson(json: String): Self
}

Note the Self type as the return type for the static method fromJson - this is a special type that refers to the type which is conforming to the given trait.

A type or enum can conform to a given trait. This is denoted with a : in the declaration itself:

type Person : Jsonify {
  firstName: String
  lastName: String
}

This syntax means that the type Person conforms to the Jsonify trait, which also means that the toJson and fromJson methods must also be implemented for Person (the snippet above would actually fail to compile in its current state). To make this compile, we can modify the above example to be:

type Person : Jsonify {
  firstName: String
  lastName: String

  trait func toJson(self): String = "{ \"firstName\": \"${self.firstName}", \"lastName\": \"${self.lastName}\" }"
  trait func fromJson(json: String): Self = ...
}

Note that the function implementations have trait func. This distinguishes required trait functions from regular functions that live within a type's definition, and helps with organization.

Traits can also be generic, such as in this imagined Iterator trait:

trait Iterator<T> {
  func next(self): T?
}

type Array<T> : Iterator<T> {
  func next(self): T? = ...
}

Trait types can be used as variables, parameters in functions, and fields in types/enums just like any other type, and any type which conforms to that trait can be passed as a value:

trait Animal {
  func speak(self): String
}

type Cat : Animal {
  trait func speak(self): String = "meow"
}

type Dog : Animal {
  trait func speak(self): String = "woof"
}

func animalGreeting(animal: Animal) = println(animal.speak())
Clone this wiki locally