{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE DeriveDataTypeable #-} module Data.Text1( Text1(Text1) , length1 , compareLength1 , _head1 , _tail1 , _last1 , _init1 , AsText1(_Text1) , IsText1(packed1, tpacked1, unpacked1, tunpacked1, text1) , isText1 , AsSingle(_Single) , OneAnd(_OneAnd) ) where import Control.Applicative(Applicative) import Control.Category(Category(id, (.))) import Control.Lens(Iso, IndexedTraversal', Optic', Profunctor, Choice, Reversing(reversing), Cons(_Cons), Snoc(_Snoc), uncons, unsnoc, Iso', Lens', Prism', prism', iso, lens, (^.), (#), (^?), (%~), _1, _2, from, indexing, traversed) import Control.Monad(Monad(return, (>>=), (>>))) import Data.Binary(Binary(put, get)) import Data.Char(Char) import Data.Data(Data) import Data.Eq(Eq) import Data.Foldable(toList) import Data.Functor(Functor(fmap)) import Data.Int(Int) import Data.List as List(null) import Data.List.NonEmpty(NonEmpty((:|))) import Data.Maybe(Maybe(Just, Nothing)) import Data.Ord(Ord, Ordering) import Data.Semigroup(Semigroup((<>))) import Data.String(String) import Data.Text(Text) import qualified Data.Text as Text(cons, snoc, append, null, empty, length, compareLength, uncons, pack, unpack, singleton) import Data.Text.Lens(IsText(packed, builder)) import Data.Traversable(Traversable(traverse)) import Data.Tuple(uncurry) import Data.Typeable(Typeable) import Prelude(Show(show), Num((+), (-))) data Text1 = Text1 Char Text deriving (Eq, Ord, Data, Typeable) instance Show Text1 where show (Text1 h t) = show (Text.cons h t) instance Semigroup Text1 where Text1 h1 t1 <> t = Text1 h1 (Text.append t1 (_Text1 # t)) instance Binary Text1 where put (Text1 h t) = put h >> put t get = do h <- get t <- get return (Text1 h t) length1 :: Text1 -> Int length1 (Text1 _ t) = 1 + Text.length t compareLength1 :: Text1 -> Int -> Ordering compareLength1 (Text1 _ t) n = Text.compareLength t (n - 1) _head1 :: Lens' Text1 Char _head1 = lens (\(Text1 h _) -> h) (\(Text1 _ t) h -> Text1 h t) _tail1 :: Lens' Text1 Text _tail1 = lens (\(Text1 _ t) -> t) (\(Text1 h _) t -> Text1 h t) _last1 :: Lens' Text1 Char _last1 = lens (\(Text1 h t) -> case unsnoc t of Nothing -> h Just (_, l) -> l) (\(Text1 h t) x -> case unsnoc t of Nothing -> Text1 x t Just (i, _) -> Text1 h (Text.snoc i x)) _init1 :: Lens' Text1 Text _init1 = lens (\(Text1 h t) -> case unsnoc t of Nothing -> Text.empty Just (i, _) -> Text.cons h i) (\(Text1 h t) x -> let r = case unsnoc t of Nothing -> h Just (_, l) -> l in case uncons x of Nothing -> Text1 r Text.empty Just (h', t') -> Text1 h' (Text.snoc t' r)) class AsText1 p f s where _Text1 :: Optic' p f s Text1 instance AsText1 p f Text1 where _Text1 = id instance (Profunctor p, Functor f) => AsText1 p f (NonEmpty Char) where _Text1 = packed1 instance (Choice p, Applicative f) => AsText1 p f String where _Text1 = prism' (\(Text1 h t) -> h : Text.unpack t) (fmap (\(h, t) -> Text1 h (Text.pack t)) . uncons) instance (Choice p, Applicative f) => AsText1 p f Text where _Text1 = prism' (\(Text1 h t) -> Text.cons h t) (fmap (uncurry Text1) . Text.uncons) class IsText1 t where packed1 :: Iso' (NonEmpty Char) t tpacked1 :: Iso' Text (Maybe t) tpacked1 = iso (fmap (\(h, t') -> (h :| Text.unpack t') ^. packed1) . Text.uncons) (\t -> case t of Nothing -> Text.empty Just t' -> Text.pack (toList (packed1 # t'))) unpacked1 :: Iso' t (NonEmpty Char) unpacked1 = from packed1 tunpacked1 :: Iso' (Maybe t) Text tunpacked1 = from tpacked1 text1 :: IndexedTraversal' Int t Char text1 = unpacked1 . traversed instance IsText1 Text1 where packed1 = iso (\(h :| t) -> Text1 h (t ^. packed)) (\(Text1 h t) -> h :| (packed # t)) tpacked1 = iso (fmap (\(h, t') -> Text1 h t') . Text.uncons) (\t -> case t of Nothing -> Text.empty Just (Text1 h t') -> Text.cons h t') instance IsText1 (NonEmpty Char) where packed1 = id text1 = indexing traverse instance IsText (Maybe Text1) where packed = packed . isText1 builder = from isText1 . builder instance Reversing Text1 where reversing (Text1 h t) = case uncons (reversing t) of Nothing -> Text1 h Text.empty Just (h', t') -> Text1 h' (Text.snoc t' h) isText1 :: Iso' Text (Maybe Text1) isText1 = iso (\x -> fmap (\(h, t) -> Text1 h t) (Text.uncons x)) (\x -> case x of Nothing -> Text.empty Just (Text1 h t) -> Text.cons h t) instance Cons (Maybe Text1) (Maybe Text1) Char Char where _Cons = prism' (\(h, t) -> (_Cons # (h, isText1 # t)) ^. isText1) (\t -> fmap (_2 %~ (^. isText1)) ((isText1 # t) ^? _Cons)) instance Snoc (Maybe Text1) (Maybe Text1) Char Char where _Snoc = prism' (\(t, s) -> (_Snoc # (isText1 # t, s)) ^. isText1) (\t -> fmap (_1 %~ (^. isText1)) ((isText1 # t) ^? _Snoc)) ---- -- The following should be in a lens-based package ---- class AsSingle c a | c -> a where _Single :: Prism' c a instance AsSingle [a] a where _Single = prism' (\a -> [a]) (\c -> case c of [a] -> Just a _ -> Nothing) instance AsSingle Text Char where _Single = prism' Text.singleton (\t -> uncons t >>= \(h, t') -> if Text.null t' then Just h else Nothing) instance AsSingle (Maybe a) a where _Single = prism' Just id instance AsSingle (NonEmpty a) a where _Single = prism' (\a -> a :| []) (\(h :| t) -> if List.null t then Just h else Nothing) instance AsSingle Text1 Char where _Single = prism' (\c -> Text1 c Text.empty) (\(Text1 h t) -> if Text.null t then Just h else Nothing) class OneAnd s t a b x y | s -> a, s -> x, t -> b, t -> y, s b -> t, x b -> t, t a -> s, y a -> s where _OneAnd :: Iso s t (a, x) (b, y) instance OneAnd Text1 Text1 Char Char Text Text where _OneAnd = iso (\(Text1 h t) -> (h, t)) (uncurry Text1) instance OneAnd (NonEmpty a) (NonEmpty b) a b [a] [b] where _OneAnd = iso (\(h :| t) -> (h, t)) (uncurry (:|))