Based on the answer by @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.
and comments thereto as well as the solution for PersistentMap by Adam Bard, I came up with a working solution:
module foo.Foo where
[EDIT] As Ingo points out, being an instance of ListView gives us list comprehension, head, tail, …
instance ListView PersistentVector
We need to annotate a Clojure class for use in Frege (pure native
basically makes the Java methods available to Frege without needing any monad to handle mutability, possible because—in general—data is immutable in Clojure, too):
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
Now there follow some functions that are added to this data type for creating a Clojure vector from Frege list or the other way around:
fromList :: [a] -> PersistentVector a
fromList = fold cons empty
toList :: PersistentVector a -> [a]
toList pv = map pv.valAt [0..(pv.length - 1)]
Note my use of the "dot" notation; see the excellent article by @Dierk, The power of the dot.
[EDIT] For ListView
(and some fun in Frege with PersistentVector
) we need to also implement uncons
, null
and take
(sorry for the quick & dirty solutions here; I will try to fix that soon):
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
In my quick & dirty solution above, note the use of PreludeList.take
to avoid calling take
in the namespace that PersistentVector
creates, and how I did not have to prefix fromList
, toList
, cons
and empty
.
With this setup (you can leave out uncons
, null
and take
as well as the instance
declaration at the top, if you don’t want to do anything with PersistentVector
in Frege directly) you can now call a Frege function that takes and returns a list by wrapping it properly:
fromClojure :: PersistentVector a -> PersistentVector a
fromClojure = PersistentVector.fromList • myfregefn • PersistentVector.toList
-- sample (your function here)
myfregefn :: [a] -> [a]
myfregefn = tail
In Clojure we just call (foo.Foo/fromClojure [1 2 3 4])
and get a Clojure vector back with whatever processing myfregefn
does (in this example [2 3 4]
). If myfregefn
returns something that both Clojure and Frege understand (String
, Long
, …), leave out the PersistentVector.fromList
(and fix the type signature). Try both out, tail
as above for getting back a list and head
for getting back, say, a Long
or a String
.
For the wrapper and for your Frege function, make sure the type signatures 'match', e. g. PersistentVector a
matches [a]
.
Moving forward: I am doing this because I would like to port some of my Clojure programs to Frege, “a function at a time“. I am sure I will be encountering some more complex data structures that I will have to look into, and, I am still looking into the suggestions by Ingo to improve things.