Протоколы Clojure против структурных типов Scala

После просмотра интервью с Ричем Хикки на Протоколы в Clojure 1.2, и, зная очень мало о Clojure, у меня есть несколько вопросов по протоколам Clojure:

  • Предназначены ли они для того же, что и структурные типы в Scala? Какие преимущества у протоколов перед структурными типами (производительность, гибкость, ясность кода и т. Д.)? Реализуются ли они через отражения?
  • Вопросы по взаимодействию со Scala: можно ли использовать протоколы вместо структурных типов в Scala? Могут ли они быть расширены (если термин «расширение» можно применить к протоколам) в Scala?

person Vasil Remeniuk    schedule 22.12.2010    source источник
comment
Что касается первого пункта, разве классы типов из Haskell не ближе к имплицитам в Scala?   -  person axel22    schedule 22.12.2010
comment
Насколько я понимаю, неявные признаки и черты являются альтернативой Scala классам типов (особенно, когда дело доходит до изменения существующей функциональности). Но то, что может быть достигнуто с помощью структурных типов (и, как я полагаю, протоколов), - это передача экземпляра существующего типа, который вы не можете (или не хотите) изменять, в API, который просто ожидает, что переданный объект будет иметь специальный метод для звоните: def call(c:{ def call():Unit }) = c.call()   -  person Vasil Remeniuk    schedule 22.12.2010
comment
Структурные типы не относятся к классам типов.   -  person Daniel C. Sobral    schedule 22.12.2010
comment
Я не сказал, что они родственники. В любом случае, упоминание о типовых классах, привлекающих слишком много внимания, теперь убрано из вопроса :)   -  person Vasil Remeniuk    schedule 22.12.2010
comment
Вам действительно стоит посмотреть это: vimeo.com/11236603.   -  person nickik    schedule 22.12.2010


Ответы (4)


Совершенно не связаны.

Scala - это язык со статической типизацией. Clojure - это язык с динамической типизацией. Эта разница в корне формирует их обоих.

Структурные типы - статические типы, период. Это просто способ заставить компилятор статически доказать, что объект будет иметь определенную структуру (здесь я говорю «доказать», но приведение типов, как всегда, может привести к ложным доказательствам).

Протоколы в Clojure - это способ создания динамической диспетчеризации, который намного быстрее, чем отражение или поиск вещей на карте. В семантическом смысле они на самом деле не расширяют возможности Clojure, но с точки зрения работы они значительно быстрее, чем механизмы, которые использовались ранее.

Черты Scala немного ближе к протоколам, как и интерфейсы Java, но опять же существует проблема статики и динамики. Черты Scala должны быть связаны с классом во время компиляции, подобно интерфейсам Java. Протоколы Clojure могут быть добавлены к типу данных во время выполнения постфактум даже третьей стороной.

Что-то вроде протоколов Clojure возможно в Java и Scala с помощью таких механизмов, как шаблоны оболочки / прокси или динамические прокси (http://download.oracle.com/javase/1.4.2/docs/guide/reflection/proxy.html). Но они будут намного более неуклюжими, чем протоколы Clojure, и получить правильную идентификацию объекта также сложно.

person James Iry    schedule 22.12.2010
comment
Хотя протоколы Clojure могут быть расширены во время выполнения, они являются статически типизированными в том смысле, что компилятор будет производить статически типизированный вызов функции на основе типа первого аргумента (или выполнять одно преобразование интерфейса, если тип не известен / не может быть выведен). Это одна из причин, по которой протоколы очень быстрые по сравнению с мультиметодами (полностью динамический эквивалент). - person mikera; 25.01.2012

Цель протоколов в Clojure - эффективное решение проблемы выражения.

[См .: Простое объяснение протоколов закрытия.]

Решение проблемы выражения в Scala - неявное. Итак, семантически this является ближайшим эквивалентом Clojure Protocols в Scala. (В Haskell это будут классы типов или, возможно, семейства типов.)

person Jörg W Mittag    schedule 22.12.2010

Как я понял из этого вводного сообщения в блоге, Протоколы закрытия ближе к Scala Traits, чем к структурным типам (и, таким образом, не могут использоваться в качестве их замены, отвечая на мой второй вопрос):

/* ----------------------- */
/* --- Protocol definition */
/* ----------------------- */

(defprotocol Fly
  "A simple protocol for flying"
  (fly [this] "Method to fly"))

/* --- In Scala */    
trait Fly{
    def fly: String
}

/* --------------------------- */
/* --- Protocol implementation */
/* --------------------------- */

(defrecord Bird [nom species]
  Fly
  (fly [this] (str (:nom this) " flies..."))

/* --- In Scala */    
case class Bird(nom: String, species: String) extends Fly{
    def fly = "%s flies..." format(nom)
}

/* --------------------- */
/* --- Dynamic extension */
/* --------------------- */

(defprotocol Walk
  "A simple protocol to make birds walk"
  (walk [this] "Birds want to walk too!"))

(extend-type Bird
  Walk
  (walk [this] (str (:nom this) " walks too..."))

/* --- In Scala */    
trait Walk{
    def walk = "Birds want to walk too!"
}

implicit def WalkingBird(bird: Bird) = new Walk{
    override def walk = "%s walks too..." format(bird.nom)
}

/* --------------- */
/* --- Reification */
/* --------------- */

(def pig (reify
                Fly (fly [_] "Swine flu...")
                Walk (walk [_] "Pig-man walking...")))

/* --- In Scala */    
object pig extends Fly with Walk{
    def fly = "Swine flu..."
    override def walk = "Pig-man walking..."
}
person Vasil Remeniuk    schedule 22.12.2010
comment
динамическое расширение означает использование протокола расширения, а не определение нового протокола .... например. вы можете расширить Fly до java.lang.String, если хотите ... - person mikera; 25.01.2012
comment
Вы можете использовать неявные преобразования для достижения того же эффекта. Это то, что делает Scala для расширения массивов Java. - person Alexandru Nedelcu; 10.11.2012

Другие ответы лучше отражают другие части вашего вопроса, но:

Реализуются ли они через отражения?

Нет - протоколы компилируются в интерфейсы JVM. Вещи, которые реализуют протоколы (reify, defrecord и т. Д.), Скомпилированы в классы JVM, которые реализуют интерфейс протокола, поэтому вызовы функций протокола такие же, как и стандартные вызовы методов JVM под капотом.

Фактически это было одним из мотивов создания протоколов - многие внутренние структуры данных Clojure были написаны на Java из соображений скорости, потому что не было возможности выполнить полиморфную отправку на полной скорости в чистом Clojure. Протоколы это обеспечивают. В исходном коде Clojure по-прежнему много Java, но теперь все это можно переписать на Clojure без потери производительности.

person levand    schedule 22.12.2010
comment
Итак, хотя в приведенных выше ответах подчеркивается, что эти две вещи разные, потому что одна (в Clojure) - это динамическая типизация, а другая (в Scala) - статическая типизация (Джеймс Айри), но все же они оба соответствуют базовым интерфейсам Java под капот. Так что, действительно, они похожи, и даже концептуально. - person imz -- Ivan Zakharyaschev; 30.01.2011
comment
Кстати, граница между статической и динамической типизацией не такая уж строгая. И Java была намеренно разработана, чтобы выглядеть, с одной стороны, как статически типизированный язык, такой как C ++, а с другой стороны, чтобы выглядеть как Smalltalk (динамически типизированный). Чем Java похож на Smalltalk с динамической типизацией? Определите достаточно много интерфейсов (по одному для каждого используемого вами метода), и вы почти у цели: можете ли вы вызвать метод (= отправить определенное сообщение) объекту, не должно быть связано с тем, наследует ли он некоторые части реализации от подходящего суперкласса . - person imz -- Ivan Zakharyaschev; 30.01.2011
comment
Этот стиль программирования в Java (множество интерфейсов для моделирования ощущений от Smalltalk) - хотя и хорош с концептуальной точки зрения - все же требовал слишком большого количества наборов текста. Язык Scala делает этот стиль программирования для JVM элегантным и на первый взгляд, делая конструкции этого языка короткими и удобочитаемыми! - person imz -- Ivan Zakharyaschev; 30.01.2011
comment
Ср. stackoverflow.com/questions/1948069 /. - person imz -- Ivan Zakharyaschev; 30.01.2011
comment
@HassanSyed зависит от пути кода. Во многих случаях вызов протокола может быть скомпилирован в одну invokevirtual инструкцию JVM, что происходит настолько быстро, насколько это возможно. - person levand; 27.04.2015
comment
Верно ли, что они не добавляют накладных расходов по сравнению с чистой диспетчеризацией интерфейса? Разве для вызовов протокола не всегда требуется дополнительный поиск в MethodImplCache, чтобы они также могли расширять существующие типы? - person Didier A.; 10.03.2020