summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorclinton <>2018-01-14 09:07:00 (GMT)
committerhdiff <hdiff@hdiff.luite.com>2018-01-14 09:07:00 (GMT)
commit73ec45d8375d65a08a77630ac09045145934afef (patch)
tree73ed5c2a7e6fe266e79c03a749ba5a5608e131cf
version 0.1.0.00.1.0.0
-rw-r--r--LICENSE30
-rw-r--r--Setup.hs2
-rw-r--r--io-string-like.cabal53
-rw-r--r--src/System/IO/StringLike/GetContents.hs7
-rw-r--r--src/System/IO/StringLike/GetLine.hs9
-rw-r--r--src/System/IO/StringLike/Impl.hs317
-rw-r--r--src/System/IO/StringLike/PutStr.hs7
-rw-r--r--src/System/IO/StringLike/PutStrLn.hs9
8 files changed, 434 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..f598e61
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Clinton Mead (c) 2018
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Clinton Mead nor the names of other
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
index 0000000..9a994af
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/io-string-like.cabal b/io-string-like.cabal
new file mode 100644
index 0000000..b4a3400
--- /dev/null
+++ b/io-string-like.cabal
@@ -0,0 +1,53 @@
+-- This file has been generated from package.yaml by hpack version 0.20.0.
+--
+-- see: https://github.com/sol/hpack
+--
+-- hash: 6d2238ec61b205b3bc42dee2863a5ec675ee3ea54b2cf1a906749d83ff7e4704
+
+name: io-string-like
+version: 0.1.0.0
+synopsis: Classes to handle Prelude style IO functions for different datatypes
+description: The functions in the Prelude such as "getContents", "putStr" only work for plain Strings.
+ .
+ There are similar functions in "ByteString" for reading and writing, as well as "Text".
+ .
+ This requires one to import the appropriate functions, usually qualified, for the particular datatype one is using. Changing the datatype at the very least involves changing import statements across your program.
+ .
+ The package introduces classes to overload functions like "getContents", "putStr" over multiple datatypes, so implementations can be changed easily.
+ .
+ All the code documentation is in "System.IO.StringLike.Impl".
+ .
+ All the other modules are just re-exports.
+category: Data
+homepage: https://github.com/clintonmead/io-string-like#readme
+bug-reports: https://github.com/clintonmead/io-string-like/issues
+author: Clinton Mead
+maintainer: clintonmead@gmail.com
+copyright: Copyright: (c) 2018 Clinton Mead
+license: BSD3
+license-file: LICENSE
+build-type: Simple
+cabal-version: >= 1.10
+
+source-repository head
+ type: git
+ location: https://github.com/clintonmead/io-string-like
+
+library
+ hs-source-dirs:
+ src
+ ghc-options: -Wall
+ build-depends:
+ base >=4.7 && <5
+ , binary
+ , bytestring
+ , text
+ exposed-modules:
+ System.IO.StringLike.GetContents
+ System.IO.StringLike.GetLine
+ System.IO.StringLike.Impl
+ System.IO.StringLike.PutStr
+ System.IO.StringLike.PutStrLn
+ other-modules:
+ Paths_io_string_like
+ default-language: Haskell2010
diff --git a/src/System/IO/StringLike/GetContents.hs b/src/System/IO/StringLike/GetContents.hs
new file mode 100644
index 0000000..7f31c28
--- /dev/null
+++ b/src/System/IO/StringLike/GetContents.hs
@@ -0,0 +1,7 @@
+module System.IO.StringLike.GetContents (
+ CanGetContents, CanGetContentsClass (hGetContents), getContents, readFile, interact
+ ) where
+
+import Prelude ()
+
+import System.IO.StringLike.Impl
diff --git a/src/System/IO/StringLike/GetLine.hs b/src/System/IO/StringLike/GetLine.hs
new file mode 100644
index 0000000..1db74b5
--- /dev/null
+++ b/src/System/IO/StringLike/GetLine.hs
@@ -0,0 +1,9 @@
+module System.IO.StringLike.GetLine (
+ module System.IO.StringLike.GetContents,
+ CanGetLine, CanGetLineClass (hGetLine), getLine
+ ) where
+
+import Prelude ()
+
+import System.IO.StringLike.GetContents
+import System.IO.StringLike.Impl
diff --git a/src/System/IO/StringLike/Impl.hs b/src/System/IO/StringLike/Impl.hs
new file mode 100644
index 0000000..c6a026d
--- /dev/null
+++ b/src/System/IO/StringLike/Impl.hs
@@ -0,0 +1,317 @@
+{-# LANGUAGE DefaultSignatures #-}
+{-# LANGUAGE TypeSynonymInstances #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE UndecidableInstances #-}
+{-# OPTIONS_GHC -Wno-unticked-promoted-constructors #-}
+
+{-|
+The classes in this library roughly encapulates things you can read or write
+through a "handle", typically writing something "string like" to a file,
+but the interface is very flexible.
+
+Currently only 'String', 'ByteString' and 'Text' in their strict/lazy varieties
+and builders are implemented.
+
+However, the interface has the flexibility to write non-character data.
+For example an implementation for a @[Int]@ could just write all the 'Int's in
+binary to the file and read them back into a @[Int]@.
+
+Also the interface can be defined for "handles" other than standard files.
+Currently the only such instance that does not use 'Handle' is for 'hGetContents',
+where one can call directly:
+
+> hGetContents fileName
+
+In this case, "handle" is a 'FilePath'.
+
+The initial motivation of to avoid having to change all your function
+calls if you change your string type, but it's actually more general than that now.
+
+The point of this library is to be able to read and write raw data to the disk
+(and perhaps other places) quickly without thinking too much about it.
+
+It's not intended to be a replacement for say 'Binary'. It's intended to be lower
+level. 'Binary' internally creates lazy bytestrings for the user to write to the
+disk, this library is instead just about directly writing raw data.
+
+Note that we currently only have classes for
+
+Most of the functions should be fairly self explanatory having the same meaning
+as in "Prelude" or "System.IO" but more general.
+-}
+module System.IO.StringLike.Impl (
+ CanGetContents, CanGetContentsClass (hGetContents), getContents, readFile,
+ CanGetLine, CanGetLineClass (hGetLine), getLine,
+ CanPutStr, CanPutStrClass (hPutStr), putStr, writeFile, appendFile,
+ interact,
+ CanPutStrLn, CanPutStrLnClass (hPutStrLn), putStrLn,
+ CanProxyT, CanProxyTo (canProxyTo), CanProxyFrom (canProxyFrom)
+ ) where
+
+import Prelude hiding (
+ readFile,
+ writeFile,
+ appendFile,
+ getContents,
+ putStr,
+ interact,
+ getLine,
+ putStrLn
+ )
+
+import qualified System.IO as IO
+import System.IO (Handle, IOMode(ReadMode))
+import Data.Semigroup ((<>))
+
+import qualified Data.ByteString.Char8 as BSS
+import qualified Data.ByteString.Lazy.Char8 as BSL
+import qualified Data.ByteString.Builder as BSB
+
+import qualified Data.Text as TS
+import qualified Data.Text.IO as TS
+
+import qualified Data.Text.Lazy as TL
+import qualified Data.Text.Lazy.IO as TL
+
+import qualified Data.Text.Lazy.Builder as TB
+
+--------------------------
+-- CanGetContents Class --
+--------------------------
+
+{-|
+'CanGetContents' if effectively the following type synonym, for the common case of
+an ordinary 'Handle' in the 'IO' monad like so.
+
+> type CanGetContents t = CanGetContentsClass IO Handle t
+
+The reason why it's defined as a class with a catch all instance instead is so
+modules which use 'CanGetContents' do not have to include the language pragma "FleixbleContexts".
+
+However, this approach requires this module to use the pragma "UndecidableInstances",
+but I figure it's better to add more complexity to the library than to clients.
+
+'CanGet', 'CanGetLine', 'CanPutStr', 'CanPutStrLn' all are similar synonyms with
+the same explanation for their definition so I won't repeat myself.
+-}
+--type CanGetContents t = CanGetContentsClass IO Handle t
+class CanGetContentsClass IO Handle t => CanGetContents t
+instance CanGetContentsClass IO Handle t => CanGetContents t
+
+class Monad m => CanGetContentsClass m handleT t where
+ {-| Generalised 'System.IO.hGetContents' -}
+ hGetContents :: handleT -> m t
+ default hGetContents :: (CanProxyFrom t, CanGetContentsClass m handleT (CanProxyT t)) => handleT -> m t
+ hGetContents = (canProxyFrom <$>) . hGetContents
+
+getContents :: CanGetContents t => IO t
+getContents = hGetContents IO.stdin
+
+readFile :: CanGetContents t => FilePath -> IO t
+readFile filePath = IO.withFile filePath ReadMode hGetContents
+
+instance (m ~ IO, CanGetContentsClass m Handle t) => CanGetContentsClass m FilePath t where
+ hGetContents = readFile
+
+----------------------
+-- CanGetLine class --
+----------------------
+
+{-| Effective type synonym for 'CanGetLineClass'. See 'CanGetContents' for more details. -}
+--type CanGetLine t = CanGetLineClass IO Handle t
+class CanGetLineClass IO Handle t => CanGetLine t
+instance CanGetLineClass IO Handle t => CanGetLine t
+
+class CanGetContentsClass m handleT t => CanGetLineClass m handleT t where
+ {-| Generalised 'System.IO.hGetLine' -}
+ hGetLine :: handleT -> m t
+ default hGetLine :: (CanProxyFrom t, CanGetLineClass m handleT (CanProxyT t)) => handleT -> m t
+ hGetLine = (canProxyFrom <$>) . hGetLine
+
+getLine :: CanGetLine t => IO t
+getLine = hGetLine IO.stdin
+
+------------------
+-- CanPutStr class --
+------------------
+
+{-| Effective type synonym for 'CanPutStrClass'. See 'CanGetContents' for more details. -}
+--type CanPutStr t = CanPutStrClass IO Handle t
+class CanPutStrClass IO Handle t => CanPutStr t
+instance CanPutStrClass IO Handle t => CanPutStr t
+
+class Monad m => CanPutStrClass m handleT t where
+ {-| Generalised 'System.IO.hPutStr' -}
+ hPutStr :: handleT -> t -> m ()
+ default hPutStr :: (CanProxyTo t, CanPutStrClass m handleT (CanProxyT t)) => handleT -> t -> m ()
+ hPutStr handle str = hPutStr handle (canProxyTo str)
+
+putStr :: CanPutStr t => t -> IO ()
+putStr = hPutStr IO.stdout
+
+writeFile :: CanPutStr t => FilePath -> t -> IO ()
+writeFile f txt = IO.withFile f IO.WriteMode (\hdl -> hPutStr hdl txt)
+
+appendFile :: CanPutStr t => FilePath -> t -> IO ()
+appendFile f txt = IO.withFile f IO.AppendMode (\hdl -> hPutStr hdl txt)
+
+interact :: (CanGetContents t, CanPutStr t) => (t -> t) -> IO ()
+interact f = getContents >>= (putStr . f)
+
+instance (m ~ IO, CanPutStrClass m Handle t) => CanPutStrClass m FilePath t where
+ hPutStr = appendFile
+
+-------------------------
+-- CanPutStrLine class --
+-------------------------
+
+{-| Effective type synonym for 'CanPutStrLnClass'. See 'CanGetContents' for more details. -}
+--type CanPutStrLn t = CanPutStrLnClass IO Handle t
+class CanPutStrLnClass IO Handle t => CanPutStrLn t
+instance CanPutStrLnClass IO Handle t => CanPutStrLn t
+
+class CanPutStrClass m handleT t => CanPutStrLnClass m handleT t where
+ {-| Generalised 'System.IO.hPutStrLn' -}
+ hPutStrLn :: handleT -> t -> m ()
+ default hPutStrLn :: (CanProxyTo t, CanPutStrLnClass m handleT (CanProxyT t)) => handleT -> t -> m ()
+ hPutStrLn handle str = hPutStrLn handle (canProxyTo str)
+
+putStrLn :: CanPutStrLn t => t -> IO ()
+putStrLn s = hPutStrLn IO.stdout s
+
+{-|
+If you have a data structure where in many cases the simplest way to read or write
+to it is just convert it to another you should define an instance of this class.
+
+For example, the simple way to write @ByteString@ 'BSB.Builder's is just to
+convert them to and from lazy 'ByteString's.
+
+Defining classese 'CanProxyTo' and 'CanProxyFrom' will define default methods
+for many of the other classes in this library.
+These can still be overriden if desired but it will save you a lot
+of boilerplate if you just which to convert your structure through some other.
+-}
+type family CanProxyT t
+
+class CanProxyTo t where
+ {-| How to convert to the type you will attempt to store -}
+ canProxyTo :: t -> CanProxyT t
+
+class CanProxyFrom t where
+ {-| How to convert from the type you will attempt to store -}
+ canProxyFrom :: CanProxyT t -> t
+
+----------------------
+-- String Instances --
+----------------------
+
+instance (m ~ IO) => CanGetContentsClass m Handle String where
+ hGetContents = IO.hGetContents
+instance (m ~ IO) => CanGetLineClass m Handle String where
+ hGetLine = IO.hGetLine
+instance (m ~ IO) => CanPutStrLnClass m Handle String where
+ hPutStrLn = IO.hPutStrLn
+instance (m ~ IO) => CanPutStrClass m Handle String where
+ hPutStr = IO.hPutStr
+
+---------------------------------
+-- Strict Bytestring Instances --
+---------------------------------
+
+instance (m ~ IO) => CanGetContentsClass m Handle BSS.ByteString where
+ hGetContents = BSS.hGetContents
+instance (m ~ IO) => CanGetLineClass m Handle BSS.ByteString where
+ hGetLine = BSS.hGetLine
+
+instance (m ~ IO) => CanPutStrClass m Handle BSS.ByteString where
+ hPutStr = BSS.hPutStr
+instance (m ~ IO) => CanPutStrLnClass m Handle BSS.ByteString where
+ hPutStrLn = BSS.hPutStrLn
+
+-------------------------------
+-- Lazy Bytestring Instances --
+-------------------------------
+
+instance (m ~ IO) => CanGetContentsClass m Handle BSL.ByteString where
+ hGetContents = BSL.hGetContents
+instance (m ~ IO) => CanGetLineClass m Handle BSL.ByteString where
+ hGetLine = (BSL.fromStrict <$>) . BSS.hGetLine
+
+instance (m ~ IO) => CanPutStrClass m Handle BSL.ByteString where
+ hPutStr = BSL.hPutStr
+instance (m ~ IO) => CanPutStrLnClass m Handle BSL.ByteString where
+ hPutStrLn = BSL.hPutStrLn
+
+----------------------------------
+-- Bytestring Builder Instances --
+----------------------------------
+
+instance (Monad m, CanGetContentsClass m Handle BSL.ByteString) => CanGetContentsClass m Handle BSB.Builder where
+instance CanGetLineClass m Handle BSL.ByteString => CanGetLineClass m Handle BSB.Builder
+
+instance (m ~ IO) => CanPutStrClass m Handle BSB.Builder where
+ hPutStr = BSB.hPutBuilder
+instance CanPutStrClass m Handle BSB.Builder => CanPutStrLnClass m Handle BSB.Builder where
+ hPutStrLn handle builder = hPutStr handle (builder <> BSB.char8 '\n')
+
+type instance CanProxyT BSB.Builder = BSL.ByteString
+
+instance CanProxyTo BSB.Builder where
+ canProxyTo = BSB.toLazyByteString
+
+instance CanProxyFrom BSB.Builder where
+ canProxyFrom = BSB.lazyByteString
+
+---------------------------
+-- Strict Text Instances --
+---------------------------
+
+instance (m ~ IO) => CanGetContentsClass m Handle TS.Text where
+ hGetContents = TS.hGetContents
+
+instance (m ~ IO) => CanGetLineClass m Handle TS.Text where
+ hGetLine = TS.hGetLine
+
+instance (m ~ IO) => CanPutStrClass m Handle TS.Text where
+ hPutStr = TS.hPutStr
+
+instance (m ~ IO) => CanPutStrLnClass m Handle TS.Text where
+ hPutStrLn = TS.hPutStrLn
+
+-------------------------
+-- Lazy Text Instances --
+-------------------------
+
+instance (m ~ IO) => CanGetContentsClass m Handle TL.Text where
+ hGetContents = TL.hGetContents
+
+instance (m ~ IO) => CanGetLineClass m Handle TL.Text where
+ hGetLine = TL.hGetLine
+
+instance (m ~ IO) => CanPutStrClass m Handle TL.Text where
+ hPutStr = TL.hPutStr
+
+instance (m ~ IO) => CanPutStrLnClass m Handle TL.Text where
+ hPutStrLn = TL.hPutStrLn
+
+----------------------------
+-- Text Builder Instances --
+----------------------------
+
+instance (Monad m, CanGetContentsClass m Handle TL.Text) => CanGetContentsClass m Handle TB.Builder
+instance CanGetLineClass m Handle TL.Text => CanGetLineClass m Handle TB.Builder
+
+instance (Monad m, CanPutStrClass m Handle TL.Text) => CanPutStrClass m Handle TB.Builder
+instance CanPutStrLnClass m Handle TL.Text => CanPutStrLnClass m Handle TB.Builder
+
+type instance CanProxyT TB.Builder = TL.Text
+
+instance CanProxyTo TB.Builder where
+ canProxyTo = TB.toLazyText
+
+instance CanProxyFrom TB.Builder where
+ canProxyFrom = TB.fromLazyText
diff --git a/src/System/IO/StringLike/PutStr.hs b/src/System/IO/StringLike/PutStr.hs
new file mode 100644
index 0000000..8c80084
--- /dev/null
+++ b/src/System/IO/StringLike/PutStr.hs
@@ -0,0 +1,7 @@
+module System.IO.StringLike.PutStr (
+ CanPutStr, CanPutStrClass (hPutStr), putStr, writeFile, appendFile, interact
+ ) where
+
+import Prelude ()
+
+import System.IO.StringLike.Impl
diff --git a/src/System/IO/StringLike/PutStrLn.hs b/src/System/IO/StringLike/PutStrLn.hs
new file mode 100644
index 0000000..a742401
--- /dev/null
+++ b/src/System/IO/StringLike/PutStrLn.hs
@@ -0,0 +1,9 @@
+module System.IO.StringLike.PutStrLn (
+ module System.IO.StringLike.PutStr,
+ CanPutStrLn, CanPutStrLnClass (hPutStrLn), putStrLn
+ ) where
+
+import Prelude ()
+
+import System.IO.StringLike.PutStr
+import System.IO.StringLike.Impl