packages feed

indexation (empty) → 0.1

raw patch · 17 files changed

+650/−0 lines, 17 filesdep +basedep +bytestringdep +cerealsetup-changed

Dependencies added: base, bytestring, cereal, deferred-folds, focus, foldl, hashable, list-t, potoki-cereal, potoki-core, profunctors, stm-containers, transformers, unordered-containers, vector

Files

+ LICENSE view
@@ -0,0 +1,22 @@+Copyright (c) 2018, Metrix.AI++Permission is hereby granted, free of charge, to any person+obtaining a copy of this software and associated documentation+files (the "Software"), to deal in the Software without+restriction, including without limitation the rights to use,+copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the+Software is furnished to do so, subject to the following+conditions:++The above copyright notice and this permission notice shall be+included in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR+OTHER DEALINGS IN THE SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ indexation.cabal view
@@ -0,0 +1,74 @@+name:+  indexation+version:+  0.1+category:+  Data+synopsis:+  Tools for entity indexation+description:+  A set of tools for indexing entities+homepage:+  https://github.com/nikita-volkov/indexation +bug-reports:+  https://github.com/nikita-volkov/indexation/issues +author:+  Nikita Volkov <nikita.y.volkov@mail.ru>+maintainer:+  Metrix Ninjas <ninjas@metrix.ai>+copyright:+  (c) 2018, Metrix.AI+license:+  MIT+license-file:+  LICENSE+build-type:+  Simple+cabal-version:+  >=1.10++source-repository head+  type:+    git+  location:+    git://github.com/nikita-volkov/indexation.git++library+  hs-source-dirs:+    library+  default-extensions:+    Arrows, BangPatterns, ConstraintKinds, DataKinds, DefaultSignatures, DeriveDataTypeable, DeriveFoldable, DeriveFunctor, DeriveGeneric, DeriveTraversable, EmptyDataDecls, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GADTs, GeneralizedNewtypeDeriving, LambdaCase, LiberalTypeSynonyms, MagicHash, MultiParamTypeClasses, MultiWayIf, NoImplicitPrelude, NoMonomorphismRestriction, OverloadedStrings, PatternGuards, PatternSynonyms, ParallelListComp, QuasiQuotes, RankNTypes, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, TemplateHaskell, TupleSections, TypeApplications, TypeFamilies, TypeOperators, UnboxedTuples+  default-language:+    Haskell2010+  exposed-modules:+    Indexation.Index+    Indexation.IndexTable+    Indexation.EntityTable+    Indexation.IO+    Indexation.Folds+  other-modules:+    Indexation.Instances.Cereal+    Indexation.Potoki.Fetch+    Indexation.Potoki.Transform+    Indexation.Types+    Indexation.Cereal.Get+    Indexation.Cereal.Put+    Indexation.Prelude+    Indexation.Vector+    Indexation.HashMap+  build-depends:+    base >=4.7 && <5,+    bytestring >=0.10.8 && <0.11,+    cereal >=0.5.5 && <0.6,+    deferred-folds >=0.4.1 && <0.5,+    focus >=0.1.5.2 && <0.2,+    foldl >=1 && <2,+    hashable >=1 && <2,+    list-t >=1 && <1.1,+    potoki-cereal >=0.2.1.1 && <0.3,+    potoki-core >=2.2 && <2.3,+    profunctors >=5.2 && <6,+    stm-containers >=0.2.16 && <0.3,+    transformers >=0.4 && <0.6,+    unordered-containers >=0.2.9 && <0.3,+    vector >=0.12 && <0.13
+ library/Indexation/Cereal/Get.hs view
@@ -0,0 +1,49 @@+module Indexation.Cereal.Get+where++import Indexation.Prelude+import Indexation.Types+import Data.Serialize.Get+import qualified Data.HashMap.Strict as A+import qualified Indexation.HashMap as A+import qualified Data.Vector as B+++getIndexHashMap :: (Eq k, Hashable k) => Get k -> Get (HashMap k Int)+getIndexHashMap getKey =+  getSize >>= getAssociations+  where+    getSize = getInt64le+    getAssociations size = foldM step A.empty (enumFromTo 0 (pred (fromIntegral size)))+      where+        step hashMap index = do+          key <- getKey+          return (A.insert key index hashMap)++getIndexTableAsEntityTable :: (Eq entity, Hashable entity) => Get entity -> Get (IndexTable entity)+getIndexTableAsEntityTable getEntity =+  do+    size <- getSize+    associations <- getAssociations size+    return (IndexTable size associations)+  where+    getSize = fromIntegral <$> getInt64le+    getAssociations size = foldM step A.empty (enumFromTo 0 (pred size))+      where+        step hashMap index = do+          entity <- getEntity+          return (A.insert entity index hashMap)++getVector :: Get element -> Get (Vector element)+getVector getElement =+  getSize >>= getElements+  where+    getSize = getInt64le+    getElements size = B.replicateM (fromIntegral size) getElement++getEntityTable :: Get entity -> Get (EntityTable entity)+getEntityTable getEntity =+  EntityTable <$> getVector getEntity++getIndex :: Get (Index entity)+getIndex = Index . fromIntegral <$> getInt64le
+ library/Indexation/Cereal/Put.hs view
@@ -0,0 +1,45 @@+module Indexation.Cereal.Put+where++import Indexation.Prelude+import Indexation.Types+import Data.Serialize.Put+import qualified Data.HashMap.Strict as A+import qualified Indexation.HashMap as A+import qualified Data.Vector as B+++putHashMapWithSize :: Putter k -> Putter v -> Putter (HashMap k v)+putHashMapWithSize keyPutter valuePutter hashMap =+  size *> associations+  where+    size = putInt64le (fromIntegral (A.size hashMap))+    associations = A.traverse_ association hashMap+    association key value = keyPutter key *> valuePutter value++putVector :: Putter element -> Putter (Vector element)+putVector putElement vector =+  putSize *> putElements+  where+    putSize = putInt64le (fromIntegral (B.length vector))+    putElements = traverse_ putElement vector++{-|+It's recommended to use 'putIndexTableAsEntityTable' instead.+The hashmap traversal implementation is inefficient and+the representation of 'EntityTable' is more compact.+-}+putIndexTableDirectly :: Putter entity -> Putter (IndexTable entity)+putIndexTableDirectly putEntity (IndexTable size hashMap) =+  putSize *> putAssociations+  where+    putSize = putInt64le (fromIntegral size)+    putAssociations = A.traverse_ putAssociation hashMap+    putAssociation key entity = putEntity key *> putInt64le (fromIntegral entity)++putEntityTable :: Putter entity -> Putter (EntityTable entity)+putEntityTable putEntity (EntityTable vector) =+  putVector putEntity vector++putIndex :: Putter (Index entity)+putIndex (Index int) = putInt64le (fromIntegral int)
+ library/Indexation/EntityTable.hs view
@@ -0,0 +1,31 @@+module Indexation.EntityTable+(+  EntityTable,+  indexTable,+  lookup,+)+where++import Indexation.Prelude hiding (lookup)+import Indexation.Types+import qualified Indexation.Vector as A+import qualified Data.Vector as B+import qualified Indexation.Cereal.Put as C+import qualified Indexation.Cereal.Get as D+import qualified Data.Serialize as E+++instance E.Serialize entity => E.Serialize (EntityTable entity) where+  put = C.putEntityTable E.put+  get = D.getEntityTable E.get++indexTable :: IndexTable entity -> EntityTable entity+indexTable (IndexTable size table) =+  EntityTable vector+  where+    vector =+      A.indexHashMapWithSize size table++lookup :: Index entity -> EntityTable entity -> Maybe entity+lookup (Index indexPrim) (EntityTable vector) =+  vector B.!? indexPrim
+ library/Indexation/Folds.hs view
@@ -0,0 +1,46 @@+module Indexation.Folds+where++import Indexation.Prelude+import Indexation.Types+import Control.Foldl+import qualified Data.HashMap.Strict as B+import qualified Data.Serialize as C+import qualified Data.ByteString as D+++indexTable :: (Eq entity, Hashable entity) => Fold entity (IndexTable entity)+indexTable =+  Fold step init extract+  where+    init = IndexTable 0 B.empty+    step (IndexTable index map) key =+      case B.lookup key map of+        Just _ -> IndexTable index map+        Nothing -> let+          newMap = B.insert key index map+          newIndex = succ index+          in IndexTable newIndex newMap+    extract = id++serializeToFile :: Serialize entity => FilePath -> FoldM IO entity (Either IOException ())+serializeToFile filePath =+  lmap C.encode (writeBytesToFile filePath)++writeBytesToFile :: FilePath -> FoldM IO ByteString (Either IOException ())+writeBytesToFile filePath =+  FoldM step init exit+  where+    step :: Either IOException Handle -> ByteString -> IO (Either IOException Handle)+    step errorOrFileHandle bytes =+      case errorOrFileHandle of+        Right fileHandle -> try (D.hPut fileHandle bytes $> fileHandle)+        Left exception -> return (Left exception)+    init :: IO (Either IOException Handle)+    init =+      try (openFile filePath WriteMode)+    exit :: Either IOException Handle -> IO (Either IOException ())+    exit errorOrFileHandle =+      case errorOrFileHandle of+        Right fileHandle -> try (hClose fileHandle)+        Left exception -> return (Left exception)
+ library/Indexation/HashMap.hs view
@@ -0,0 +1,13 @@+module Indexation.HashMap+where++import Indexation.Prelude+import Data.HashMap.Strict+++traverse_ :: Applicative effect => (k -> v -> effect ()) -> HashMap k v -> effect ()+traverse_ effect =+  foldrWithKey step init+  where+    init = pure ()+    step k v acc = effect k v *> acc
+ library/Indexation/IO.hs view
@@ -0,0 +1,65 @@+module Indexation.IO+(+  indexProduceToFiles,+  readEntitiesAmountFromEntityTableFile,+)+where++import Indexation.Prelude+import Indexation.Types+import Indexation.Instances.Cereal ()+import qualified Potoki.Core.IO as PotokiIo+import qualified Potoki.Core.Consume as PotokiConsume+import qualified Potoki.Cereal.Consume as PotokiConsume+import qualified Potoki.Cereal.Produce as PotokiProduce+import qualified Indexation.Potoki.Transform as PotokiTransform+import qualified Indexation.Vector as Vector+import qualified STMContainers.Map as StmMap+import qualified Data.Serialize as Cereal+import qualified Data.ByteString as ByteString+++createIndexer :: IO (Indexer entity)+createIndexer = do+  sizeVar <- newTVarIO 0+  map <- StmMap.newIO+  return (Indexer sizeVar map)++createIndexerVector :: Indexer entity -> IO (Vector entity)+createIndexerVector (Indexer sizeVar map) =+  atomically $ do+    size <- readTVar sizeVar+    Vector.listT size (fmap swap (StmMap.stream map))++createIndexerEntityTable :: Indexer entity -> IO (EntityTable entity)+createIndexerEntityTable = fmap EntityTable . createIndexerVector++serializeProduceEntityIndicesToFile :: (Eq entity, Hashable entity) => FilePath -> Indexer entity -> Produce entity -> IO (Either IOException ())+serializeProduceEntityIndicesToFile path indexer entityProduce =+  PotokiIo.produceAndConsume entityProduce $+  PotokiConsume.transform (PotokiTransform.indexConcurrently indexer) $+  PotokiConsume.encodeToFile path++serializeIndexerToFile :: Serialize entity => FilePath -> Indexer entity -> IO (Either IOException ())+serializeIndexerToFile path indexer = do+  entityTable <- createIndexerEntityTable indexer+  let+    produce = PotokiProduce.put (Cereal.put entityTable)+    consume = PotokiConsume.writeBytesToFile path+    in PotokiIo.produceAndConsume produce consume++indexProduceToFiles :: (Eq entity, Hashable entity, Serialize entity) => FilePath {-^ Entity table -} -> FilePath {-^ Index stream -} -> Produce entity -> IO (Either IOException ())+indexProduceToFiles entityTablePath indexStreamPath entityProduce =+  do+    indexer <- createIndexer+    runExceptT $ do+      ExceptT (serializeProduceEntityIndicesToFile indexStreamPath indexer entityProduce)+      ExceptT (serializeIndexerToFile entityTablePath indexer)++readEntitiesAmountFromEntityTableFile :: FilePath -> IO (Either IOException Int)+readEntitiesAmountFromEntityTableFile filePath =+  try $ do+    bytes <- withFile filePath ReadMode (flip ByteString.hGet 4)+    Cereal.runGet Cereal.getInt64le bytes & \ case+      Right x -> return (fromIntegral x)+      Left x -> error ("Unexpected binary parsing error: " <> x)
+ library/Indexation/Index.hs view
@@ -0,0 +1,9 @@+module Indexation.Index+(+  Index(..),+)+where++import Indexation.Prelude+import Indexation.Types+import Indexation.Instances.Cereal
+ library/Indexation/IndexTable.hs view
@@ -0,0 +1,40 @@+module Indexation.IndexTable+(+  IndexTable,+  lookup,+  register,+  empty,+)+where++import Indexation.Prelude hiding (lookup, empty)+import Indexation.Types+import qualified Data.HashMap.Strict as A+import qualified Indexation.Cereal.Get as B+import qualified Indexation.Cereal.Put as C+import qualified Indexation.EntityTable as D+import qualified Data.Serialize as E+++instance (E.Serialize entity, Eq entity, Hashable entity) => E.Serialize (IndexTable entity) where+  put = C.putEntityTable E.put . D.indexTable+  get = B.getIndexTableAsEntityTable E.get++lookup :: (Eq entity, Hashable entity) => entity -> IndexTable entity -> Maybe (Index entity)+lookup entity (IndexTable size hashMap) =+  fmap Index (A.lookup entity hashMap)++register :: (Eq entity, Hashable entity) => entity -> IndexTable entity -> (Index entity, IndexTable entity)+register entity (IndexTable size hashMap) =+  A.lookup entity hashMap & \ case+    Just index -> (Index index, IndexTable size hashMap)+    Nothing -> let+      newSize = succ size+      newHashMap = A.insert entity size hashMap+      index = Index size+      newTable = IndexTable newSize newHashMap+      in (index, newTable)++empty :: (Eq entity, Hashable entity) => IndexTable entity+empty =+  IndexTable 0 mempty
+ library/Indexation/Instances/Cereal.hs view
@@ -0,0 +1,13 @@+module Indexation.Instances.Cereal+where++import Indexation.Prelude+import Indexation.Types+import Data.Serialize+import qualified Indexation.Cereal.Get as Get+import qualified Indexation.Cereal.Put as Put+++instance Serialize (Index a) where+  get = Get.getIndex+  put = Put.putIndex
+ library/Indexation/Potoki/Fetch.hs view
@@ -0,0 +1,36 @@+module Indexation.Potoki.Fetch where++import Indexation.Prelude+import Indexation.Types+import Potoki.Core.Fetch+import qualified Indexation.IndexTable as A+import qualified STMContainers.Map as B+import qualified Focus as C+++index :: (Eq entity, Hashable entity) => IORef (IndexTable entity) -> Fetch entity -> Fetch (Index entity)+index indexTableRef (Fetch entityMaybeIO) =+  Fetch $ do+    entityMaybe <- entityMaybeIO+    case entityMaybe of+      Just entity -> do+        indexTable <- readIORef indexTableRef+        A.register entity indexTable & \ (!index, !newIndexTable) -> do+          writeIORef indexTableRef newIndexTable+          return (Just index)+      Nothing -> return Nothing++indexConcurrently :: (Eq entity, Hashable entity) => Indexer entity -> Fetch entity -> Fetch (Index entity)+indexConcurrently (Indexer sizeVar map) (Fetch entityMaybeIO) =+  Fetch $ do+    entityMaybe <- entityMaybeIO+    case entityMaybe of+      Just entity -> fmap Just $ atomically $ B.focus strategy entity map+      Nothing -> return Nothing+  where+    strategy = \ case+      Just indexInt -> return (Index indexInt, C.Keep)+      Nothing -> do+        size <- readTVar sizeVar+        writeTVar sizeVar $! succ size+        return (Index size, C.Replace size)
+ library/Indexation/Potoki/Transform.hs view
@@ -0,0 +1,12 @@+module Indexation.Potoki.Transform where++import Indexation.Prelude hiding (runState)+import Indexation.Types+import Potoki.Core.Transform+import qualified Indexation.Potoki.Fetch as A+import qualified Indexation.IndexTable as B+import qualified STMContainers.Map as C+++indexConcurrently :: (Eq entity, Hashable entity) => Indexer entity -> Transform entity (Index entity)+indexConcurrently indexer = Transform (return . A.indexConcurrently indexer)
+ library/Indexation/Prelude.hs view
@@ -0,0 +1,124 @@+module Indexation.Prelude+(+  module Exports,+)+where+++-- base+-------------------------+import Control.Applicative as Exports+import Control.Arrow as Exports+import Control.Category as Exports+import Control.Concurrent as Exports+import Control.Exception as Exports+import Control.Monad as Exports hiding (mapM_, sequence_, forM_, msum, mapM, sequence, forM)+import Control.Monad.IO.Class as Exports+import Control.Monad.Fix as Exports hiding (fix)+import Control.Monad.ST as Exports+import Data.Bits as Exports+import Data.Bool as Exports+import Data.Char as Exports+import Data.Coerce as Exports+import Data.Complex as Exports+import Data.Data as Exports+import Data.Dynamic as Exports+import Data.Either as Exports+import Data.Fixed as Exports+import Data.Foldable as Exports+import Data.Function as Exports hiding (id, (.))+import Data.Functor as Exports+import Data.Int as Exports+import Data.IORef as Exports+import Data.Ix as Exports+import Data.List as Exports hiding (sortOn, isSubsequenceOf, uncons, concat, foldr, foldl1, maximum, minimum, product, sum, all, and, any, concatMap, elem, foldl, foldr1, notElem, or, find, maximumBy, minimumBy, mapAccumL, mapAccumR, foldl')+import Data.Maybe as Exports+import Data.Monoid as Exports hiding (Last(..), First(..))+import Data.Ord as Exports+import Data.Proxy as Exports+import Data.Ratio as Exports+import Data.STRef as Exports+import Data.String as Exports+import Data.Traversable as Exports+import Data.Tuple as Exports+import Data.Unique as Exports+import Data.Version as Exports+import Data.Word as Exports+import Debug.Trace as Exports+import Foreign.ForeignPtr as Exports+import Foreign.Ptr as Exports+import Foreign.StablePtr as Exports+import Foreign.Storable as Exports+import GHC.Conc as Exports hiding (withMVar, threadWaitWriteSTM, threadWaitWrite, threadWaitReadSTM, threadWaitRead)+import GHC.Exts as Exports (lazy, inline, sortWith, groupWith)+import GHC.Generics as Exports (Generic)+import GHC.IO.Exception as Exports+import Numeric as Exports+import Prelude as Exports hiding (concat, foldr, mapM_, sequence_, foldl1, maximum, minimum, product, sum, all, and, any, concatMap, elem, foldl, foldr1, notElem, or, mapM, sequence, id, (.))+import System.Environment as Exports+import System.Exit as Exports+import System.IO as Exports+import System.IO.Error as Exports+import System.IO.Unsafe as Exports+import System.Mem as Exports+import System.Mem.StableName as Exports+import System.Timeout as Exports+import Text.ParserCombinators.ReadP as Exports (ReadP, ReadS, readP_to_S, readS_to_P)+import Text.ParserCombinators.ReadPrec as Exports (ReadPrec, readPrec_to_P, readP_to_Prec, readPrec_to_S, readS_to_Prec)+import Text.Printf as Exports (printf, hPrintf)+import Text.Read as Exports (Read(..), readMaybe, readEither)+import Unsafe.Coerce as Exports++-- transformers+-------------------------+import Control.Monad.IO.Class as Exports+import Control.Monad.Trans.Class as Exports+import Control.Monad.Trans.Cont as Exports hiding (shift, callCC)+import Control.Monad.Trans.Except as Exports (ExceptT(ExceptT), Except, except, runExcept, runExceptT, mapExcept, mapExceptT, withExcept, withExceptT)+import Control.Monad.Trans.Maybe as Exports+import Control.Monad.Trans.Reader as Exports (Reader, runReader, mapReader, withReader, ReaderT(ReaderT), runReaderT, mapReaderT, withReaderT)+import Control.Monad.Trans.State.Strict as Exports (State, runState, evalState, execState, mapState, withState, StateT(StateT), runStateT, evalStateT, execStateT, mapStateT, withStateT, state)+import Control.Monad.Trans.Writer.Strict as Exports (Writer, runWriter, execWriter, mapWriter, WriterT(..), execWriterT, mapWriterT)++-- deferred-folds+-------------------------+import DeferredFolds.Unfold as Exports (Unfold(..))++-- foldl+-------------------------+import Control.Foldl as Exports (Fold(..), FoldM(..))++-- hashable+-------------------------+import Data.Hashable as Exports (Hashable(..))++-- unordered-containers+-------------------------+import Data.HashMap.Strict as Exports (HashMap)++-- vector+-------------------------+import Data.Vector as Exports (Vector)++-- cereal+-------------------------+import Data.Serialize as Exports (Serialize)++-- bytestring+-------------------------+import Data.ByteString as Exports (ByteString)++-- profunctors+-------------------------+import Data.Profunctor as Exports hiding (WrappedArrow(..))++-- list-t+-------------------------+import ListT as Exports (ListT(..))++-- potoki-core+-------------------------+import Potoki.Core.Produce as Exports (Produce)+import Potoki.Core.Consume as Exports (Consume)+import Potoki.Core.Transform as Exports (Transform)+import Potoki.Core.Fetch as Exports (Fetch)
+ library/Indexation/Types.hs view
@@ -0,0 +1,14 @@+module Indexation.Types+where++import Indexation.Prelude+import qualified STMContainers.Map as A+++data Indexer entity = Indexer (TVar Int) (A.Map entity Int)++data IndexTable entity = IndexTable {-# UNPACK #-} !Int {-# UNPACK #-} !(HashMap entity Int)++newtype EntityTable entity = EntityTable (Vector entity)++newtype Index entity = Index Int
+ library/Indexation/Vector.hs view
@@ -0,0 +1,55 @@+module Indexation.Vector+where++import Indexation.Prelude+import Indexation.Types+import Data.Vector+import qualified Data.Vector.Mutable as A+import qualified Data.HashMap.Strict as B+import qualified ListT+import qualified STMContainers.Map as StmMap+++{-|+This function is not tested.+-}+{-# NOINLINE populate #-}+populate :: Monad effect => Int -> effect (Int, element) -> effect (Vector element)+populate size effect =+  do+    mv <- return (unsafeDupablePerformIO (A.unsafeNew size))+    let+      loop stepsRemaining =+        if stepsRemaining > 0+          then do+            (index, element) <- effect+            () <- return (unsafeDupablePerformIO (A.write mv index element))+            loop (pred stepsRemaining)+          else do+            !v <- return (unsafeDupablePerformIO (freeze mv))+            return v+      in loop size++{-|+This function is partial. It doesn't check the size or indices.+-}+{-# INLINE indexHashMapWithSize #-}+indexHashMapWithSize :: Int -> HashMap element Int -> Vector element+indexHashMapWithSize size hashMap =+  unsafePerformIO $ do+    mv <- A.unsafeNew size+    let+      step () element index = unsafeDupablePerformIO (A.write mv index element)+      !() = B.foldlWithKey' step () hashMap+      in freeze mv++{-# NOINLINE listT #-}+listT :: Monad m => Int -> ListT m (Int, element) -> m (Vector element)+listT size listT =+  let+    step mv (index, element) = return (unsafeDupablePerformIO (A.write mv index element $> mv))+    in do+      !mv <- return (unsafeDupablePerformIO (A.unsafeNew size))+      ListT.fold step mv listT+      !iv <- return (unsafeDupablePerformIO (unsafeFreeze mv))+      return iv