$ cat posts/ogu-type-system-1.md

Ogu's type system (1)

es Leer este post en español →

It’s time to look at more details of the Ogu programming language. We’ll start with its type system; this article is an introduction to the subject.

Ogu is a language with static type declarations, although it implements type inference. Let’s look at some examples:

i : Int = 0

j := i // j is of type Int

s : String = "un string"

t := s

Ogu has “syntactic saccharin” to support tuples

naipe : (Int,String) = (10, "espadas")  // naipe is a tuple

as := (1,"espadas")

Naipe = (Int,String) // Introduces the type Naipe as an alias for the tuple (Int,String)

There is also saccharin for lists and hashes:

vocales : \[String\] = \["a", "e", "i", "o", "u"\]

ListaDeStrings = \[String\]

mapa : \[String : Int\] = \["Chile":56, "Usa":1\]

Classes can be generic:

Stack{T}  : () = {

    var \_data : \[T\] = \[\]

    push : (x:T) = \_data = x :: \_data

    pop : ()-\>(r:T) = {

        r = head \_data\
        \_data = tail \_data\
    }

}

Tuples, lists and maps are implemented on top of generic types. In fact, declaring a variable of type

\[T\]

is the same as declaring it as Sequence{T}, and declaring it as

\[K:V\]

is the same as Map{K,V}. Both Sequence and Map are classes defined in Ogu’s basic runtime.

To create a stack of integers we do the following

stackOfInt := Stack{Int}()

// another way

StackOfInt = Stack{Int} // type alias

stackOfInt := StackOfInt()

Ogu supports class inheritance

// a troglodyte is a caveman who lives in a cave

Troglodita : (nombre,grito,cueva : String) > Cavernicola(nombre,grito) = {
    _cueva := cueva
}

Interfaces

Consider a package that works with geometric shapes

Circulo : (radio:Int) = {

    area : () -> Int  = pi * radio ^ 2

    perimetro: () -> Int = 2 * pi * radio

}

Cuadrado : (lado:Int) = {

    area : () -> Int = lado * lado

}

Suppose we want to create a function that prints the area of a geometric shape:

imprimeArea : (c:Cuadrado) = println("el area es "+c.area())

imprimeArea : (c:Circulo) = println("el area es "+c.area())

the problem is that Ogu doesn’t let us overload functions :sad:

There are three ways to solve this. The first is to use type selectors:

imprimeArea : (c:Cuadrado\|Circulo) = println("el area es "+c.area())

what we’ve done is make the type of the imprimeArea function either a Cuadrado or a Circulo (Cuadrado | Circulo ).

Now if we define this function:

imprimePerimetro : (c : Cuadrado\|Circulo) = println("el perimetro es "+c.perimetro())

we’ll get a compilation error: Cuadrado doesn’t define the perimetro function.

The other way to solve this problem is to declare an interface:

Figura := {

    area : ()->Int

    perimetro : () -> Int

}

An interface is like a class, but it has no constructor and the function bodies are optional.

This way I can redefine the Circulo and Cuadrado classes like this:

Circulo : (radio:Int) ~ Figura = {

    area = pi * radio ^ 2

    perimetro = 2 * pi * radio

}

Cuadrado : (lado:Int) ~ Figura ={

    area = lado * lado

    perimetro = lado * 4

}

and implement our functions like so

imprimeArea : (f:Figura) = println("el area es "+f.area())

imprimePerimetro : (f:Figura) = println("el perimetro es"+f.perimetro())

The third way to implement polymorphism is through inheritance, and that one is left as an exercise for the reader.

Consider the Dibujo interface

Dibujo :=  {

    dibujar : () = println ("dibuja un "+nombre)

    nombre : String

}

We can now extend Circulo and Cuadrado so that they are also drawings:

Circulo : (radio:Int) \~ Figura & Dibujo = {

    area = pi \* radio \^ 2

    perimetro = 2 \* pi \* radio

    nombre = "circulo"

}

Cuadrado : (lado:Int) \~ Figura & Dibujo ={

    area = lado\*lado

    perimetro = lado\*4

    nombre = "circulo"

}

This article is just an introduction, and complements the first part of the presentation of Ogu. I look forward to your comments.

Comments

comments powered by Disqus