根据@Ingo 的回答,
更好的方法是让 Frege 了解 clojure.lang.PersistentVector 是什么,并直接处理 Frege 中的 clojure 数据。
和 cmets 以及 Adam Bard 的 PersistentMap 解决方案,我想出了一个可行的解决方案:
module foo.Foo where
[EDIT] 正如 Ingo 所指出的,作为 ListView 的一个实例,我们可以理解列表,头,尾,......
instance ListView PersistentVector
我们需要注释一个 Clojure 类以在 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)]
注意我对“点”符号的使用;请参阅@Dierk,The power of the dot 的精彩文章。
[编辑] 对于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 以避免在PersistentVector 创建的命名空间中调用take,以及我如何不必为fromList、toList、@ 添加前缀987654338@和empty。
使用此设置(如果您不想直接在 Frege 中对 PersistentVector 执行任何操作,则可以省略 uncons、null 和 take 以及顶部的 instance 声明) 你现在可以调用一个 Frege 函数,该函数通过正确包装一个列表并返回一个列表:
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 返回 Clojure 和 Frege 都能理解的内容(String、Long、...),请忽略 PersistentVector.fromList(并修复类型签名)。试试这两种方法,tail 如上所述用于返回列表,head 用于返回,例如 Long 或 String。
对于包装器和您的 Frege 函数,请确保类型签名“匹配”,例如。 G。 PersistentVector a 匹配 [a]。
向前迈进:我这样做是因为我想将我的一些 Clojure 程序移植到 Frege,“一次一个函数”。我确信我会遇到一些我必须研究的更复杂的数据结构,而且我仍在研究 Ingo 提出的改进建议。