В общем, наличие слишком большого количества «волшебников» c в языке является признаком плохого языкового дизайна. Может быть, поэтому не так много языков, которые имеют методы magi c?
Magi c, как это создает двухклассовую систему: разработчик языка может добавлять новые методы magi c к языку , но программист может использовать только те методы, которые им позволяет High Priest Of Language Design. В общем, программист должен иметь возможность делать как можно больше без , требующего изменения спецификации языка.
Например, в Scala, +
, -
, *
, /
, ==
, !=
, <
, >
, <=
, >=
, ::
, |
, &
, ||
, &&
, **
, ^
, +=
, -=
, *=
, /=
и т. Д. И т. Д. Являются просто допустимыми идентификаторами. Итак, если вы хотите реализовать свою собственную версию умножения для ваших собственных объектов, вы просто пишете метод с именем *
. Это просто скучный старый стандартный метод, в нем нет абсолютно ничего "магического" c.
И наоборот, любой метод может быть вызван с использованием операторной нотации, то есть без точки. И любой метод, который принимает ровно один аргумент, может быть вызван без скобок в нотации оператора.
Это относится не только к методам. Кроме того, любой конструктор типа с ровно двумя аргументами типа может использоваться в инфиксной нотации, поэтому, если у меня есть
class ↔️[A, B]
, я могу сделать
class Foo extends (String ↔️ Int)
, что аналогично
class Foo extends ↔️[String, Int]
Ну ... я вроде соврал: в Scala есть немного синтаксиса c сахара:
foo()
переводится в foo.apply()
, если нет метода с именем foo
по объему. Это позволяет эффективно перегрузить оператор вызова функции. foo.bar = baz
переводится в foo.bar_=(baz)
. Это позволяет эффективно перегружать присваивание свойств. (Это то, как вы пишете сеттеры в Scala.) foo(bar) = baz
переводится в foo.update(bar, baz)
. Это позволяет эффективно перегружать назначение индекса. (Это, например, способ записи массива или словаря в Scala, например.) !foo
(и несколько других) переводятся в foo.unary_!
. foo += bar
попытается вызвать +=
метод foo
, т.е. он эквивалентен foo.+=(bar)
. Но если это не удастся и foo
является допустимым значением lvalue, и foo
имеет метод с именем +
, тогда Scala также попытается foo = foo + bar
.
Кроме того, приоритет, ассоциативность и фиксированность зафиксированы в Scala: они определяются первым символом имени метода. Т.е. все методы, начинающиеся с *
, имеют одинаковый приоритет, все методы, начинающиеся с -
, имеют одинаковый приоритет и т. Д.
Haskell идет на шаг дальше: * фундаментальный Разница между функциями и операторами. Любая функция может использоваться в нотации вызова функции и в нотации оператора. Единственное отличие - лексическое: если имя функции состоит из символов оператора, тогда, когда я хочу использовать его в нотации вызова функции, я должен заключить его в скобки. OTOH, если имя функции состоит из букв alphanumeri c и я хочу использовать его в нотации оператора, мне нужно обернуть его в кавычки. Итак, следующие значения эквивалентны:
a + b
(+) a b
a `plus` b
plus a b
Для использования оператором функций вы можете свободно определять фиксированность, ассоциативность и приоритет, например:
infixr 15 <!==!>
In Ruby, существует заранее определенный набор операторов, который имеет соответствующие методы, например:
def +(other)
plus(other)
end