我正在考虑在Clojure和Scala之间进行一些互操作。由于Java本身现在具有lambda,因此我在考虑数据与如何将函数应用于集合之间的概括
clojure.lang.IFn
,并概括了clojure.lang.ISeq
上的收集操作scala.Function
并概括了scala.collection.Traversable
上的收集操作java.util.function.Function
并概括了java.util.stream.Stream
上的收集操作问题:
map
操作,并且该操作如何推广?示例:
(map (scala-fn +)
[1 2 3]
(scala-seq [1 2 3])
(.stream [1 2 3]))
=> (scala-seq [3 6 9])
续(添加了haskell作为标签,以防人们可能知道这种铁杆类型)
Clojure,Scala和Java中都有一些操作来获取一个集合,将一个函数应用于该集合并返回一个新集合。
我对clojure较为熟悉,因此有类似的操作:
(into {} [[:a 1] [:b 2]]) => {:a 1 :b 2}
将clojure向量转换为clojure映射。因为into
操作概括于java.util.List
,所以可以使用任何继承java.util.List
的数据结构。
我希望在Clojure中使用一些Scala库并遇到某些障碍:
scala.Function
,因此需要包装到clojure.lang.IFn
Scala数据结构不继承自java.util.List
,这意味着:
(into {} (scala-list [:a 1] [:b 2]))
不起作用。
我正在寻求重新实现一些基本的Clojure函数,这些函数还包含了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]
f:X->Y
都是通用的。答案 0 :(得分:3)
scala函数扩展了scala.Function并概括了对scala.collection.Traversable的收集操作
java lambdas扩展了java.util.function.Function并概括了java.util.stream.Stream上的收集操作
首先,好消息是:这是不正确的,Java和Scala lambdas可以实现任何SAM(单一抽象方法)接口。这允许将Java lambda与期望scala.FunctionN
的API一起使用,而Scala lambda与期望java.util.function.*
的API一起使用(包括Java流)。据我所知,这种互操作性应在Scala 2.12和更高版本中完成。
一个坏消息(您知道即将到来):当专门谈论Scala集合API时,它也非常依赖于隐式参数,而这些参数在Java或Clojure中并不是真正可用的。同样,Clojure集合API依赖于动态类型,并且IFn
不是SAM类型(因为它涵盖了具有不同数量参数的函数)。当然,从Clojure中使用Java和Scala lambda之间的互操作无济于事。
更笼统地说,这3个集合API(如果算上Scala 2.13中的兑换,则为4个)可能相差太大,无法像这样统一。
我认为单子集本身在这里不会有用。如果我想从Clojure中做一些有用的事情,我会选择“检查集合和函数类型,并在函数应用之前进行一些强制”作为解决方案。 Protocols可以简化它,但是会降低性能。
答案 1 :(得分:1)
我只能给您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中)。从表面上看,泛化看起来不错,但总不能使事情变得更清晰,并可能导致歧义。