diff --git a/ply-loader.cabal b/ply-loader.cabal
--- a/ply-loader.cabal
+++ b/ply-loader.cabal
@@ -1,5 +1,5 @@
 name:                ply-loader
-version:             0.1.0.3
+version:             0.1.1.0
 synopsis:            PLY file loader.
 
 description:         PLY is a lightweight file format for representing 3D
@@ -32,7 +32,8 @@
 
 library
   ghc-options:         -O2 -Wall
-  exposed-modules:     PLY.Data, PLY.Types, PLY.Conf, PLY.Internal.Parsers
+  exposed-modules:     PLY.Data, PLY.Types, PLY.Conf, PLY.Internal.Parsers,
+                       PLY.Internal.StrictReplicate
                        
   build-depends:       base >= 4.6 && < 5, 
                        attoparsec >= 0.10, 
@@ -45,7 +46,6 @@
                        parallel-io >= 0.3.2
   hs-source-dirs:      src
   default-language:    Haskell2010
-  
 
 executable ply2bin
   main-is:          Main.hs
@@ -57,4 +57,3 @@
                     vector >= 0.9,
                     ply-loader
   default-language: Haskell2010
-  
diff --git a/src/PLY/Data.hs b/src/PLY/Data.hs
--- a/src/PLY/Data.hs
+++ b/src/PLY/Data.hs
@@ -20,7 +20,7 @@
 -- > loadConf confFile = loadMeshesV3 confFile "vertex"
 --
 module PLY.Data (PLYData, loadPLY, loadElements, loadElementsV3, 
-                 loadMeshesV3) where
+                 loadMeshesV3, loadHeader) where
 import Control.Applicative
 import Control.Concurrent.ParallelIO (parallel)
 import Control.Lens (view)
@@ -41,6 +41,7 @@
 import PLY.Conf
 import PLY.Internal.Parsers (line, parseSkip, skip, multiProps, 
                              parseScalar, header)
+import PLY.Internal.StrictReplicate
 import PLY.Types
 
 type Header = (Format, [Element])
@@ -53,12 +54,12 @@
   show (PLYData (_,h)) = "PLYData <bytes> " ++ show h
 
 parseASCII :: Element -> Parser (Vector (Vector Scalar))
-parseASCII e = V.replicateM (elNum e) 
-                            (skip *> (V.fromList <$> multiProps (elProps e)))
+parseASCII e = replicateM' (elNum e)
+                           (skip *> (V.fromList <$> multiProps (elProps e)))
 
 parseASCIIv3 :: forall a. PLYType a => Element -> Parser (VS.Vector (V3 a))
 parseASCIIv3 (Element _ n ps@[_,_,_])
-  | all samePropType ps = VS.replicateM n (skip *> (V3 <$> p <*> p <*> p))
+  | all samePropType ps = replicateM' n (skip *> (V3 <$> p <*> p <*> p))
   | otherwise = empty
   where t = plyType (undefined::a)
         p = unsafeUnwrap <$> (parseScalar t <* skipSpace)
@@ -75,8 +76,12 @@
         aux (Partial _) = Left "Incomplete header"
         aux (Done t r) = Right $ PLYData (t, r)
 
--- |@loadElements elementName ply@ loads a 'Vector' of each instance
--- of the requested element array. If you are extracted 3D data,
+-- |Load a PLY header from a file.
+loadHeader :: FilePath -> IO (Either String PLYData)
+loadHeader = fmap loadPLY . BS.readFile
+
+-- |@loadElements elementName ply@ loads a 'Vector' of each vertex of
+-- the requested element array. If you are extracting 3D data,
 -- consider using 'loadElementsV3'.
 loadElements :: ByteString -> PLYData -> 
                 Either String (Vector (Vector Scalar))
@@ -136,4 +141,3 @@
                            IO ([Either String (VS.Vector (V3 a))])
           loadAllMeshes dir = parallel . map (loadMesh dir) . meshes
 {-# INLINABLE loadMeshesV3 #-}
-  
diff --git a/src/PLY/Internal/Parsers.hs b/src/PLY/Internal/Parsers.hs
--- a/src/PLY/Internal/Parsers.hs
+++ b/src/PLY/Internal/Parsers.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE OverloadedStrings, BangPatterns #-}
 module PLY.Internal.Parsers where
 import Control.Applicative
 import Data.Attoparsec.Char8 hiding (char)
@@ -90,11 +90,11 @@
 parseScalar Tfloat  = Sfloat  <$> float
 parseScalar Tdouble = Sdouble <$> double
 
--- Parse a flat property list
+-- |Parse a flat property list
 multiProps :: [Property] -> Parser [Scalar]
 multiProps = go []
   where go acc [] = pure (reverse acc)
-        go acc (ScalarProperty t _:ps) = do x <- parseScalar t
+        go acc (ScalarProperty t _:ps) = do !x <- parseScalar t
                                             skipSpace
                                             go (x:acc) ps
         go _ (ListProperty t _:_) = int >>= flip count (parseScalar t)
diff --git a/src/PLY/Internal/StrictReplicate.hs b/src/PLY/Internal/StrictReplicate.hs
new file mode 100644
--- /dev/null
+++ b/src/PLY/Internal/StrictReplicate.hs
@@ -0,0 +1,29 @@
+{-# LANGUAGE BangPatterns #-}
+module PLY.Internal.StrictReplicate where
+import Data.Vector.Generic (Vector, unstream)
+import Data.Vector.Fusion.Stream (unsafeFromList)
+import Data.Vector.Fusion.Stream.Monadic (Stream (..), Step(..), size, toList)
+import Data.Vector.Fusion.Util (delay_inline)
+import Data.Vector.Fusion.Stream.Size (Size(..))
+
+-- | Yield a 'Stream' of values obtained by performing the monadic
+-- action the given number of times. Each value yielded by teh monadic
+-- action is evaluated to WHNF.
+replicateStreamM' :: Monad m => Int -> m a -> Stream m a
+{-# INLINE [1] replicateStreamM' #-}
+-- NOTE: We delay inlining max here because GHC will create a join point for
+-- the call to newArray# otherwise which is not really nice.
+replicateStreamM' n p = Stream step n (Exact (delay_inline max n 0))
+  where
+    {-# INLINE [0] step #-}
+    step i | i <= 0    = return Done
+           | otherwise = do { !x <- p; return $ Yield x (i-1) }
+
+-- |Execute the monadic action the given number of times and store the
+-- results in a vector. Each value yielded by the monadic action is
+-- evaluated to WHNF.
+replicateM' :: (Monad m, Vector v a) => Int -> m a -> m (v a)
+replicateM' n p = do let s = replicateStreamM' n p
+                     xs <- toList s
+                     return . unstream $ unsafeFromList (size s) xs
+{-# INLINE replicateM' #-}
diff --git a/src/PLY/Types.hs b/src/PLY/Types.hs
--- a/src/PLY/Types.hs
+++ b/src/PLY/Types.hs
@@ -8,21 +8,21 @@
 
 data Format = ASCII | Binary_LE | Binary_BE deriving Show
 
-data Scalar = Schar Int8 
-            | Suchar Word8
-            | Sshort Int16
-            | Sushort Word16
-            | Sint Int
-            | Suint Word32
-            | Sfloat Float
-            | Sdouble Double
+data Scalar = Schar   {-# UNPACK #-}!Int8 
+            | Suchar  {-# UNPACK #-}!Word8
+            | Sshort  {-# UNPACK #-}!Int16
+            | Sushort {-# UNPACK #-}!Word16
+            | Sint    {-# UNPACK #-}!Int
+            | Suint   {-# UNPACK #-}!Word32
+            | Sfloat  {-# UNPACK #-}!Float
+            | Sdouble {-# UNPACK #-}!Double
               deriving Show
 
 data ScalarT = Tchar | Tuchar | Tshort | Tushort | Tint | Tuint 
              | Tfloat | Tdouble deriving (Eq,Show)
 
-data Property = ScalarProperty ScalarT ByteString
-              | ListProperty ScalarT ByteString
+data Property = ScalarProperty !ScalarT !ByteString
+              | ListProperty !ScalarT !ByteString
                 deriving Show
 
 data Element = Element { elName  :: ByteString
@@ -47,3 +47,8 @@
   plyType _ = Tint
   unsafeUnwrap (Sint x) = x
   unsafeUnwrap y = error $ "Tried to unwrap "++show y++" as an Int"
+
+instance PLYType Word8 where
+  plyType _ = Tuchar
+  unsafeUnwrap (Suchar x) = x
+  unsafeUnwrap y = error $ "Tried to unwrap "++show y++" as a Word8"
