packages feed

sext (empty) → 0.1.0.0

raw patch · 6 files changed

+484/−0 lines, 6 filesdep +basedep +bytestringdep +template-haskellsetup-changed

Dependencies added: base, bytestring, template-haskell, text

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2014, Dmitry Dzhus++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 Dmitry Dzhus 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
+ sext.cabal view
@@ -0,0 +1,57 @@+name:                sext+version:             0.1.0.0+synopsis:            Lists, Texts and ByteStrings with type-encoded length+homepage:            http://github.com/dzhus/sext/++description:         Sext (/s/tatic t/ext/) provides type-level safety for+                     basic operations on string-like types (finite+                     lists of elements). Use it when you need static+                     guarantee on lengths of strings produced in your+                     code.++license:             BSD3+license-file:        LICENSE+author:              Dmitry Dzhus+maintainer:          dima@dzhus.org+category:            Data, Text, Type System+stability:           Experimental++build-type:          Simple+cabal-version:       >=1.10+tested-with:         GHC == 7.8.3++source-repository head+  type:     git+  location: http://github.com/dzhus/sext++flag text+  description: Build interface for Text+  default: True++flag bytestring+  description: Build interface for ByteString+  default: True++library+  exposed-modules:+    Data.Sext,+    Data.Sext.Class,+    Data.Sext.TH++  build-depends:+    base             >=4.7 && <4.8,+    template-haskell++  if flag(bytestring)+    cpp-options: -DWITH_BS+    build-depends:+      bytestring     >=0.10 && <0.11++  if flag(text)+    cpp-options: -DWITH_TEXT+    build-depends:+      text           >=1.1 && <1.2++  ghc-options: -Wall+  hs-source-dirs:      src+  default-language:    Haskell2010
+ src/Data/Sext.hs view
@@ -0,0 +1,219 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}++{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}++{-|++Sext (/s/tatic t/ext/) provides type-level safety for basic operations+on string-like types (finite lists of elements). Use it when you need+static guarantee on lengths of strings produced in your code.++An example application would be a network exchange protocol built of+packets with fixed-width fields:++> mkPacket :: String -> Sext 32 String+> mkPacket inp =+>   -- 5-character version signature+>   $(sext "PKT10") `append`+>   -- 25-character payload+>   payload `append`+>   -- 2-character payload checksum+>   checksum+>   where+>     payload = createLeft ' ' inp+>     checksum :: Sext 2 String+>     checksum = createLeft ' ' $+>                show $ length payload `mod` 100+>+> message :: Sext 64 String+> message = mkPacket "Hello" `append` mkPacket "world"++Sext combinators are defined for members of 'Sextable' class. The+package includes 'Sextable' instances for several common types.++This module is meant to be imported qualifed, e.g.++> import qualified Data.Sext as S++-}++module Data.Sext+       (+         -- * Constructing Sexts+         --+         -- | See also 'C.unsafeCreate'+         createLeft+       , createRight+       , sext+       , create+       , replicate++         -- * Working with Sexts+       , append+       , take+       , drop+       , map+       , padLeft+       , padRight++       , length++         -- * Sextable class+       , Sextable(C.unsafeCreate, C.unwrap)+       )++where++import           Prelude as P hiding (drop, length, map, replicate, take)+import qualified Prelude as P hiding (length)++import           GHC.TypeLits++import           Data.Proxy+import           Data.Sext.Class (Elem, Sext, Sextable)+import qualified Data.Sext.Class as C+import           Data.Sext.TH+++-- | Safely create a Sext, possibly altering the source to match+-- target length. If target length is less than that of the source,+-- the source gets truncated. If target length is greater, the source+-- is padded using the provided basic element. Elements on the left+-- are preferred.+--+-- >>> createLeft ' ' "foobarbaz" :: Sext 6 String+-- "foobar"+-- >>> createLeft '#' "foobarbaz" :: Sext 12 String+-- "foobarbaz###"+createLeft :: forall a i.+              (Sextable a, KnownNat i) =>+              Elem a -> a -> Sext i a+createLeft e s =+  C.unsafeCreate $+  C.take (C.length s) $+  C.append s $+  C.replicate (t - C.length s) e+  where+    t = fromIntegral $ natVal (Proxy :: Proxy i)+++-- | Just like 'createLeft', except that elements on the right are preferred.+--+-- >>> createRight '@' "foobarbaz" :: Sext 6 String+-- "barbaz"+-- >>> createRight '!' "foobarbaz" :: Sext 12 String+-- "!!!foobarbaz"+createRight :: forall a i.+               (Sextable a, KnownNat i) =>+               Elem a -> a -> Sext i a+createRight e s =+  C.unsafeCreate $+  C.drop (C.length s - t) $+  C.append (C.replicate (t - C.length s) e) s+  where+    t = fromIntegral $ natVal (Proxy :: Proxy i)+++-- | Attempt to safely create a Sext if it matches target length.+--+-- >>> create "foobar" :: Maybe (Sext 6 String)+-- Just "foobar"+-- >>> create "barbaz" :: Maybe (Sext 8 String)+-- Nothing+--+-- This is safer than 'C.unsafeCreate' and unlike with 'createLeft' /+-- 'createRight' the source value is left unchanged. However, this+-- implies a further run-time check for Nothing values.+create :: forall a i.+          (Sextable a, KnownNat i) =>+          a -> P.Maybe (Sext i a)+create s =+  if C.length s == t+  then Just $ C.unsafeCreate s+  else Nothing+  where+    t = fromIntegral $ natVal (Proxy :: Proxy i)+++-- | Append two Sexts together.+--+-- >>> append "foo" "bar" :: Sext 6 String+-- "foobar"+append :: forall a m n.+          (Sextable a) => Sext m a -> Sext n a -> Sext (m + n) a+append a b = C.unsafeCreate $ C.append (C.unwrap a) (C.unwrap b)+++-- | Construct a new Sext from a basic element.+--+-- >>> replicate '=' :: Sext 10 String+-- "=========="+replicate :: forall a i.+             (Sextable a, KnownNat i) => Elem a -> Sext i a+replicate e =+  C.unsafeCreate $ C.replicate t e+  where+    t = fromIntegral $ natVal (Proxy :: Proxy i)+++map :: Sextable a =>+       (Elem a -> Elem a) -> Sext m a -> Sext m a+map f s =+  C.unsafeCreate $ C.map f $ C.unwrap s+++-- | Reduce Sext length, preferring elements on the left.+--+-- >>> take "Foobar" :: Sext 3 String+-- "Foo"+take :: forall a m n.+        (Sextable a, KnownNat m, KnownNat n, n <= m) =>+        Sext m a -> Sext n a+take s =+  C.unsafeCreate $ C.take t $ C.unwrap s+  where+    t = fromIntegral $ natVal (Proxy :: Proxy n)+++-- | Reduce Sext length, preferring elements on the right.+--+-- >>> drop "Foobar" :: Sext 2 String+-- "ar"+drop :: forall a m n.+        (Sextable a, KnownNat m, KnownNat n, n <= m) =>+        Sext m a -> Sext n a+drop s =+  C.unsafeCreate $ C.drop (C.length s' - t) s'+  where+    s' = C.unwrap s+    t = fromIntegral $ natVal (Proxy :: Proxy n)+++-- | Obtain value-level length.+length :: forall a m.+          KnownNat m => Sext m a -> P.Int+length _ = P.fromIntegral P.$ natVal (Proxy :: Proxy m)+++-- | Fill a Sext with extra elements up to target length, padding+-- original elements to the left.+padLeft :: forall a m n.+           (Sextable a, KnownNat m, KnownNat (n - m),+            n ~ (n - m + m), m <= n) =>+           Elem a -> Sext m a -> Sext n a+padLeft pad = append (replicate pad)+++-- | Like 'padLeft', but original elements are padded to the right.+padRight :: forall a m n.+           (Sextable a, KnownNat m, KnownNat (n - m),+            n ~ (m + (n - m)), m <= n) =>+           Elem a -> Sext m a -> Sext n a+padRight pad = P.flip append (replicate pad)
+ src/Data/Sext/Class.hs view
@@ -0,0 +1,122 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeFamilies #-}++{-|++Use this only if you need to make some type Sextable.++-}++module Data.Sext.Class+       ( Sextable(..)+       )++where++import           Prelude+import qualified Prelude as P++#ifdef WITH_BS+import qualified Data.ByteString as B+import           GHC.Word+#endif++#ifdef WITH_TEXT+import qualified Data.Text as T+#endif++import           GHC.TypeLits+++-- | Class of types which can be assigned a type-level length.+class Sextable a where+  -- | Data family which wraps values of the underlying type giving+  -- them a type-level length. @Sext 6 t@ means a value of type @t@ of+  -- length 6.+  data Sext (i :: Nat) a++  -- | Basic element type. For @Sextable [a]@, this is @a@.+  type Elem a++  -- | Simply wrap a value in a Sext as is, assuming any length.+  --+  -- For example, an expression like+  --+  -- > unsafeCreate "somestring" :: Sext 50 String+  --+  -- will typecheck, although the stored length information will not+  -- match actual string size. This may result in wrong behaviour of+  -- all functions defined for Sext.+  --+  -- Use it only when you know what you're doing.+  --+  -- When implementing new Sextable instances, code this to simply+  -- apply the constructor of 'Sext'.+  unsafeCreate :: a -> Sext i a++  -- | Forget type-level length, obtaining the underlying value.+  unwrap :: Sext i a -> a++  length :: a -> Int+  append :: a -> a -> a+  replicate :: Int -> Elem a -> a+  map :: (Elem a -> Elem a) -> a -> a+  take :: Int -> a -> a+  drop :: Int -> a -> a+++instance (Show a, Sextable a) => Show (Sext i a) where+  show s = show $ unwrap s+++instance Sextable [a] where+  type Elem [a] = a++  data Sext i [a] = List [a]++  unsafeCreate = List+  unwrap (List l) = l++  length = P.length+  append = (P.++)+  replicate = P.replicate+  map = P.map+  take = P.take+  drop = P.drop+++#ifdef WITH_TEXT+instance Sextable T.Text where+  type Elem T.Text = Char++  data Sext i T.Text = Text T.Text++  unsafeCreate = Text+  unwrap (Text t) = t++  length = T.length+  append = T.append+  replicate = \n c -> T.replicate n (T.singleton c)+  map = T.map+  take = T.take+  drop = T.drop+#endif+++#ifdef WITH_BS+instance Sextable B.ByteString where+  type Elem B.ByteString = Word8++  data Sext i B.ByteString = ByteString B.ByteString++  unsafeCreate = ByteString+  unwrap (ByteString t) = t++  length = B.length+  append = B.append+  replicate = B.replicate+  map = B.map+  take = B.take+  drop = B.drop+#endif
+ src/Data/Sext/TH.hs view
@@ -0,0 +1,54 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE TemplateHaskell #-}++{-|++Template Haskell helpers for Sext.++-}++module Data.Sext.TH+       ( sext+       )++where++import           Prelude+import qualified Prelude as P (length)++import           Data.Sext.Class+import           Data.String++import           Language.Haskell.TH+++-- | A type with IsString instance to allow string literals in 'sext'+-- argument without quoting.+newtype LitS = LitS String deriving IsString+++-- | Type-safe Sext constructor macro for string literals.+--+-- Example:+--+-- > $(sext "Foobar")+--+-- compiles to+--+-- > unsafeCreate "Foobar" :: forall a. (IsString a, Sextable a) => Sext 6 a+--+-- where 6 is the string length obtained at compile time.+sext :: LitS -> Q Exp+sext (LitS s) =+  do+    at <- newName "a"+    return $ SigE (AppE (VarE 'unsafeCreate) (LitE $ StringL s))+                (ForallT+                 [PlainTV at]+                 [ ClassP ''IsString [VarT at]+                 , ClassP ''Sextable [VarT at]] $+                 (AppT+                  (AppT+                   (ConT ''Sext)+                   (LitT $ NumTyLit (fromIntegral $ P.length s)))+                  (VarT at)))