Ответ 1
Протоколы представляют собой точку интерфейса между двумя типами конкретных объектов. Один из них - это код, вызывающий протокол (все, что вызывает foo
в вашем примере), а другой - это его реализация (Atype
foo-method
). То, что удобно для одного, может быть не удобным для другого. Разработчик хочет предоставить самый маленький интерфейс, который будет завершен, в то время как вызывающие абоненты хотят поддерживать самый богатый API.
Вы упомянули ядро ClojureScript; посмотрите там протокол ISeq
. Он реализуется несколькими типами, каждый из которых должен реализовывать -first
и -rest
. Чтобы сделать их максимально легкими для реализации, ни один из них не требуется для вызова seq в его аргументе. Однако связанные функции first
и rest
, которые сталкиваются с поддержкой вызывающих абонентов, передаются в несекундах, таких как строки, векторы и т.д., Поэтому эта общая функциональность обеспечивается не-протокольными функциями. Конечно, API-интерфейс, обращенный к вызывающему абоненту, богаче, чем при next
, map
, filter
, последовательном деструктурировании и т.д., все они построены поверх -first
и -rest
.
Другие общие функции, предоставляемые fns, которые включают методы протокола, включают проверку аргументов (например, утверждений), аргументы по умолчанию и поддержку для var-args.