Saltar al contenido

Sobrecarga de Operadores, to_string y Encadenamiento

En esta página

Sobrecarga por convención de nombre

El VM de Zolo resuelve los operadores buscando métodos con nombres canónicos en el tipo del operando izquierdo: add para +, neg para - unario, eq para ==. Como el verificador de tipos estático es conservador, usa la forma directa a.add(b) para pasar zolo check:

Vec2 con add, neg, eq y to_string — llamados directamente por nombre de método.

13-operator-overloading.zolo
Playground
// Feature: Operator overloading via special methods

// Syntax: define `add`, `eq`, `neg`, `to_string` etc. in the `impl`

// When to use: make custom types usable with `+`, `-`, `==` at

// runtime. The VM resolves the operator by looking up the method

// with the canonical name on the type of the left operand.

//

// Note: the conservative static checker may reject `a + b` for

// structs, so the DIRECT form is `a.add(b)`. The runtime effect is

// the same. We use the direct form here to satisfy the checker.


struct Vec2 {
  x: float,
  y: float,
}

impl Vec2 {
  fn new(x: float, y: float) -> Vec2 {
    return Vec2 { x: x, y: y }
  }
  // `+` convention.



  fn add(self, other: Vec2) -> Vec2 {
    return Vec2 { x: self.x + other.x, y: self.y + other.y }
  }
  // Unary `-` convention.



  fn neg(self) -> Vec2 {
    return Vec2 { x: -self.x, y: -self.y }
  }
  // `==` convention.



  fn eq(self, other: Vec2) -> bool {
    return self.x == other.x && self.y == other.y
  }
  // String conversion used by `print` / interpolation.



  fn to_string(self) -> str {
    return "Vec2({self.x}, {self.y})"
  }
}

let a = Vec2::new(3.0, 4.0)
let b = Vec2::new(1.0, 2.0)

// Direct form — always works in the checker.

let c = a.add(b)
print(c.to_string())  // Vec2(4, 6)


let n = a.neg()
print(n.to_string())  // Vec2(-3, -4)


print(a.eq(b))  // false

let d = Vec2::new(3.0, 4.0)
print(a.eq(d))  // true

Sobrecarga con @op / @operator

La forma con decorador vincula explícitamente un método a un símbolo de operador. El verificador de tipos reconoce esta declaración, permitiendo escribir a + b directamente en el código. El nombre del método queda libre — elige el que sea más legible en el contexto:

@op("+"), @op("-"), @op("unary-") y @operator("==") — sintaxis natural de operador aceptada por el checker.

16-operator-overloading-decorator.zolo
Playground
// Feature: Operator overloading via `@op` / `@operator` decorators

// Syntax: `@op("symbol") fn <any_name>(self, other) -> T`

// When to use: when you want the method name to read well in the

// rest of the code (e.g. `scale`, `dot`, `combine`) but still bind

// it to an operator. Unlike the name-convention form (see file 13),

// the decorator form is recognized by the static type checker, so

// you can write `a + b` directly without the `a.add(b)` workaround.

//

// Supported symbols: `+`, `-`, `*`, `/`, `%`, `==`, `<`, `<=`, `..`,

// and the unary forms `unary-`, `not`, `len`. The full table lives

// in `docs/08-decorators.md`.


struct Vec2 {
  x: float,
  y: float,
}

impl Vec2 {
  fn new(x: float, y: float) -> Vec2 {
    return Vec2 { x: x, y: y }
  }

  // Method name is free; the decorator does the wiring.

  @op("+")
  fn plus(self, other: Vec2) -> Vec2 {
    return Vec2 { x: self.x + other.x, y: self.y + other.y }
  }

  @op("-")
  fn minus(self, other: Vec2) -> Vec2 {
    return Vec2 { x: self.x - other.x, y: self.y - other.y }
  }

  // Unary minus uses the `unary-` symbol to disambiguate from `-`.

  @op("unary-")
  fn invert(self) -> Vec2 {
    return Vec2 { x: -self.x, y: -self.y }
  }

  // `@operator` is the long alias for `@op`.

  @operator("==")
  fn same(self, other: Vec2) -> bool {
    return self.x == other.x && self.y == other.y
  }

  fn to_string(self) -> str {
    return "Vec2({self.x}, {self.y})"
  }
}

let a = Vec2::new(3.0, 4.0)
let b = Vec2::new(1.0, 2.0)

// Natural operator syntax — the static checker accepts it because

// the decorator makes the binding explicit at the declaration site.

let sum = a + b
print(sum.to_string())  // Vec2(4, 6)


let diff = a - b
print(diff.to_string())  // Vec2(2, 2)


let neg = -a
print(neg.to_string())  // Vec2(-3, -4)


print(a == b)            // false

let a2 = Vec2::new(3.0, 4.0)
print(a == a2)           // true

Representación textual con to_string

Define fn to_string(self) -> str en impl para controlar lo que print y la interpolación "{obj}" muestran. La función builtin tostring(x) delega en to_string cuando está presente, y devuelve la forma natural para primitivos:

User con to_string; tostring aplicado a int, float, bool, str, nil y array.

15-tostring.zolo
Playground
// Feature: `to_string` — controls textual representation

// Syntax: implement `fn to_string(self) -> str` in the `impl`

// When to use: so that `print(obj)` and interpolation `"{obj}"`

// return a readable string instead of the default dump.


struct User {
  name: str,
  age: int,
}

impl User {
  fn to_string(self) -> str {
    return "User({self.name}, {self.age})"
  }
}

let u = User { name: "Alice", age: 30 }
print(u.to_string())  // User(Alice, 30)

print("user: {u.to_string()}")  // user: User(Alice, 30)


// `tostring(x)` — builtin function that delegates to `to_string`

// when present; for primitives returns the natural form.

print(tostring(42))  // 42

print(tostring(3.14))  // 3.14

print(tostring(true))  // true

print(tostring("hi"))  // hi

print(tostring(nil))  // nil

print(tostring([1, 2, 3]))  // [1, 2, 3]

Encadenamiento de métodos

Cuando cada método devuelve self (o una nueva instancia del mismo tipo), las llamadas pueden encadenarse en secuencia. Este patrón, conocido como builder, es idiomático para construir valores paso a paso:

Builder::new().word("Hello").punct(", ").word("world").punct("!").build() — cadena fluente.

14-method-chaining.zolo
Playground
// Feature: Method chaining — fluent calls

// Syntax: each method returns `self` (or a new `Type`)

// When to use: builder pattern, successive transformations, DSLs.


use std::Array

struct Builder {
  parts: [str],
}

impl Builder {
  fn new() -> Builder {
    return Builder { parts: [] }
  }
  // Mutation + returning self => allows chaining.



  fn word(self, w: str) -> Builder {
    self.parts.push(w)
    return self
  }

  fn punct(self, p: str) -> Builder {
    self.parts.push(p)
    return self
  }

  fn build(self) -> str {
    var out = ""
    for p in self.parts {
      out = out + p
    }
    return out
  }
}

let s = Builder::new().word("Hello").punct(", ").word("world").punct("!").build()

print(s)  // Hello, world!

// expected:

// Hello, world!

Desafío

Extiende Builder con un método upper(self) -> Builder que convierta la última palabra añadida a mayúsculas antes de devolver self.

Buscar en Zolo

9 resultados

enespt-br