category-printf (empty) → 0.1.0.0
raw patch · 9 files changed
+339/−0 lines, 9 filesdep +basedep +bytestringdep +comonadsetup-changed
Dependencies added: base, bytestring, comonad, text
Files
- LICENSE +30/−0
- Setup.hs +2/−0
- category-printf.cabal +34/−0
- src/Control/Category/Printf.hs +142/−0
- src/Control/Category/Printf/ByteString/Lazy.hs +28/−0
- src/Control/Category/Printf/ByteString/Strict.hs +28/−0
- src/Control/Category/Printf/String.hs +23/−0
- src/Control/Category/Printf/Text/Lazy.hs +26/−0
- src/Control/Category/Printf/Text/Strict.hs +26/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2016, Cale Gibbard++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 Cale Gibbard 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.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ category-printf.cabal view
@@ -0,0 +1,34 @@+name: category-printf+version: 0.1.0.0+synopsis: Highbrow approach to type-safe printf format specifications.+description: We use the co-Kleisli category for the comonad of+ functions out of a fixed monoid to implement a generic+ combinator library for type-safe format specifications.+ Works with pretty much anything that's a monoid, with+ specific support for String, Text (strict/lazy), and+ ByteString (strict/lazy). Credit to Daniel Patterson+ for introducing me to something that looked just enough+ like it ought to be the composition for a category, and+ Ryan Trinkle for the IsString instance. :)+license: BSD3+license-file: LICENSE+author: Cale Gibbard+maintainer: cgibbard@gmail.com+category: Text+build-type: Simple++-- Constraint on the version of Cabal needed to build this package.+cabal-version: >=1.10++library+ exposed-modules: Control.Category.Printf,+ Control.Category.Printf.String,+ Control.Category.Printf.Text.Strict,+ Control.Category.Printf.Text.Lazy,+ Control.Category.Printf.ByteString.Strict,+ Control.Category.Printf.ByteString.Lazy+ + other-extensions: OverloadedStrings, FlexibleInstances, TypeFamilies+ build-depends: base >=4.7 && <4.8, comonad >=5 && <5.1, text >=1.1 && <1.2, bytestring >=0.10 && <0.11+ hs-source-dirs: src+ default-language: Haskell2010
+ src/Control/Category/Printf.hs view
@@ -0,0 +1,142 @@+{-# LANGUAGE OverloadedStrings, FlexibleInstances, TypeFamilies #-}++module Control.Category.Printf + ( module Control.Category+ , Format+ , printfWith+ , sprintf+ , c+ , i+ , spliceWith+ , s+ , generalizeString+ , intAtBase+ , hex+ , oct+ , eFloat+ , fFloat+ , gFloat+ , push+ , dup+ , swap+ , skip+ , apply+ , apply2+ ) where++import Prelude hiding (id, (.))+import Control.Arrow+import Control.Category+import Data.Monoid+import Control.Comonad+import Numeric++import Data.String++-- | Handy type synonym for the things we're working with.+type Format m = Cokleisli ((->) m)++-- | You should regard a value of type @Format m a b@ as something which explains how to write+-- some element of the monoid @m@ (a "string" for our purposes), and which will change the type+-- of printf from @a@ to @b@. For instance, something which adds a responsibility to provide an+-- additional argument of type @t@ might have type @Format m a (t -> a)@, while a formatter which+-- somehow absolves you of that responsibility would have type @Format m (t -> a) a@.++-- | We can apply this to something like putStrLn to get a function for formatted printing.+-- Typically you'll have @r = IO ()@, but that needn't be the case.+printfWith :: (m -> r) -> Format m r b -> b+printfWith printer f = runCokleisli f printer++-- | If you just want to build a string / element of your monoid, we have @sprintf = printfWith id@+sprintf :: Format m m b -> b+sprintf = printfWith id++instance (a ~ b, IsString s, Monoid s) => IsString (Cokleisli ((->) s) a b) where+ fromString = c . fromString++-- | Formatter for a constant string.+c :: (Monoid m) => m -> Format m a a+c x = Cokleisli (\k -> k x)++-- | Inclusion of a string directly.+i :: Format m a (m -> a)+i = Cokleisli id++-- | Given a way to turn a value of type t into a string, this builds a+-- formatter which demands an additional argument of type t and splices it in.+spliceWith :: (Monoid m) => (t -> m) -> Format m a (t -> a)+spliceWith f = Cokleisli (\k n -> k (f n))++-- | Splice in anything showable.+s :: (Monoid s, IsString s, Show t) => Format s a (t -> a)+s = spliceWith (fromString . show)++-- | Generalizes the string type that a formatter uses by applying fromString internally.+generalizeString :: (IsString s, Monoid s) => Format String a b -> Format s a b+generalizeString f = Cokleisli (\k -> runCokleisli f (k . fromString))++-- | Show an integral value using the given base, and using the provided function to determine how+-- to display individual digits.+intAtBase :: (Real t, Integral t, Show t, Monoid s, IsString s)+ => t -> (Int -> Char) -> Format s a (t -> a)+intAtBase b showDigit = generalizeString $+ spliceWith (\n -> showSigned (showIntAtBase b showDigit) 0 n "")++-- | Show an integral value in hexadecimal.+hex :: (Integral t, Show t, Monoid s, IsString s) => Format s a (t -> a)+hex = generalizeString $+ spliceWith (\n -> showSigned showHex 0 n "")++-- | Show an integral value in octal.+oct :: (Integral t, Show t, Monoid s, IsString s) => Format s a (t -> a)+oct = generalizeString $+ spliceWith (\n -> showSigned showOct 0 n "")++-- | Show a floating point value in exponential format. (e.g. 2.45e2, -1.5e-3)+-- If `digs` is Nothing, the value is shown to full precision, if it is Just d then at most+-- d digits after the decimal point are shown.+eFloat :: (RealFloat t, Monoid s, IsString s) => Maybe Int -> Format s a (t -> a)+eFloat digs = generalizeString $ spliceWith (\n -> showEFloat digs n "")++-- | Show a floating point value in standard decimal format. (e.g. 245000, -0.0015)+-- If `digs` is Nothing, the value is shown to full precision, if it is Just d then at most+-- d digits after the decimal point are shown.+fFloat :: (RealFloat t, Monoid s, IsString s) => Maybe Int -> Format s a (t -> a)+fFloat digs = generalizeString $ spliceWith (\n -> showFFloat digs n "")++-- | Show a floating point value using standard decimal notation for arguments whose absolute+-- value lies between 0.1 and 9,999,999, and scientific notation otherwise. +-- If `digs` is Nothing, the value is shown to full precision, if it is Just d then at most+-- d digits after the decimal point are shown.+gFloat :: (RealFloat t, Monoid s, IsString s) => Maybe Int -> Format s a (t -> a)+gFloat digs = generalizeString $ spliceWith (\n -> showGFloat digs n "")++-- | We can use `arr` from the Arrow instance for Cokleisli w to produce formatters+-- that manipulate the stack without printing. That is, we have+-- +-- > arr :: (Monoid m) => (s -> s') -> Format m s s'++-- | Push an argument onto the stack to be consumed by subsequent formatters.+push :: Monoid m => t -> Format m (t -> a) a+push x = arr (\k -> k x)++-- | Duplicate an argument on the stack, making it available twice.+dup :: Monoid m => Format m (t -> t -> a) (t -> a)+dup = arr (\k x -> k x x)++-- | Swap the next two arguments on the stack.+swap :: Monoid m => Format m (t -> t' -> a) (t' -> t -> a)+swap = arr (\k x y -> k y x)++-- | Skip the next argument on the stack.+skip :: Monoid m => Format m (t -> a) (t' -> t -> a)+skip = arr (\k x -> k)++-- | Apply a function to the argument on the top of the stack.+apply :: Monoid m => (u -> v) -> Format m (v -> a) (u -> a)+apply f = arr (\k x -> k (f x))++-- | Apply a binary function to the top two arguments on the stack.+apply2 :: Monoid m => (u -> v -> w) -> Format m (w -> a) (u -> v -> a)+apply2 f = arr (\k x y -> k (f x y))+
+ src/Control/Category/Printf/ByteString/Lazy.hs view
@@ -0,0 +1,28 @@+module Control.Category.Printf.ByteString.Lazy+ ( module Control.Category.Printf+ , printf+ , printfLn+ , hPrintf+ , hPrintfLn+ ) where++import Prelude hiding (id, (.))+import Control.Comonad+import Control.Category.Printf+import Data.ByteString.Lazy (ByteString)+import qualified Data.ByteString.Lazy as B+import qualified Data.ByteString.Lazy.Char8 as BC+import System.IO (Handle)++printf :: Format ByteString (IO ()) b -> b+printf = printfWith B.putStr++printfLn :: Format ByteString (IO ()) b -> b+printfLn = printfWith BC.putStrLn ++hPrintf :: Handle -> Format ByteString (IO ()) b -> b+hPrintf h = printfWith (B.hPutStr h)++hPrintfLn :: Handle -> Format ByteString (IO ()) b -> b+hPrintfLn h = printfWith (BC.hPutStrLn h)+
+ src/Control/Category/Printf/ByteString/Strict.hs view
@@ -0,0 +1,28 @@+module Control.Category.Printf.ByteString.Strict+ ( module Control.Category.Printf+ , printf+ , printfLn+ , hPrintf+ , hPrintfLn+ ) where++import Prelude hiding (id, (.))+import Control.Comonad+import Control.Category.Printf+import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as BC+import System.IO (Handle)++printf :: Format ByteString (IO ()) b -> b+printf = printfWith B.putStr++printfLn :: Format ByteString (IO ()) b -> b+printfLn = printfWith BC.putStrLn ++hPrintf :: Handle -> Format ByteString (IO ()) b -> b+hPrintf h = printfWith (B.hPutStr h)++hPrintfLn :: Handle -> Format ByteString (IO ()) b -> b+hPrintfLn h = printfWith (BC.hPutStrLn h)+
+ src/Control/Category/Printf/String.hs view
@@ -0,0 +1,23 @@+module Control.Category.Printf.String + (module Control.Category.Printf+ , printf+ , printfLn+ , hPrintf+ , hPrintfLn+ ) where++import Control.Comonad+import Control.Category.Printf+import System.IO (Handle, hPutStr, hPutStrLn)++printf :: Format String (IO ()) b -> b+printf = printfWith putStr++printfLn :: Format String (IO ()) b -> b+printfLn = printfWith putStrLn++hPrintf :: Handle -> Format String (IO ()) b -> b+hPrintf h = printfWith (hPutStr h)++hPrintfLn :: Handle -> Format String (IO ()) b -> b+hPrintfLn h = printfWith (hPutStrLn h)
+ src/Control/Category/Printf/Text/Lazy.hs view
@@ -0,0 +1,26 @@+module Control.Category.Printf.Text.Lazy+ ( module Control.Category.Printf+ , printf+ , printfLn+ , hPrintf+ , hPrintfLn+ ) where++import Prelude hiding (id, (.))+import Control.Comonad+import Control.Category.Printf+import Data.Text.Lazy (Text)+import qualified Data.Text.Lazy.IO as T+import System.IO (Handle)++printf :: Format Text (IO ()) b -> b+printf = printfWith T.putStr++printfLn :: Format Text (IO ()) b -> b+printfLn = printfWith T.putStrLn ++hPrintf :: Handle -> Format Text (IO ()) b -> b+hPrintf h = printfWith (T.hPutStr h)++hPrintfLn :: Handle -> Format Text (IO ()) b -> b+hPrintfLn h = printfWith (T.hPutStrLn h)
+ src/Control/Category/Printf/Text/Strict.hs view
@@ -0,0 +1,26 @@+module Control.Category.Printf.Text.Strict+ ( module Control.Category.Printf+ , printf+ , printfLn+ , hPrintf+ , hPrintfLn+ ) where++import Prelude hiding (id, (.))+import Control.Comonad+import Control.Category.Printf+import Data.Text (Text)+import qualified Data.Text.IO as T+import System.IO (Handle)++printf :: Format Text (IO ()) b -> b+printf = printfWith T.putStr++printfLn :: Format Text (IO ()) b -> b+printfLn = printfWith T.putStrLn ++hPrintf :: Handle -> Format Text (IO ()) b -> b+hPrintf h = printfWith (T.hPutStr h)++hPrintfLn :: Handle -> Format Text (IO ()) b -> b+hPrintfLn h = printfWith (T.hPutStrLn h)