Skip to content

U support Object-Oriented (OOP) syntax with the dot . operator, but U is not natively Object-Oriented like C++/Java/Python/Ruby/Js. Only user defined operator add OOP on demand. This leads to all advantages of object-oriented programming without compromises:

  • better expressiveness for sharing knowledge,
  • high development productivity,
  • rapid prototyping,
  • better maintenance,
  • enhanced portability,
  • extensible and reusable systems,
  • isolated and optimal analysis, debugging.

U Object Oriented Model

Using OOP on demand means that U relies on user defined syntax for its OOP model, not its core implementation. U makes it possible to have OOP with/without inheritance, with/without traits, ...

What are Classes ?

Classes are closer to C structs than classes in OOP. They are special kinds of user-defined collections where elements have no existence.

If we think of origami, suppose we have a paper cube. In U, the paper cube would be an object – a collection that can contain values – and classes would be the pattern on the flat paper. Without creating space by joining paper sides to create the cube, the cube does not exist and is not a container. U does the same by allocating memory with classes to create objects.

Classes are compile-time collections underneath. They don't exist at run-time. But they allow U to manage efficiently cache-friendly data structures.

Classes have:

  • Attributes: values type and specifications for objects,
  • Instance Methods: functions attached to classes that process objects with implicit target object object.meth,
  • Class Methods: functions attached to classes that process objects with explicit target object Class.run(object).

What are Objects ?

In U, everything is a value. But not all values are objects. Only Values that contain other values are. Usually, in U, the term object is often used instead of value when using the dot operator for method like obj.method, or when accessing its elements with colon obj:attribute.

Objects have:

  • Instance Variables: values as attributes inside allocated objects,

Objects call Instance or Class methods.

Attributes Visibility

Classes can restrict access to their attributes. By default, all attributes and members are private i.e attributes can only be use within the objects. However, you can change this behavior in ugo to make them public by default. It's very useful when prototyping before hardening your system.

In this documentation, we assume that all attributes are public.

::A :Robot, :speed: @int, :pub: {
  ...
}, :priv: {
  ...
}

Creating Classes and Objects

We usually don't need all the paradigms used in OOP (traits, abstract classes, protocols, ...) for all applications and product design phases.

For an MVP, for example, you usually don't need to take time organising your code to deliver quickly. After prototyping, you can think of how better organize your codebase for reuse. For this gradual design, U defines easy keywords for defining classes and objects:

  • ::A and ::An: for classes definition. Same as ::class,
  • ::a and ::an: for objects creation. Same as ::object, or ::new,
  • ::the: for singleton creation. Singleton ensures there is only one instance of an object. It allows U to better optimize your code. See Singleton.

We use capitalized keywords for classes and lowercase for object creation.

For example:

# Define a structure 
::A :Robot, {
    ::has :speed, :power

    ::new {: ... 
        # constructor ...
    }
    # Class methods
    :.check : {: robot
    }
    # Instance methods
    :run ::> \< "Running..."
    :jump ::> ...
}

:marvin : ::a @Robot, .speed: 3 

# Or 
:marvin : @Robot::new(.speed: 3)

# Method call
marvin.run 

# Access an element
\< marvin:power

# Method as value
:run_meth : marvin:run

run-meth()
# Prints "Running"

In U, You can easily build sophisticated data structures by adding more OOP behaviors like ::class, ::traits, ....

Self

::self allows you to use current object in a method.

::A :Person, :name: @String, {
  :say : \< "My name is " + ::self.name
}

Attribute Shorthand

::A :Person, :name: @String, :age: @Number

:name: "Al"
:age : 42

::a @Person, .name, .age
# Same as 
::a @Person, .name: name, .age: age

Instance Variables

Instance Variables start with a dot . to remind you that you are calling a getter to retrieve a value. Getters/Setters are

::A :Person, :name: @String, {
  :say : \< "My name is " + .name 
}

Getters and Setters

Getters/Setters are generated methods to access attributes from outside the object:

::A :Person, :name: @String, {

  :say : \< "My name is " + .name 
}

Singleton

keyword ::the create a singleton. Singleton ensures there is only one instance of an object for better performance and safety.

::the :Zookeeper, :producers, :consumers, ...

::a Zookeeper # ERROR: only one instance allowed

Inheritance vs Composition

U inheritance is minimal to allow you to use your rules. Inheritance, composition are simply links between classes either at compile-time or at run-time. The inheritance chain will be used to retrieve a method.

For more details, please read Inheritance vs Composition.

# Dimension must be < 3 
::A :Shape, :dim: @<3

::A :Cube, :is_a: @Shape(:dim: 3)

::A :ColoredCube, :is_a: @Cube(), :color: ...

:mycube : (::a @ColoredCube)
\< mycube.dim 
# Prints 3

To evaluate mycube.dim, U executes the following steps: - lookup for an instance method called dim through the class chain ColoredCube -> Cube -> Shape, - dim will be found in Shape class as a getter, - call Shape.dim(mycube)

Traits

Traits are classes without attributes and without type specification. They allow you to model an expected behavior on an object with differents attributes type. They use keywords ::Is, or ::Can.

::A :Shape, { ... }

::Can :Rotate, { ... }
::Is :Blue, { ... }

::A :MovingShape, :is: @Blue, :can: @Rotate # Traits

#Inheritance
::A :Rocket, :is_a: @MovingShape

:falcon : ::a @Rocket

You can check for object type with aperture @

falcon::is_a  @MovingShape, {
    ...
}

Mutually Recursive Classes

Mutually recursive classes (MRS) are classes that refer to each other. U ensures there is no cycle in value references that could lead to incorrect behaviors. To define MRS, use keyword ::strec

// <- allows EvenNumber and OddNumber to be recursive!

::strec :EvenNumber: {
  ...
  @OddNumber :num # Refer to OddNumber
},// <- allows EvenNumber and OddNumber to be recursive!
  :OddNumber: {
    @EvenNumber :num # Refer to EvenNumber
}

Mutually recursive classes are usually created with forward declaration in other languages. U syntax is clearer and safer. For example, you can check literature about how others languages manage mutually recursive classes like a Tree and its Nodes.

Generic Classes

Work in progress