如何将整数列表从 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
的一些乐趣)我们还需要实现 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
、cons
和 empty
.
使用此设置(您可以省略 uncons
、null
和 take
以及顶部的 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 都理解的东西(String
、Long
、……),请忽略 PersistentVector.fromList
(并修复类型签名)。尝试两者,如上 tail
用于取回列表,head
用于取回 Long
或 String
.
对于包装器和 Frege 函数,请确保类型签名 'match',例如。 G。 PersistentVector a
匹配 [a]
.
前进:我这样做是因为我想将我的一些 Clojure 程序移植到 Frege,“一次一个函数”。我确信我会遇到一些我必须研究的更复杂的数据结构,而且,我仍在研究 Ingo 提出的改进建议。
受上一个问题 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
的一些乐趣)我们还需要实现 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
、cons
和 empty
.
使用此设置(您可以省略 uncons
、null
和 take
以及顶部的 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 都理解的东西(String
、Long
、……),请忽略 PersistentVector.fromList
(并修复类型签名)。尝试两者,如上 tail
用于取回列表,head
用于取回 Long
或 String
.
对于包装器和 Frege 函数,请确保类型签名 'match',例如。 G。 PersistentVector a
匹配 [a]
.
前进:我这样做是因为我想将我的一些 Clojure 程序移植到 Frege,“一次一个函数”。我确信我会遇到一些我必须研究的更复杂的数据结构,而且,我仍在研究 Ingo 提出的改进建议。