packages feed

jvm-streaming 0.2.2 → 0.3

raw patch · 13 files changed

+451/−218 lines, 13 filesdep +criteriondep +deepseqdep +jvm-batchingdep ~basedep ~inline-javadep ~jnibuild-type:Customsetup-changed

Dependencies added: criterion, deepseq, jvm-batching, vector

Dependency ranges changed: base, inline-java, jni, jvm, streaming

Files

README.md view
@@ -1,3 +1,38 @@ # jvm-streaming  Expose Java iterators as streams from the streaming package.++## Using it as a dependency++Add `jvm-streaming` to the list of dependencies in your .cabal file.+Then edit the `Setup.hs` file to add some necessary jars to the+classpath.++```Haskell+import Distribution.Simple+import Language.Java.Inline.Cabal+import qualified Language.Java.Streaming.Jars++main = do+    jars <- Language.Java.Streaming.Jars.getJars+    defaultMainWithHooks (addJarsToClasspath jars simpleUserHooks)+```++Add a `custom-setup` stanza to your .cabal file.++```+custom-setup+  setup-depends:+    base,+    Cabal >= 1.24,+    inline-java,+    jvm-streaming+```++## Layout of source directories++This is a multi-language package. We use+Maven's [standard directory layout][maven-sdl] to organize source code+in multiple languages side-by-side.++[maven-sdl]: https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html
+ Setup.hs view
@@ -0,0 +1,7 @@+import Distribution.Simple+import Language.Java.Inline.Cabal+import qualified Language.Java.Batching.Jars++main = do+    jars <- Language.Java.Batching.Jars.getJars+    defaultMainWithHooks (addJarsToClasspath jars simpleUserHooks)
+ benchmarks/Main.hs view
@@ -0,0 +1,50 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Main where++import Criterion.Main as Criterion+import Control.DeepSeq+import Data.Int+import qualified Data.Vector.Storable as VS+import Language.Java+import Language.Java.Streaming ()+import Streaming (Of, Stream)+import qualified Streaming.Prelude as Streaming++roundtrip :: forall a. (Reify a, Reflect a) => a -> IO ()+roundtrip a = pushWithSizeHint 30 $ (reflect a >>= reify :: IO a) >> pop++intValues :: [Int32]+intValues = [1..4000]++vValues :: [VS.Vector Int32]+vValues = [ VS.fromList [i, i] | i <- [1..2000] ]++benchBatching :: Benchmark+benchBatching = deepseq intValues $+    bgroup "roundtrips"+    [ bgroup "ints"+      [ bgroup "no batching"+        [ bench "list" $ nfIO $ roundtrip intValues+        ]+      , bgroup "batching"+        [ bench "streams" $ nfIO $ roundtrip+            (Streaming.each intValues :: Stream (Of Int32) IO ())+        ]+      ]+    , bgroup "int vectors"+      [ bgroup "no batching"+        [ bench "list" $ nfIO $ roundtrip vValues+        ]+      , bgroup "batching"+        [ bench "streams" $ nfIO $ roundtrip+            (Streaming.each vValues :: Stream (Of (VS.Vector Int32)) IO ())+        ]+      ]+    ]++main :: IO ()+main =+    withJVM ["-Djava.class.path=../jvm-batching/build/libs/jvm-batching.jar"] $+      Criterion.defaultMain [benchBatching]
jvm-streaming.cabal view
@@ -1,5 +1,5 @@ name:                jvm-streaming-version:             0.2.2+version:             0.3 synopsis:            Expose Java iterators as streams from the streaming package. description:         Please see README.md. homepage:            http://github.com/tweag/inline-java/tree/master/jvm-streaming#readme@@ -9,7 +9,7 @@ maintainer:          m@tweag.io copyright:           2015-2016 EURL Tweag. category:            FFI, JVM, Java-build-type:          Simple+build-type:          Custom cabal-version:       >=1.10 extra-source-files:  README.md @@ -18,24 +18,34 @@   location: https://github.com/tweag/inline-java   subdir: jvm-streaming +custom-setup+  setup-depends:+    base,+    Cabal >= 1.24,+    inline-java >= 0.6.3,+    jvm-batching >= 0.1 && <0.2+ library-  hs-source-dirs: src+  hs-source-dirs: src/main/haskell src/cabal/haskell   default-language: Haskell2010   exposed-modules:     Language.Java.Streaming+    Language.Java.Streaming.Jars   build-depends:     base >= 4.7 && < 5,     distributed-closure >= 0.3,-    jni >= 0.4.0 && <0.6,+    jni >= 0.4.0 && <0.7,     jvm >= 0.4.0 && <0.5,-    inline-java >= 0.7 && <0.8,+    jvm-batching >= 0.1 && <0.2,+    inline-java >= 0.8 && <0.9,     singletons >= 2.2,-    streaming >= 0.1.4+    streaming >= 0.1.4,+    vector  test-suite spec   type:     exitcode-stdio-1.0-  hs-source-dirs: tests+  hs-source-dirs: src/test/haskell   main-is: Main.hs   other-modules:     Language.Java.StreamingSpec@@ -48,3 +58,18 @@     jvm-streaming,     streaming   default-language: Haskell2010++benchmark micro-benchmarks+  type: exitcode-stdio-1.0+  main-is: Main.hs+  hs-source-dirs: benchmarks+  build-depends:+    base >=4.8 && <5,+    criterion,+    deepseq,+    jvm,+    jvm-streaming,+    streaming,+    vector+  default-language: Haskell2010+  ghc-options: -threaded
− src/Language/Java/Streaming.hs
@@ -1,158 +0,0 @@--- | Expose Java iterators as streams from the--- <http://hackage.haskell.org/package/streaming streaming> package.--{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE ExistentialQuantification #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE UndecidableInstances #-}--{-# OPTIONS_GHC -fno-warn-orphans #-}-{-# OPTIONS_GHC -fno-warn-unused-top-binds #-}-{-# OPTIONS_GHC -fplugin=Language.Java.Inline.Plugin #-}--module Language.Java.Streaming () where--import Control.Distributed.Closure.TH-import qualified Data.Coerce as Coerce-import Data.IORef (IORef, newIORef, readIORef, writeIORef)-import Data.Int (Int64)-import Data.Singletons (SomeSing(..))-import Data.Word (Word8)-import Foreign.Ptr (FunPtr, Ptr, freeHaskellFunPtr, intPtrToPtr, ptrToIntPtr)-import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)-import qualified Foreign.JNI as JNI-import Foreign.JNI.Types (jnull)-import GHC.Stable-  ( castPtrToStablePtr-  , castStablePtrToPtr-  , deRefStablePtr-  , freeStablePtr-  , newStablePtr-  )-import Language.Java-import Language.Java.Inline-import Streaming (Stream, Of)-import qualified Streaming.Prelude as Streaming--isPoppableStream :: IORef (Stream (Of a) IO ()) -> IO Word8-isPoppableStream ref = do-    readIORef ref >>= Streaming.uncons >>= \case-      Nothing -> return $ fromIntegral $ fromEnum False-      Just (x, stream) -> do-        writeIORef ref (Streaming.cons x stream)-        return $ fromIntegral $ fromEnum True--popStream :: Reflect a => IORef (Stream (Of a) IO ()) -> IO (J (Interp a))-popStream ref = do-    stream <- readIORef ref-    Streaming.uncons stream >>= \case-      Nothing -> do-        exc :: J ('Class "java.util.NoSuchElementException") <- new []-        JNI.throw exc-        return jnull-      Just (x, stream') -> do-        writeIORef ref stream'-        reflect x--type JNIFun a = JNIEnv -> Ptr JObject -> IO a--foreign import ccall "wrapper" wrapObjectFun-  :: JNIFun (Ptr (J ty)) -> IO (FunPtr (JNIFun (Ptr (J ty))))-foreign import ccall "wrapper" wrapBooleanFun-  :: JNIFun Word8 -> IO (FunPtr (JNIFun Word8))---- Export only to get a FunPtr.-foreign export ccall "jvm_streaming_freeIterator" freeIterator-  :: JNIEnv -> Ptr JObject -> Int64 -> IO ()-foreign import ccall "&jvm_streaming_freeIterator" freeIteratorPtr-  :: FunPtr (JNIEnv -> Ptr JObject -> Int64 -> IO ())--data FunPtrTable = forall ty. FunPtrTable-  { hasNextPtr :: FunPtr (JNIFun Word8)-  , nextPtr :: FunPtr (JNIFun (Ptr (J ty)))-  }--freeIterator :: JNIEnv -> Ptr JObject -> Int64 -> IO ()-freeIterator _ _ ptr = do-    let sptr = castPtrToStablePtr $ intPtrToPtr $ fromIntegral ptr-    FunPtrTable{..} <- deRefStablePtr sptr-    freeHaskellFunPtr hasNextPtr-    freeHaskellFunPtr nextPtr-    freeStablePtr sptr--newIterator-  :: Reflect a-  => Stream (Of a) IO ()-  -> IO (J ('Iface "java.util.Iterator"))-newIterator stream = do-    ref <- newIORef stream-    hasNextPtr <- wrapBooleanFun $ \_ _ -> isPoppableStream ref-    nextPtr <- wrapObjectFun $ \_ _ ->-      -- Conversion is safe, because result is always a reflected object.-      unsafeForeignPtrToPtr <$> Coerce.coerce <$> popStream ref-    -- Keep FunPtr's in a table that can be referenced from the Java side, so-    -- that they can be freed.-    tblPtr :: Int64 <- fromIntegral . ptrToIntPtr . castStablePtrToPtr <$> newStablePtr FunPtrTable{..}-    iterator <--      [java| new java.util.Iterator() {-                @Override-                public native boolean hasNext();--                @Override-                public native Object next();--                @Override-                public void remove() {-                    throw new UnsupportedOperationException();-                }--                private native void hsFinalize(long tblPtr);--                @Override-                public void finalize() {-                    hsFinalize($tblPtr);-                }-             } |]-    klass <- JNI.getObjectClass iterator-    JNI.registerNatives klass-      [ JNI.JNINativeMethod-          "hasNext"-          (methodSignature [] (sing :: Sing ('Prim "boolean")))-          hasNextPtr-      , JNI.JNINativeMethod-          "next"-          (methodSignature [] (sing :: Sing ('Class "java.lang.Object")))-          nextPtr-      , JNI.JNINativeMethod-          "hsFinalize"-          (methodSignature [SomeSing (sing :: Sing ('Prim "long"))] (sing :: Sing 'Void))-          freeIteratorPtr-      ]-    return iterator--withStatic [d|-  instance Interpretation (Stream (Of a) m r) where-    type Interp (Stream (Of a) m r) = 'Iface "java.util.Iterator"--  instance Reify a => Reify (Stream (Of a) IO ()) where-    reify itLocal = do-      -- We make sure the iterator remains valid while we reference it.-      it <- JNI.newGlobalRef itLocal-      return $ Streaming.untilRight $ do-        call it "hasNext" [] >>= \case-          False -> return (Right ())-          True -> do-            obj <- call it "next" []-            Left <$> reify (unsafeCast (obj :: JObject))--  instance Reflect a => Reflect (Stream (Of a) IO ()) where-    reflect x = newIterator x-  |]
+ src/cabal/haskell/Language/Java/Streaming/Jars.hs view
@@ -0,0 +1,4 @@+-- | Provides the paths to the jars needed to build with jvm-streaming.+module Language.Java.Streaming.Jars (getJars) where++import Language.Java.Batching.Jars
+ src/main/haskell/Language/Java/Streaming.hs view
@@ -0,0 +1,267 @@+-- | Expose Java iterators as streams from the+-- <http://hackage.haskell.org/package/streaming streaming> package.++{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE RecursiveDo #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE UndecidableInstances #-}++{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# OPTIONS_GHC -fno-warn-unused-top-binds #-}+{-# OPTIONS_GHC -fplugin=Language.Java.Inline.Plugin #-}++module Language.Java.Streaming+  ( reifyStreamWithBatching+  , reflectStreamWithBatching+  ) where++import Control.Distributed.Closure.TH+import Control.Monad.IO.Class (liftIO)+import qualified Data.Coerce as Coerce+import Data.IORef (newIORef, readIORef, writeIORef)+import Data.Int (Int32, Int64)+import Data.Proxy+import qualified Data.Vector as V+import Data.Singletons (SomeSing(..))+import Foreign.Ptr (FunPtr, Ptr, freeHaskellFunPtr, intPtrToPtr, ptrToIntPtr)+import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)+import qualified Foreign.JNI as JNI+import qualified Foreign.JNI.Types as JNI+import Foreign.JNI.Types (jnull)+import GHC.Stable+  ( castPtrToStablePtr+  , castStablePtrToPtr+  , deRefStablePtr+  , freeStablePtr+  , newStablePtr+  )+import Language.Java+import Language.Java.Batching+import Language.Java.Inline+import Streaming (Bifunctor(first), Stream, Of)+import qualified Streaming as Streaming+import qualified Streaming.Prelude as Streaming++imports "io.tweag.jvm.batching.*"+imports "java.util.Iterator"++type JNIFun a = JNIEnv -> Ptr JObject -> IO a++foreign import ccall "wrapper" wrapObjectFun+  :: JNIFun (Ptr (J ty)) -> IO (FunPtr (JNIFun (Ptr (J ty))))++-- Export only to get a FunPtr.+foreign export ccall "jvm_streaming_freeIterator" freeIterator+  :: JNIEnv -> Ptr JObject -> Int64 -> IO ()+foreign import ccall "&jvm_streaming_freeIterator" freeIteratorPtr+  :: FunPtr (JNIEnv -> Ptr JObject -> Int64 -> IO ())++data FunPtrTable = forall ty. FunPtrTable+  { nextPtr :: FunPtr (JNIFun (Ptr (J ty)))+  }++freeIterator :: JNIEnv -> Ptr JObject -> Int64 -> IO ()+freeIterator _ _ ptr = do+    let sptr = castPtrToStablePtr $ intPtrToPtr $ fromIntegral ptr+    FunPtrTable{..} <- deRefStablePtr sptr+    freeHaskellFunPtr nextPtr+    freeStablePtr sptr++-- | Reflects a stream with no batching.+newIterator+  :: forall ty. Stream (Of (J ty)) IO ()+  -> IO (J ('Iface "java.util.Iterator" <> '[ty]))+newIterator stream0 = mdo+    ref <- newIORef stream0+    nextPtr <- wrapObjectFun $ \_ jthis ->+      -- Conversion is safe, because result is always a reflected object.+      unsafeForeignPtrToPtr <$> Coerce.coerce <$> popStream jthis+    -- Keep FunPtr's in a table that can be referenced from the Java side, so+    -- that they can be freed.+    tblPtr :: Int64 <- fromIntegral . ptrToIntPtr . castStablePtrToPtr <$> newStablePtr FunPtrTable{..}+    iterator <-+      [java| new Iterator() {+          /// A field that the Haskell side sets to true when it reaches the end.+          private boolean end = false;+          /// Lookahead element - it always points to a valid element unless+          /// end is true. There is no constructor, so in order to initialize+          // it, next() must be invoked once.+          private Object lookahead;+          @Override+          public boolean hasNext() { return !end; }++          @Override+          public Object next() {+            if (hasNext()) {+              final Object temp = lookahead;+              lookahead = hsNext();+              return temp;+            } else+              throw new java.util.NoSuchElementException();+          }++          @Override+          public void remove() { throw new UnsupportedOperationException(); }++          private native void hsFinalize(long tblPtr);++          private native Object hsNext();++          @Override+          public void finalize() { hsFinalize($tblPtr); }+        } |]+    klass <- JNI.getObjectClass iterator >>= JNI.newGlobalRef+    fieldEndId <- JNI.getFieldID klass "end"+                    (JNI.signature (sing :: Sing ('Prim "boolean")))+    let popStream :: Ptr JObject -> IO (J ty)+        popStream ptrThis = do+          stream <- readIORef ref+          Streaming.uncons stream >>= \case+            Nothing -> do+              jthis <- JNI.objectFromPtr ptrThis+              -- When the stream ends, set the end field to True+              -- so the Iterator knows not to call hsNext again.+              JNI.setBooleanField jthis fieldEndId 1+              return jnull+            Just (x, stream') -> do+              writeIORef ref stream'+              return x+    JNI.registerNatives klass+      [ JNI.JNINativeMethod+          "hsNext"+          (methodSignature [] (sing :: Sing ('Class "java.lang.Object")))+          nextPtr+      , JNI.JNINativeMethod+          "hsFinalize"+          (methodSignature [SomeSing (sing :: Sing ('Prim "long"))] (sing :: Sing 'Void))+          freeIteratorPtr+      ]+    -- Call next once to initialize the iterator.+    () <- [java| { $iterator.next(); } |]+    return $ generic iterator++-- | Reifies streams from iterators in batches of the given size.+reifyStreamWithBatching+  :: forall a. BatchReify a+  => Int32  -- ^ The batch size+  -> J ('Iface "java.util.Iterator" <> '[Interp a])+  -> IO (Stream (Of a) IO ())+reifyStreamWithBatching batchSize jiterator0 = do+    let jiterator1 = unsafeUngeneric jiterator0+    jbatcher <- unsafeUngeneric <$> newBatchWriter (Proxy :: Proxy a)+    jiterator <- [java| new Iterator() {+        private final int batchSize = $batchSize;+        private final Iterator it = $jiterator1;+        private final BatchWriter batcher = $jbatcher;+        public int count = 0;++        @Override+        public boolean hasNext() { return it.hasNext(); }++        @Override+        public Object next() {+          int i = 0;+          batcher.start(batchSize);+          while (it.hasNext() && i < batchSize) {+            batcher.set(i, it.next());+            i++;+          }+          count = i;+          return batcher.getBatch();+        }++        @Override+        public void remove() {+          throw new UnsupportedOperationException();+        }+      } |]+      >>= JNI.newGlobalRef+      :: IO (J ('Iface "java.util.Iterator"))+    cls <- JNI.getObjectClass jiterator >>= JNI.newGlobalRef+    fieldId <- JNI.getFieldID cls "count"+                 (JNI.signature (sing :: Sing ('Prim "int")))++    let go :: Int        -- next element to return from the batch+           -> V.Vector a -- current batch of elements+           -> Stream (Of a) IO ()+        go i v =+          if V.length v == i then do+            hasNext <- liftIO [java| $jiterator.hasNext() |]+            if hasNext then do+              v' <- liftIO $+                [java| $jiterator.next() |] `withLocalRef` \jbatch ->+                  JNI.getIntField jiterator fieldId+                  >>= reifyBatch (unsafeCast (jbatch :: JObject) :: J (Batch a))+              go 0 v'+            else+              liftIO $ do+                JNI.deleteGlobalRef jiterator+                JNI.deleteGlobalRef cls+          else do+            Streaming.yield $ v V.! i+            go (i + 1) v++    return $ go 0 V.empty++-- | Reflects streams to iterators in batches of the given size.+reflectStreamWithBatching+  :: forall a. BatchReflect a+  => Int  -- ^ The batch size+  -> Stream (Of a) IO ()+  -> IO (J ('Iface "java.util.Iterator" <> '[Interp a]))+reflectStreamWithBatching batchSize s0 = do+    jiterator <- unsafeUngeneric <$>+      (reflectStream $ Streaming.mapsM+                        (\s -> first V.fromList <$> Streaming.toList s)+                     $ Streaming.chunksOf batchSize s0+      )+    jbatchReader <- unsafeUngeneric <$> newBatchReader (Proxy :: Proxy a)+    generic <$> [java| new Iterator() {+        private final Iterator it = $jiterator;+        private final BatchReader batchReader = $jbatchReader;+        private int count = 0;++        @Override+        public boolean hasNext() {+          return count < batchReader.getSize() || it.hasNext();+        }+        @Override+        public Object next() {+          if (count == batchReader.getSize()) {+            batchReader.setBatch(it.next());+            count = 0;+          }+          Object o = batchReader.get(count);+          count++;+          return o;+        }+        @Override+        public void remove() {+          throw new UnsupportedOperationException();+        }+      } |]+  where+    reflectStream :: Stream (Of (V.Vector a)) IO ()+                  -> IO (J ('Iface "java.util.Iterator" <> '[Batch a]))+    reflectStream = newIterator . Streaming.mapM reflectBatch++withStatic [d|+  instance Interpretation (Stream (Of a) m r) where+    type Interp (Stream (Of a) m r) = 'Iface "java.util.Iterator"++  instance BatchReify a => Reify (Stream (Of a) IO ()) where+    reify = reifyStreamWithBatching 1024 . generic++  instance BatchReflect a => Reflect (Stream (Of a) IO ()) where+    reflect = fmap unsafeUngeneric . reflectStreamWithBatching 1024+  |]
+ src/test/haskell/Language/Java/StreamingSpec.hs view
@@ -0,0 +1,44 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE QuasiQuotes #-}+{-# OPTIONS_GHC -fplugin=Language.Java.Inline.Plugin #-}++module Language.Java.StreamingSpec where++import Data.Int+import Data.IORef+import Language.Java+import Language.Java.Inline+import Language.Java.Streaming ()+import Test.Hspec+import Streaming (Stream, Of)+import qualified Streaming.Prelude as Streaming++spec :: Spec+spec = do+    describe "iteration" $ do+      it "succeeds on empty lists" $ do+        vals <- reflect [1..0 :: Int32]+        iterator <- [java| java.util.Arrays.asList($vals).iterator() |]+        stream <- reify iterator+        Streaming.toList_ stream `shouldReturn` [1..0 :: Int32]+      it "succeeds on singleton lists" $ do+        vals <- reflect [1 :: Int32]+        iterator <- [java| java.util.Arrays.asList($vals).iterator() |]+        stream <- reify iterator+        Streaming.toList_ stream `shouldReturn` [1 :: Int32]+      it "succeeds on non-trivial lists" $ do+        vals <- reflect [1..10000 :: Int32]+        iterator <- [java| java.util.Arrays.asList($vals).iterator() |]+        stream <- reify iterator+        Streaming.toList_ stream `shouldReturn` [1..10000 :: Int32]+    describe "streams" $ do+      it "have the property that reify . reflect == id" $ do+        iterator <- reflect (Streaming.each [1..10000] :: Stream (Of Int32) IO ())+        stream <- reify iterator+        Streaming.toList_ stream `shouldReturn` [1..10000 :: Int32]+      it "don't redo effects" $ do+        ref <- newIORef (1 :: Int32)+        iterator <- reflect $+          Streaming.replicateM 10 $ atomicModifyIORef ref (\i -> (i + 1, i))+        stream <- reify iterator+        Streaming.toList_ stream `shouldReturn` [1..10 :: Int32]
+ src/test/haskell/Main.hs view
@@ -0,0 +1,11 @@+{-# LANGUAGE OverloadedStrings #-}+module Main where++import Language.Java (withJVM)+import qualified Spec+import Test.Hspec++main :: IO ()+main =+    withJVM ["-Djava.class.path=../jvm-batching/build/libs/jvm-batching.jar"] $+      hspec Spec.spec
+ src/test/haskell/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover -optF --module-name=Spec #-}
− tests/Language/Java/StreamingSpec.hs
@@ -1,44 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE QuasiQuotes #-}-{-# OPTIONS_GHC -fplugin=Language.Java.Inline.Plugin #-}--module Language.Java.StreamingSpec where--import Data.Int-import Data.IORef-import Language.Java-import Language.Java.Inline-import Language.Java.Streaming ()-import Test.Hspec-import Streaming (Stream, Of)-import qualified Streaming.Prelude as Streaming--spec :: Spec-spec = do-    describe "iteration" $ do-      it "succeeds on empty lists" $ do-        vals <- reflect [1..0 :: Int32]-        iterator <- [java| java.util.Arrays.asList($vals).iterator() |]-        stream <- reify iterator-        Streaming.toList_ stream `shouldReturn` [1..0 :: Int32]-      it "succeeds on singleton lists" $ do-        vals <- reflect [1 :: Int32]-        iterator <- [java| java.util.Arrays.asList($vals).iterator() |]-        stream <- reify iterator-        Streaming.toList_ stream `shouldReturn` [1 :: Int32]-      it "succeeds on non-trivial lists" $ do-        vals <- reflect [1..10000 :: Int32]-        iterator <- [java| java.util.Arrays.asList($vals).iterator() |]-        stream <- reify iterator-        Streaming.toList_ stream `shouldReturn` [1..10000 :: Int32]-    describe "streams" $ do-      it "have the property that reify . reflect == id" $ do-        iterator <- reflect (Streaming.each [1..10000] :: Stream (Of Int32) IO ())-        stream <- reify iterator-        Streaming.toList_ stream `shouldReturn` [1..10000 :: Int32]-      it "don't redo effects" $ do-        ref <- newIORef (1 :: Int32)-        iterator <- reflect $-          Streaming.replicateM 10 $ atomicModifyIORef ref (\i -> (i + 1, i))-        stream <- reify iterator-        Streaming.toList_ stream `shouldReturn` [1..10 :: Int32]
− tests/Main.hs
@@ -1,8 +0,0 @@-module Main where--import Language.Java (withJVM)-import qualified Spec-import Test.Hspec--main :: IO ()-main = withJVM [] $ hspec Spec.spec
− tests/Spec.hs
@@ -1,1 +0,0 @@-{-# OPTIONS_GHC -F -pgmF hspec-discover -optF --module-name=Spec #-}