как будут применяться монадические правила, если функция может быть другого типа - PullRequest
0 голосов
/ 16 сентября 2018

Я смотрю на взаимодействие между clojure и scala. Поскольку в самой java теперь есть лямбды, я думал об обобщении данных и о том, как применить функцию к коллекции

  • закрывающие функции расширяют clojure.lang.IFn и обобщают операции сбора на clojure.lang.ISeq
  • функции scala расширяют scala.Function и обобщают операции сбора на scala.collection.Traversable
  • Java-лямбда-расширение java.util.function.Function и обобщение операций сбора на java.util.stream.Stream

Вопросы:

  • Были бы полезны монады в этом случае?
  • Если это так, будет ли операция map реализована во всех типах коллекций и как это может быть обобщено?
* +1025 * Пример:
  (map (scala-fn +) 
       [1 2 3]
       (scala-seq [1 2 3]) 
       (.stream [1 2 3]))
  => (scala-seq [3 6 9])

Продолжение (добавлен haskell в качестве тега на тот случай, если люди могут знать хардкорный тип)

В Clojure, Scala и Java есть операции, которые принимают коллекцию, применяют функцию к этой коллекции и возвращают новую коллекцию.

  • Все эти языки работают на JVM.
  • Однако каждый язык определяет свой собственный класс для представления функции.

Я больше знаком с clojure, поэтому есть такие операции:

 (into {} [[:a 1] [:b 2]]) => {:a 1 :b 2}

Который преобразует вектор замыкания в карту замыкания. Поскольку операция into обобщается на java.util.List, может использоваться любая структура данных, которая наследует java.util.List.

Я хочу работать с некоторыми библиотеками Scala в ближайшем будущем и столкнуться с определенными препятствиями:

  • Scala, как и clojure, также имеет неизменяемые структуры данных, но они определяются совершенно иначе, чем структуры данных clojure
  • Функции Scala наследуются от scala.Function, поэтому их необходимо перенести в clojure.lang.IFn
  • Структуры данных Scala не наследуются от java.util.List, что означает:

    (into {} (scala-list [:a 1] [:b 2])) не будет работать.

  • Я хочу переопределить некоторые базовые функции замыкания, которые также включают структуры данных scala. (отображать, уменьшать, отображать и т.д.)

Функциональность будет выглядеть примерно так:

 (into {} (scala-list [:a 1] [:b 2])) => {:a 1 :b 2}

 (into (scala-map) [[:a 1] [:b 2]]) => (scala-map :a 1 :b 2)

 (concat (scala-list 1 2) [3 4]) => (scala-list 1 2 3 4)

 (concat [1 2] (scala-list 3 4)) => (1 2 3 4) ;lazy seq

 (map + [1 2] (scala-list 3 4)) => [4 6]

 (map (scala-fn +) [1 2] (scala-list 3 4)) => [4 6]
  • Мне нужна возможность использовать функции clojure и scala в операциях по сбору.
  • Я могу сделать это, не используя монады (проверяя коллекцию и типы функций и выполняя некоторое принуждение перед применением функции)
  • То, что я здесь спрашиваю, вызывает у меня любопытство, так как вся литература, которую я читал о монадах, предполагает, что любая функция f:X->Y универсальна.
  • Однако в случае взаимодействия clojure / scala / lambda функция clojure, функция scala и java лямбда не являются универсальными. Мне интересно, как теория категорий может быть использована для решения этой проблемы.

Ответы [ 2 ]

0 голосов
/ 24 сентября 2018

функции scala расширяют scala.Function и обобщают операции сбора на scala.collection.Traversable

java lambdas расширяют java.util.function.Function и обобщают операции сбора на java.util.stream.Stream.

Во-первых, хорошие новости: это не правильно, лямбды Java и Scala могут реализовывать любой интерфейс SAM (единственный абстрактный метод).Это позволяет использовать лямбды Java с API, ожидающими scala.FunctionN, и лямбды Scala с API, ожидающими java.util.function.* (включая потоки Java).Эта совместимость должна быть полной в Scala 2.12 и более поздних версиях (насколько я знаю).

Плохие новости (вы знали, что это произойдет): когда речь идет конкретно об API-интерфейсе сбора Scala, он также очень сильно зависит от неявногопараметры, и они не могут быть использованы в Java или Clojure.Точно так же API коллекции Clojure опирается на динамическую типизацию, а IFn не является типом SAM (поскольку он охватывает функции с различным числом аргументов).И, конечно же, для использования с Clojure взаимодействие между Java и лямбдами Scala не помогает.

В более общем смысле, 3 API-интерфейса коллекции (4, если учесть пересмотр в Scala 2.13), вероятно, слишком различныбыть объединенным таким образом.

Я не вижу никакого способа, которым монады как таковые были бы здесь полезны.Если бы я пытался сделать что-то полезное из Clojure, я бы пошел с «проверкой коллекции и типов функций и выполнением некоторого принуждения перед применением функции» в качестве решения. Протоколы могут упростить его, но с некоторыми затратами на производительность.

0 голосов
/ 24 сентября 2018

Я могу дать вам только ответ на Haskell, поскольку я не говорю ни на одном из этих языков.Однако мне кажется, что вы в основном ищете способ автоматически преобразовать входные данные в функцию.Похоже, он не имеет ничего общего с монадами.

(concat (scala-list 1 2) [3 4]) => (scala-list 1 2 3 4)

Если я переведу это на Haskell, я бы дал ему такой тип

concat :: (IsList l1, IsList l2) => l1 elem -> l2 elem -> [elem]

, где ToList - класс типовкоторый просто преобразует этот контейнер в список

class IsListOf a where
    toList :: a elem -> [elem]

Из вашего примера неясно, как вы определитесь с типом вывода, поэтому я не могу помочь с этим.

(map + [1 2] (scala-list 3 4)) => [4 6]

В Haskell эта функция называется не map, а zipWith.Если вы хотите автоматически преобразовать ввод, вы можете сделать это следующим образом.

zipWith :: (IsList l1, IsList l2) => (a -> b -> c) -> l1 a -> l2 b -> [c]

Если вы хотите автоматически преобразовать функцию, вы можете сделать это точно так же.

zipWith :: (IsList l1, IsList l2, Is2Function f) => f a b c -> l1 a -> l2 b -> [c]

Is2Functionопять-таки это класс типов, который просто преобразуется в 2-арную функцию

class Is2Function f where
    toFunction :: f a b c -> a -> b -> c

Есть также кое-что, что следует иметь в виду в отношении обобщений.В предыдущем сообщении я сказал, что не знаю, как вы определитесь с выходом.Это проблема, с которой компилятор также время от времени сталкивается (по крайней мере, в haskell), когда вы делаете много обобщений.Обобщения кажутся приятными на первый взгляд, но они не всегда проясняют ситуацию и могут привести к неоднозначности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...