Специальные методы (string, equalityKey, !, =)
Метод string
Метод string используется для строкового представления сущности, в контексте которой этот метод определен.
Это может быть структура или трейт. Метод может быть определен внутри структуры или трейта. Или как глобальный метод.
Метод не должен принимать параметры.
struct Test(string: String)
struct Value(value: Int)
def Value string = "Value: "+ this.value
trait Calc {
def string: String
}
trait Work {
}
def Work string = "Work string"
def main = {
println: Test("Test string")
println: Value(123)
println: new Calc {
def string = "Calc string"
}
println: new Work {}
}
Выдаёт:
Test string
Value: 123
Calc string
Work string
Тип результата метода string может быть любой, не обязательно String. И может быть любая кардинальность:
struct Test(string: Int*)
struct Value(value: Int*)
def Value string = this.value
trait Calc {
def string: Int*
}
trait Work {
}
def Work string = (123, 123)
def main = {
println: Test((123, 123))
println: Value((123, 123))
println: new Calc {
def string = (123, 123)
}
println: new Work {}
}
Выдаёт:
(123,123)
(123,123)
(123,123)
(123,123)
Но конечное преобразование в String в таком случае делает компилятор. Для предсказуемого поведения лучше определять метод string с типом результата String.
Специальный метод string ищется только среди полностью определенных (не шаблонных) методов, у которых в контексте явно или через объединение указан соответствующий трейт или структура. Методы string без контекста, с контекстом Any, шаблоны не используются для генерации строкового представления.
Метод equalityKey
Метод equalityKey используется только для структуры, он определяет способ сравнение экземпляров структур. См. соответствующий раздел в описании структур.
Специальный метод equalityKey ищется только среди полностью определенных (не шаблонных) методов, у которых в контексте явно или через объединение указана соответствующая структура. Методы equalityKey без контекста, с контекстом Any, шаблоны не используются для управления сравнением.
Для трейтов этот метод не поддерживается.
Метод !
При помощи обратных кавычек `` можно определять методы с именами, которые не допускает парсер напрямую. Например, можно определить и вызвать метод ! как `!`:
def `!` = "test"
def main = {
println: `!`
}
Выдаёт:
test
Но особенность метода ! в том, что его нужно определять при помощи обратных кавычек ``, но вызывать можно без них:
def `!` = "test"
def main = {
println: !
}
Выдаёт:
test
Поскольку это обычный метод, то нет ограничений на число параметров, контекст или тип результат.
В Libretto имя ! имеет смысл apply (применить). Например, в этом смысле он используется в анонимных функциях и отображениях.
Методы для ! и =
Например, в отображениях метод ! с одним параметром служит для получения значения, привязанного к указанному ключу. А для привязки используется комбинация ! и знака равно =:
def main = {
fix m = map[Int, String]()
m.!(1) = "one"
println: m.!(1)
}
Выдаёт:
one
Вызов m.!(1) = "one" преобразуется в m.`!=`(1, "one").
struct Test(v: Int)
def Test `!=`(a: Int, b: Int) = a + b + this.v
def main = {
fix t = Test(123)
println: t.!(777) = 100
}
Выдаёт:
1000
Если параметров для ! нет, то вызова преобразуется в != с одним параметром:
struct Test(v: Int)
def Test `!=`(a: Int) = a + this.v
def main = {
fix t = Test(123)
println: t.! = 777
}
Выдаёт:
900
Это работает не только для =, но и для += и .=. И для любого числа параметров. Общее правило: вызов превращается в !=, !+=, !.=, в аргументы этого вызова сперва добавляются аргументы, что указаны для ! (если они есть), а последним аргументом добавляется выражение после =:
a.! = xпреобразуется вa.`!=`(x)a.! += xпреобразуется вa.`!+=`(x)a.! .= xпреобразуется вa.`!.=`(x)a.!(b) = xпреобразуется вa.`!=`(b, x)a.!(b) += xпреобразуется вa.`!+=`(b, x)a.!(b) .= xпреобразуется вa.`!.=`(b, x)a.!(b, c) = xпреобразуется вa.`!=`(b, c, x)a.!(b, c) += xпреобразуется вa.`!+=`(b, c, x)a.!(b, c) .= xпреобразуется вa.`!.=`(b, c, x)- и т.д.
При определении такого метода с = в названии его нужно оборачивать в обратные кавычки ``
def Int `!=`(a: Int, b: Int, c: Int, d: Int, e: Int) =
this + a + b + c + d + e
def main = {
println: 123.!(1,2,3,4) = 5
}
Выдаёт:
138
Например, простой буфер целых чисел:
trait IntBuffer {
def `!`: Int*
def `!=`(v: Int*): ()
def `!.=`(v: Int*): ()
def `!+=`(v: Int*): ()
}
def intBuffer(initValue: Int*) = {
var buf: Int* = initValue
new IntBuffer {
def `!` = buf
def `!=`(v) = buf = v
def `!.=`(v) = buf .= v
def `!+=`(v) = buf += v
}
}
def main = {
fix b = intBuffer: (1, 2, 3)
println: b.!
b.! .= 0
println: b.!
b.! += (4, 5)
println: b.!
b.! = 6..9
println: b.!
}
Выдаёт
(1,2,3)
(0,1,2,3)
(0,1,2,3,4,5)
(6,7,8,9)
Методы для = (общее правило)
Правило для ! и = - это частный случай более общего правила вызова методов с =.
Если присваивание (=, +=, .=) не связано с переменной (в левой части), то оно при компиляции заменяется на вызов метода:
-
Имя метода (простое или с префиксом) формируется добавлением
=,+=,.=к локальной части имени, стоящего последним шагом слева от присваивания.a =… превращается в`a=`a/b +=… превращается вa/`b+=` -
В аргументы вызова сперва переходят аргументы в скобках слева от
=(если такие есть), а правая часть от присваивания добавляется последним аргументом.a = 123превращается в`a=`(123)a/b(c, d) += xпревращается вa/`b+=`(c, d, x)
При определении такого метода с = в названии его нужно оборачивать в обратные кавычки ``
Такой подход можно использовать для имитации т.н. getter и setter. В качестве getter будут выступать обычные методы без параметров, а в качестве setter как раз метод с =.
def Test() = {
var aValue = 0
new {
def `a=`(v: Int): () = aValue = v
def a: Int = aValue
}
}
def main = {
fix obj = Test()
obj.a = 123
println: obj.a
obj.a = obj.a + 777
println: obj.a
}
Выдаёт:
123
900
В этом примере трейт Test выглядит как имеющий изменяемое поле a, но работа с ним - это вызов двух методов: a для чтения и `a=` для записи.