如何将整数列表从 Clojure 传递给 Frege 函数?

How to pass a list of integers from Clojure to a Frege function?

受上一个问题 what is the easiest way to pass a list of integers from java to a frege function? 和@Ingo 回答中的评论的启发,我尝试了

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

但得到 (ctor = constructor):

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

有什么想法吗?至少 java.util.List 没有产生 ClassCastException;这是否意味着这是在正确的轨道上?

我几乎可以从 Clojure 发送任何 Java 集合类型给弗雷格,参见 Converting Clojure data structures to Java collections

顺便说一句,使用普通的 (Foo/myfregefunction [1,2,3,4]) 代替产生 ClassCastException clojure.lang.PersistentVector cannot be cast to free.runtime.Lazy,@Ingo 指出,“clojure 列表不是 frege 列表。”转换为 java.util.ArrayList.

时的类似响应

在弗雷格这边,代码是这样的

module Foo where

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

好吧,不知道 Clojure,但是根据你给的 link 我认为你需要给出一个可实例化的 class(即 java.util.ArraList)的名称,因为 [=14] =] 只是一个接口,因此无法构造。

对于 Frege 端,在这种情况下是消费者,假设接口就足够了。

整个事情变得有点复杂,因为 Frege 知道 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 的接口有两个缺点。首先,我们引入了可变性,Frege 编译器不允许我们忽略它,因此接口变得更加复杂。此外,列表数据将被复制。我不知道 Clojure 是怎么做到的,但至少在 Frege 方面,有这段代码遍历迭代器并将数据收集到 Frege 列表中。

所以更好的方法是让 Frege 知道什么是 clojure.lang.PersistentVector 并直接在 Frege 中处理 clojure 数据。我知道有人用 clojure 持久哈希映射这样做过,所以我想应该可以对列表做同样的事情。

(此时我不得不指出贡献一个深思熟虑的 Clojure/Frege 界面库是多么有价值!)

编辑:正如@0dB 的自我回答所暗示的那样,他即将实施前几段中提到的高级解决方案。我鼓励大家通过投票支持这项崇高的事业。

第三种方法是直接在 Clojure 中构造 Frege 列表。

根据@Ingo 的回答,

a better way would be to make Frege aware of what a clojure.lang.PersistentVector is and work directly on the clojure data in Frege.

及其评论以及 Adam Bard 的 PersistentMap 解决方案,我想出了一个可行的解决方案:

module foo.Foo where

[编辑] 正如 Ingo 指出的那样,作为 ListView 的一个实例,我们可以理解列表、头、尾……

instance ListView PersistentVector

我们需要注释 Clojure class 以便在 Frege 中使用(pure native 基本上使 Java 方法可用于 Frege 而无需任何 monad 来处理可变性,这可能是因为——在一般——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

现在有一些函数添加到此数据类型,用于从 Frege 列表或其他方式创建 Clojure 向量:

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

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

注意我使用了 "dot" 符号;请参阅@Dierk 的优秀文章,The power of the dot

[编辑] 对于 ListView(以及 Frege 中使用 PersistentVector 的一些乐趣)我们还需要实现 unconsnulltake(很抱歉这里的快速和肮脏的解决方案;我会尽快解决这个问题):

  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 来避免在 PersistentVector 创建的命名空间中调用 take,以及我如何不必在前缀 fromListtoListconsempty.

使用此设置(您可以省略 unconsnulltake 以及顶部的 instance 声明,如果您不想直接用 Frege 中的 PersistentVector 做任何事情)你现在可以调用一个 Frege 函数,该函数通过正确包装它 returns 一个列表:

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]) 并通过 myfregefn 所做的任何处理(在本例中为 [2 3 4])获得一个 Clojure 向量。如果 myfregefn returns Clojure 和 Frege 都理解的东西(StringLong、……),请忽略 PersistentVector.fromList(并修复类型签名)。尝试两者,如上 tail 用于取回列表,head 用于取回 LongString.

对于包装器和 Frege 函数,请确保类型签名 'match',例如。 G。 PersistentVector a 匹配 [a].

前进:我这样做是因为我想将我的一些 Clojure 程序移植到 Frege,“一次一个函数”。我确信我会遇到一些我必须研究的更复杂的数据结构,而且,我仍在研究 Ingo 提出的改进建议。