I'm looking at doing some interop between clojure and scala. As java itself now has lambdas, I was thinking of a generalisation between data and how to apply a function to a collection
clojure.lang.IFn
and generalises collection operations on clojure.lang.ISeq
scala.Function
and generalises collection operations on scala.collection.Traversable
java.util.function.Function
and generalises collection operations on java.util.stream.Stream
Questions:
map
operation be implemented across all collection types and how might this be generalisable?Example:
(map (scala-fn +)
[1 2 3]
(scala-seq [1 2 3])
(.stream [1 2 3]))
=> (scala-seq [3 6 9])
Continued (added haskell as a tag just in case the hardcore type people might know)
There are operations in both Clojure, Scala and Java that take a collection, applies a function to that collection and returns a new collection.
I'm more familiar with clojure, so there are operations like:
(into {} [[:a 1] [:b 2]]) => {:a 1 :b 2}
Which converts a clojure vector into a clojure map. Because the into
operation generalises on java.util.List
any datastructure that inherits java.util.List
can be used.
I wish to work with some scala libraries in clojure and face certain obstacles:
scala.Function
and so need to be wrapped to clojure.lang.IFn
Scala datastructures do not inherit from java.util.List
which means that:
(into {} (scala-list [:a 1] [:b 2]))
will not work.
I'm looking to reimplement some basic clojure functions that also incorporate scala datastructures. (map, reduce, mapcat, etc...)
The functionality would look something like:
(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
is universal.scala functions extend scala.Function and generalises collection operations on scala.collection.Traversable
java lambdas extend java.util.function.Function and generalises collection operations on java.util.stream.Stream
First, the good news: this isn't correct, Java and Scala lambdas can implement any SAM (single abstract method) interface. This allows using Java lambdas with APIs expecting scala.FunctionN
and Scala lambdas with APIs expecting java.util.function.*
(including Java streams). This interoperability should be complete in Scala 2.12 and later (so far as I know).
The bad news (you knew this was coming): when talking about Scala collection API specifically, it also very much relies on implicit parameters, and those aren't really usable from Java or Clojure. Similarly, Clojure collection API relies on dynamic typing, and IFn
isn't a SAM type (because it covers functions with different numbers of arguments). And of course, for use from Clojure, interoperation between Java and Scala lambdas doesn't help.
More generally speaking, the 3 collection APIs (4 if you count the redesing coming in Scala 2.13) are probably too different to be unified like that.
I don't see any way in which monads per se would be useful here. If I was trying to do something usable from Clojure, I'd go with "checking the collection and function types and doing some coercing before function application" as the solution. Protocols could simplify it, but with some performance cost.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With