И какие гарантии дает GDScript? Он такой же динамический как и Lua. По ссылке сказано что его придумали только потому что задолбались писать байндинги внутренностей движка к языку общего назначения.
Да, конечно будет кучу памяти отжирать. Но гигабайт данных отправлять в браузер одним куском, без пагинации? Тут у JS начнутся проблемы, не только у редакса. Это особенный случай, и решение понадобится тоже особое.
There is no applicable method for the generic function
#<STANDARD-GENERIC-FUNCTION COMMON-LISP-USER::ATTACK (2)>
when called with arguments
(#<WIZARD {1005F6AB23}> #<WIZARD {10060999F3}>).
[Condition of type SB-PCL::NO-APPLICABLE-METHOD-ERROR]
Чтобы это исправить надо либо добавить конкретную реализацию для это случая
А подпрограммы, которые задают поведение объектов называют методами. Как правило, набор методов свой у каждого класса, а не у каждого объекта. Чтобы каждый объект определённого класса вёл себя как и другие объекты того же класса. Буду рад узнать из комментариев о языках, где дело обстоит иначе
Дело обстоит немного иначе в языках реализующих metaobject protocol (Common Lisp Object System), да и вообще в языках в которых есть мультиметоды или multiple dispatch (Common Lisp, Clojure, Julia, C#). Там методы не принадлежат классам — классы это просто тип структуры. А методы реализуются над классами, и один и тот-же метод может быть объявлен для нескольких классов.
(defclass entity ()
((x :initarg :x :initform 0)
(y :initarg :y :initform 0))
(:documentation "Базовый класс сущности на карте"))
(defclass creature (entity)
((hp :initarg :hp :initform 100)
(mana :initarg :mana :initform 0))
(:documentation "Живое существо, наследник класса entity"))
Придумываем два типа существ. Обратите внимание — никаких методов внутри класса.
(defclass warrior (creature) ()
(:default-initargs :hp 200 :mana 0)
(:documentation "Воин"))
(defclass wizard (creature) ()
(:default-initargs :hp 50 :mana 100)
(:documentation "Волшебник"))
(setf conan (make-instance 'warrior))
(setf merlin (make-instance 'wizard))
(describe conan)
;#<WARRIOR {10033335F3}>
; [standard-object]
; X = 0
; Y = 0
; HP = 200
; MANA = 0
(describe merlin)
;#<WIZARD {1002F1C9F3}>
; [standard-object]
; X = 0
; Y = 0
; HP = 50
; MANA = 100
Научим их перемещаться.
(defgeneric walk (entity x y)
(:documentation "Перемещает сущность на новое место"))
(defmethod walk (creature d-x d-y)
(with-slots (x y) creature
(setf x d-x)
(setf y d-y)))
(walk conan 10 50)
(walk merlin 5 20)
(describe conan)
;#<WARRIOR {10033335F3}>
; [standard-object]
; X = 10
; Y = 50
; HP = 200
; MANA = 0
(describe merlin)
;#<WIZARD {1002F1C9F3}>
; [standard-object]
; X = 5
; Y = 20
; HP = 50
; MANA = 100
Пока все очень похоже на обычные ООП-языки. Зачем выносить метод из класса пока не очень понятно. Приведем пример еще одного метода.
(defgeneric attack (attacker target)
(:documentation "Позволяет одному существу атаковать другое"))
(defmethod attack ((attacker warrior) (target wizard))
(with-slots (hp) target
(decf hp 20)))
(defmethod attack ((attacker wizard) (target warrior))
(with-slots (mana) attacker
(decf mana 10))
(with-slots (hp) target
(decf hp 40)))
(attack conan merlin)
(attack merlin conan)
(describe conan)
;#<WARRIOR {10033335F3}>
; [standard-object]
; X = 10
; Y = 50
; HP = 160
; MANA = 0
(describe merlin)
;#<WIZARD {1002F1C9F3}>
; [standard-object]
; X = 5
; Y = 20
; HP = 30
; MANA = 90
Метод attack диспатчится используя классы аргументов attacker и target. Когда воин атакует волшебника вызывается одна реализация метода, когда волшебник атакует воина — другая. Получается что метод attack нельзя положить ни внутрь класса warrior, ни внутрь класса wizard — он принадлежит одновременно обеим классам.
Еще один пример того что инкаспуляция не имеет отношения к ООП — ее тривиально можно организовать в функциональном стиле при помощи замыканий. Например есть счетчик, который можно только инкрементировать и обнулять. Отрицательное значение для счетчика — это ошибка.
Здесь без хитростей уже не залезть внутрь замыкания. Прямого доступа к переменной нет, нужно пользоваться интерфейсом из двух функций который вернул createCounter.
Есть ещё очень крутой GSAP ScrollTrigger
И какие гарантии дает GDScript? Он такой же динамический как и Lua. По ссылке сказано что его придумали только потому что задолбались писать байндинги внутренностей движка к языку общего назначения.
Зачем в начале упоминается GDScript? Он с питоном не связан никак кроме похожего синтаксиса и не является языком общего назначения.
Да, конечно будет кучу памяти отжирать. Но гигабайт данных отправлять в браузер одним куском, без пагинации? Тут у JS начнутся проблемы, не только у редакса. Это особенный случай, и решение понадобится тоже особое.
Вдогонку — Hexing the technical interview
http://openworm.org/ — симулируют круглого червя Caenorhabditis elegans.
Чтобы это исправить надо либо добавить конкретную реализацию для это случая
либо сделать какой-нибудь общий метод, который будет ловить все комбинации
Ну возможно, но в любом случае инкапсуляция старее ооп.
Дело обстоит немного иначе в языках реализующих metaobject protocol (Common Lisp Object System), да и вообще в языках в которых есть мультиметоды или multiple dispatch (Common Lisp, Clojure, Julia, C#). Там методы не принадлежат классам — классы это просто тип структуры. А методы реализуются над классами, и один и тот-же метод может быть объявлен для нескольких классов.
Придумываем два типа существ. Обратите внимание — никаких методов внутри класса.
Научим их перемещаться.
Пока все очень похоже на обычные ООП-языки. Зачем выносить метод из класса пока не очень понятно. Приведем пример еще одного метода.
Метод attack диспатчится используя классы аргументов attacker и target. Когда воин атакует волшебника вызывается одна реализация метода, когда волшебник атакует воина — другая. Получается что метод attack нельзя положить ни внутрь класса warrior, ни внутрь класса wizard — он принадлежит одновременно обеим классам.
Такой вот гибкий полиморфизм.
Еще один пример того что инкаспуляция не имеет отношения к ООП — ее тривиально можно организовать в функциональном стиле при помощи замыканий. Например есть счетчик, который можно только инкрементировать и обнулять. Отрицательное значение для счетчика — это ошибка.
Здесь без хитростей уже не залезть внутрь замыкания. Прямого доступа к переменной нет, нужно пользоваться интерфейсом из двух функций который вернул createCounter.