Как передать список целых чисел из Clojure в функцию Фреге?

Вдохновленный предыдущим вопросом, как проще всего передать список целых чисел из Java в функцию frege? и комментарий в ответах @Ingo, я пытался

(Foo/myfregefunction (java.util.List. [1,2,3,4]))

но получить (ctor = конструктор):

CompilerException java.lang.IllegalArgumentException: No matching ctor found for interface java.util.List

Есть идеи? По крайней мере java.util.List не выдал ClassCastException; Означает ли это, что это на правильном пути?

Я могу отправить Фреге практически любой тип коллекции Java из Clojure, см. Преобразование структур данных Clojure в коллекции Java.

Кстати, используя простой (Foo/myfregefunction [1,2,3,4]) вместо этого дает ClassCastException clojure.lang.PersistentVector cannot be cast to free.runtime.Lazy, на что @Ingo указывает: "Список перестановок не является списком Фреге". Подобный ответ при приведении java.util.ArrayList,

На стороне Фреге, код что-то вроде

module Foo where

myfregefunction :: [Int] -> Int
-- do something with the list here

2 ответа

Решение

Хорошо, не зная Clojure, но по ссылке, которую вы дали, я так понимаю, вам нужно дать имя экземпляра класса (т.е. java.util.ArraList) поскольку java.util.List это просто интерфейс и поэтому не может быть построен.

Для стороны Фреге, которая в этом случае является потребителем, достаточно предположить интерфейс.

Все становится немного сложнее, так как Фреге знает, что списки Java являются изменяемыми. Это означает, что не может быть чистой функции

∀ s a. Mutable s (List a) → [a]

и каждая попытка написать такую ​​функцию на чистом языке должна потерпеть неудачу и будет отклонена компилятором.

Вместо этого нам нужно ST действие, чтобы обернуть чистую часть (в этом случае, ваша функция myfregefunction). ST это монада, которая позволяет иметь дело с изменчивыми данными. Это будет выглядеть так:

import Java.Util(List, Iterator)   -- java types we need

fromClojure !list = 
    List.iterator list >>= _.toList >>= pure . myfregefunction

Из clojure теперь вы можете вызывать что-то вроде (простите, если я неправильно понял синтаксис clojure (правка приветствуется)):

(frege.prelude.PreludeBase$TST/run (Foo/fromClojure (java.util.ArrayList. [1,2,3,4])))

Это взаимодействие через Java имеет два недостатка, IMHO. Например, мы вводим изменчивость, которую компилятор Фреге не позволяет нам игнорировать, поэтому интерфейс становится более сложным. И, кроме того, данные списка будут продублированы. Я не знаю, как это делает Clojure, но, по крайней мере, на стороне Фреге есть этот код, который перебирает итератор и собирает данные в список Фреге.

Так что лучшим способом было бы донести до Фреге clojure.lang.PersistentVector есть и работаю непосредственно на данных clojure во Фреге. Я знаю кого-то, кто сделал это с помощью clojure персистентных хеш-карт, поэтому я думаю, что можно сделать то же самое для списков.

(На данный момент я не могу не указать, насколько ценным было бы создание хорошо продуманной библиотеки интерфейса Clojure/Frege!)

Редактировать: Как предполагает самоответ от @0dB, он собирается реализовать превосходное решение, упомянутое в предыдущих параграфах. Я призываю всех поддержать это благородное начинание с помощью голосов.

Третий способ - построить список Фреге непосредственно в Clojure.

Основываясь на ответе @Ingo,

лучшим способом было бы информировать Фреге о том, что такое clojure.lang.PersistentVector, и работать непосредственно с данными clojure во Фреге.

и комментарии к нему, а также решение для PersistentMap от Адама Барда, я придумал рабочее решение:

module foo.Foo where

[РЕДАКТИРОВАТЬ] Как указывает Инго, будучи экземпляром ListView дает нам понимание списка, голову, хвост,...

instance ListView PersistentVector

Нам нужно аннотировать класс Clojure для использования во Фреге (pure native в основном делает методы Java доступными для Фреге, не нуждаясь в какой-либо монаде для обработки изменчивости, возможно потому, что - в общем случае данные неизменны и в Clojure):

data PersistentVector a = native clojure.lang.IPersistentVector where
  -- methods needed to create new instances
  pure native empty clojure.lang.PersistentVector.EMPTY :: PersistentVector a
  pure native cons :: PersistentVector a -> a -> PersistentVector a
  -- methods needed to transform instance into Frege list
  pure native valAt :: PersistentVector a -> Int -> a
  pure native length :: PersistentVector a -> Int

Теперь следуйте некоторым функциям, которые добавляются к этому типу данных для создания вектора Clojure из списка Фреге или наоборот:

  fromList :: [a] -> PersistentVector a
  fromList = fold cons empty

  toList :: PersistentVector a -> [a]
  toList pv = map pv.valAt [0..(pv.length - 1)]

Обратите внимание, что я использовал обозначение "точка"; см. отличную статью @Dierk, Сила точки.

[РЕДАКТИРОВАТЬ] Для ListView (и повеселиться во Фреге с PersistentVector) нам нужно также реализовать uncons, null а также take (извините за быстрые и грязные решения здесь; я постараюсь исправить это в ближайшее время):

  null :: PersistentVector a -> Bool
  null x = x.length == 0

  uncons :: PersistentVector a -> Maybe (a, PersistentVector a)
  uncons x
    | null x = Nothing
    -- quick & dirty (using fromList, toList); try to use first and rest from Clojure here
    | otherwise = Just (x.valAt 0, fromList $ drop 1 $ toList x)

  take :: Int -> PersistentVector a -> PersistentVector a
  -- quick and dirty (using fromList, toList); improve this
  take n = fromList • PreludeList.take n • toList

В моем быстром и грязном решении выше, обратите внимание на использование PreludeList.take чтобы не звонить take в пространстве имен, которое PersistentVector создает, а как мне не надо префикс fromList, toList, cons а также empty,

С помощью этой настройки (вы можете оставить uncons, null а также take так же хорошо как instance объявление вверху, если вы не хотите ничего делать с PersistentVector непосредственно во Фреге) теперь вы можете вызывать функцию Фреге, которая принимает и возвращает список, упаковав его правильно:

fromClojure :: PersistentVector a -> PersistentVector a
fromClojure = PersistentVector.fromList • myfregefn • PersistentVector.toList

-- sample (your function here)
myfregefn :: [a] -> [a]
myfregefn = tail

В Clojure мы просто звоним (foo.Foo/fromClojure [1 2 3 4]) и получить вектор Clojure с любой обработкой myfregefn делает (в этом примере [2 3 4]). Если myfregefn возвращает то, что понимают и Clojure, и Frege (String, Long…), Пропустите PersistentVector.fromList (и исправьте тип подписи). Попробуйте оба, tail как указано выше для получения списка и head чтобы вернуться, скажем, Long или String,

Для обертки и для вашей функции Frege убедитесь, что сигнатуры типов совпадают, например, PersistentVector a Матчи [a],

Двигаясь вперед: я делаю это, потому что я хотел бы перенести некоторые из моих программ Clojure на Фреге, "функцию за раз". Я уверен, что столкнусь с некоторыми более сложными структурами данных, которые мне придется изучить, и я все еще изучаю предложения Ingo по улучшению ситуации.

Другие вопросы по тегам