Clojure - отправка по типу возврата? (Такой же выразительный, как классы типов Haskell)
Это вопрос о выразительности Clojure по сравнению с другими языками, такими как Haskell. Более широкая проблема - это решение проблемы выражения.
Этот вопрос пришел к выводу, что в целом протоколы Clojure (и мультиметоды) были менее выразительными, чем классы типов Haskell, поскольку протоколы отправлялись по первому аргументу, а классы типов Haskell могли отправляться по типу возвращаемого значения. (Теперь я думаю, что эти рассуждения действительно интересны, и они не заинтересованы в развязывании языковой войны. Меня просто интересует ясность мысли).
Как часть нарушения этого рассуждения - мой вопрос - мы не можем создать мультиметод Clojure, который отправляет тип возврата (или подсказку типа). Я думаю, что мы можем поместить следующее выражение в мультиметод Clojure:
(= java.lang.String (:tag (meta #'my-string)))
где функция:
(defn ^String my-string []
"hello world")
Изменить: Дело в том, что я могу запустить:
(meta #'my-string)
и получите следующий результат без оценки функции:
{:arglists ([]), :ns #<Namespace push-price.core>, :name my-string, :column 1,
:line 1, :file "/private/var/folders/0l/x6hr0t1j2hvcmm_sqq04vdym0000gn/T/form-
init7576840885484540032.clj", :tag java.lang.String}
т.е. у меня есть некоторая информация о предполагаемом типе моей функции без оценки.
Изменить 3 (24 апреля 2014 г.):
Предположим, у меня есть следующие типы: (deftype string-type [])
(deftype int-type [])
Тогда у меня есть следующие функции, определенные в терминах этих типов:
(defn #^{:return-type string-type} return-string []
"I am returning a string")
(defn #^{:return-type int-type} return-int []
42)
Теперь я пишу функцию для отправки по типу возврата, например, так:
(defn return-type-dispatch [fn-arg]
(let [return-type (:return-type (meta fn-arg))]
(cond
(= return-type string-type)
"This function has a return type of the string type"
(= return-type int-type)
"This function has a return type of the integer type"
:else (str "This function has a return type of:" return-type))))
Затем я пишу макрос для запуска во время компиляции
(defmacro compile-time-type-check-string []
(println (return-type-dispatch #'return-string)))
(compile-time-type-check-string)
Затем я проверяю это так:
lein uberjar
Это дает следующий результат:
$ lein uberjar
Compiling dispatch-type-compile-time.core
This function has a return type of:class dispatch_type_compile_time.core.string-type
...
Таким образом, я, кажется, рассылаю по типу возврата.
1 ответ
Давайте представим, что в Clojure был полиморфизм возвращаемого типа. Это позволило бы нам написать что-то вроде класса Haskell
class Default a where def :: a
В Haskell это работает, потому что во время компиляции иметь такой фрагмент
def
так как известно, что во время компиляции он неоднозначен относительно того, что это значит. В том же духе, если бы мы написали фрагмент Clojure
(def)
было бы невозможно знать, как отправить этот вызов на соответствующий экземпляр. Чтобы быть более понятным, порядок оценки для Clojure таков, что во фрагменте
(foo x y z)
выражения x
, y
, а также z
оценивать все до foo
, Для того чтобы (def)
для работы нужно будет как-то изучить foo
(и, таким образом, заставить его оценку), чтобы получить информацию о том, как возвращаемое значение (def)
будет использоваться.
Это может быть сделано после преобразования CPS, в этом случае (def)
будет преобразован в функцию, как (в записи типа Haskell)
class Default a where def :: (a -> r) -> r
и теперь мы видим, что мы могли бы изучить функцию продолжения, чтобы узнать информацию о типе ожидаемого параметра и затем отослать его.
Наконец, при наличии достаточного количества магии макросов это может быть сделано... но, вероятно, на данный момент меньше усилий, чем реализация системы типов в стиле Haskell поверх Clojure. Типизированный Clojure может быть отличной моделью для этого, за исключением того, что он был явно разработан так, что предполагаемые типы не могут влиять на семантику Clojure. Это именно то , что происходит в полиморфизме возвращаемого типа, и поэтому в Typed Clojure это явно невозможно.