feat(users/Profpatsch/my-prelude): add builder & divisible helpers
I’ve had enough of `show & stringToText` bullshit, let’s finally create a trivial builder contravariant module that wraps `Text.Builder` and `ByteString.Builder` and has a naming scheme that does not collide with anything and that I can remember in my sleep. Plus some experiments in making `Divisible` useful with `HasField`. Change-Id: Ie25f78226d24eadf4c35353fe9db40b7683d82c3 Reviewed-on: https://cl.tvl.fyi/c/depot/+/12955 Tested-by: BuildkiteCI Reviewed-by: Profpatsch <mail@profpatsch.de>
This commit is contained in:
		
							parent
							
								
									a14a7e6ec9
								
							
						
					
					
						commit
						444b67b010
					
				
					 5 changed files with 146 additions and 0 deletions
				
			
		|  | @ -7,8 +7,10 @@ pkgs.haskellPackages.mkDerivation { | |||
|   src = depot.users.Profpatsch.exactSource ./. [ | ||||
|     ./my-prelude.cabal | ||||
|     ./src/Aeson.hs | ||||
|     ./src/Builder.hs | ||||
|     ./src/Comparison.hs | ||||
|     ./src/Debug.hs | ||||
|     ./src/Divisive.hs | ||||
|     ./src/Json.hs | ||||
|     ./src/Json/Enc.hs | ||||
|     ./src/Arg.hs | ||||
|  | @ -34,6 +36,7 @@ pkgs.haskellPackages.mkDerivation { | |||
|     pkgs.haskellPackages.pa-pretty | ||||
|     pkgs.haskellPackages.pa-field-parser | ||||
|     pkgs.haskellPackages.aeson-better-errors | ||||
|     pkgs.haskellPackages.contravariant | ||||
|     pkgs.haskellPackages.foldl | ||||
|     pkgs.haskellPackages.resource-pool | ||||
|     pkgs.haskellPackages.error | ||||
|  |  | |||
|  | @ -61,8 +61,10 @@ library | |||
|       Aeson | ||||
|       Arg | ||||
|       AtLeast | ||||
|       Builder | ||||
|       Comparison | ||||
|       Debug | ||||
|       Divisive | ||||
|       Json | ||||
|       Json.Enc | ||||
|       Test | ||||
|  | @ -92,6 +94,7 @@ library | |||
|      , base64-bytestring | ||||
|      , bytestring | ||||
|      , containers | ||||
|      , contravariant | ||||
|      , foldl | ||||
|      , unordered-containers | ||||
|      , resource-pool | ||||
|  |  | |||
							
								
								
									
										94
									
								
								users/Profpatsch/my-prelude/src/Builder.hs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								users/Profpatsch/my-prelude/src/Builder.hs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,94 @@ | |||
| module Builder | ||||
|   ( TextBuilder (..), | ||||
|     BytesBuilder (..), | ||||
|     buildText, | ||||
|     buildTextLazy, | ||||
|     buildBytes, | ||||
|     buildBytesLazy, | ||||
|     textT, | ||||
|     textLazyT, | ||||
|     bytesB, | ||||
|     bytesLazyB, | ||||
|     utf8B, | ||||
|     utf8LazyB, | ||||
|     intDecimalT, | ||||
|     intDecimalNaturalT, | ||||
|   ) | ||||
| where | ||||
| 
 | ||||
| import Data.ByteString.Builder qualified as Bytes | ||||
| import Data.ByteString.Lazy qualified as Bytes.Lazy | ||||
| import Data.Functor.Contravariant | ||||
| import Data.Functor.Contravariant.Divisible | ||||
| import Data.String | ||||
| import Data.Text.Lazy qualified as Text.Lazy | ||||
| import Data.Text.Lazy.Builder qualified as Text | ||||
| import Data.Text.Lazy.Builder.Int qualified as Text | ||||
| import MyPrelude | ||||
| 
 | ||||
| newtype TextBuilder a = TextBuilder {unTextBuilder :: a -> Text.Builder} | ||||
|   deriving newtype (Semigroup, Monoid) | ||||
| 
 | ||||
| instance IsString (TextBuilder a) where | ||||
|   fromString s = TextBuilder $ \_ -> s & fromString | ||||
| 
 | ||||
| instance Contravariant TextBuilder where | ||||
|   contramap f (TextBuilder g) = TextBuilder $ g . f | ||||
| 
 | ||||
| instance Divisible TextBuilder where | ||||
|   divide f (TextBuilder bb) (TextBuilder bc) = | ||||
|     TextBuilder $ \a -> let (b, c) = f a in bb b <> bc c | ||||
|   conquer = TextBuilder $ \_ -> mempty | ||||
| 
 | ||||
| -- | Convert a 'TextBuilder' to a strict 'Text' by applying it to a value. | ||||
| buildText :: TextBuilder a -> a -> Text | ||||
| buildText (TextBuilder f) a = f a & Text.toLazyText & toStrict | ||||
| 
 | ||||
| -- | Convert a 'TextBuilder' to a lazy 'Text' by applying it to a value. | ||||
| buildTextLazy :: TextBuilder a -> a -> Text.Lazy.Text | ||||
| buildTextLazy (TextBuilder f) a = f a & Text.toLazyText | ||||
| 
 | ||||
| newtype BytesBuilder a = BytesBuilder {unBytesBuilder :: a -> Bytes.Builder} | ||||
| 
 | ||||
| instance IsString (BytesBuilder a) where | ||||
|   fromString s = BytesBuilder $ \_ -> s & fromString | ||||
| 
 | ||||
| instance Contravariant BytesBuilder where | ||||
|   contramap f (BytesBuilder g) = BytesBuilder $ g . f | ||||
| 
 | ||||
| instance Divisible BytesBuilder where | ||||
|   divide f (BytesBuilder bb) (BytesBuilder bc) = | ||||
|     BytesBuilder $ \a -> let (b, c) = f a in bb b <> bc c | ||||
|   conquer = BytesBuilder $ \_ -> mempty | ||||
| 
 | ||||
| -- | Convert a 'BytesBuilder' to a strict 'ByteString' by applying it to a value. | ||||
| buildBytes :: BytesBuilder a -> a -> ByteString | ||||
| buildBytes (BytesBuilder b) a = b a & Bytes.toLazyByteString & toStrictBytes | ||||
| 
 | ||||
| -- | Convert a 'BytesBuilder' to a lazy 'ByteString' by applying it to a value. | ||||
| buildBytesLazy :: BytesBuilder a -> a -> Bytes.Lazy.ByteString | ||||
| buildBytesLazy (BytesBuilder b) a = b a & Bytes.toLazyByteString | ||||
| 
 | ||||
| textT :: TextBuilder Text | ||||
| textT = TextBuilder Text.fromText | ||||
| 
 | ||||
| textLazyT :: TextBuilder Text.Lazy.Text | ||||
| textLazyT = TextBuilder Text.fromLazyText | ||||
| 
 | ||||
| bytesB :: BytesBuilder ByteString | ||||
| bytesB = BytesBuilder Bytes.byteString | ||||
| 
 | ||||
| bytesLazyB :: BytesBuilder Bytes.Lazy.ByteString | ||||
| bytesLazyB = BytesBuilder Bytes.lazyByteString | ||||
| 
 | ||||
| utf8B :: BytesBuilder Text | ||||
| utf8B = textToBytesUtf8 >$< bytesB | ||||
| 
 | ||||
| utf8LazyB :: BytesBuilder Text.Lazy.Text | ||||
| utf8LazyB = textToBytesUtf8Lazy >$< bytesLazyB | ||||
| 
 | ||||
| intDecimalT :: TextBuilder Int | ||||
| intDecimalT = TextBuilder Text.decimal | ||||
| 
 | ||||
| intDecimalNaturalT :: TextBuilder Natural | ||||
| intDecimalNaturalT = TextBuilder Text.decimal | ||||
							
								
								
									
										44
									
								
								users/Profpatsch/my-prelude/src/Divisive.hs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								users/Profpatsch/my-prelude/src/Divisive.hs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,44 @@ | |||
| {-# LANGUAGE AllowAmbiguousTypes #-} | ||||
| 
 | ||||
| module Divisive where | ||||
| 
 | ||||
| import Data.Functor.Contravariant | ||||
| import Data.Functor.Contravariant.Divisible | ||||
| import GHC.Records (HasField (getField)) | ||||
| import Label | ||||
| 
 | ||||
| -- | Combine two divisibles into a struct with any two labelled fields. | ||||
| divide2 :: | ||||
|   forall l1 l2 t1 t2 d r. | ||||
|   (Divisible d, HasField l1 r t1, HasField l2 r t2) => | ||||
|   d t1 -> | ||||
|   d t2 -> | ||||
|   d r | ||||
| divide2 = divide (\r -> (getField @l1 r, getField @l2 r)) | ||||
| 
 | ||||
| -- | Combine two divisibles into a 'T2' with any two labelled fields. | ||||
| dt2 :: | ||||
|   forall l1 l2 t1 t2 d. | ||||
|   (Divisible d) => | ||||
|   d t1 -> | ||||
|   d t2 -> | ||||
|   d (T2 l1 t1 l2 t2) | ||||
| dt2 = divide (\(T2 a b) -> (getField @l1 a, getField @l2 b)) | ||||
| 
 | ||||
| -- | Combine three divisibles into a struct with any three labelled fields. | ||||
| divide3 :: forall l1 l2 l3 t1 t2 t3 d r. (Divisible d, HasField l1 r t1, HasField l2 r t2, HasField l3 r t3) => d t1 -> d t2 -> d t3 -> d r | ||||
| divide3 a b c = adapt >$< a `divided` b `divided` c | ||||
|   where | ||||
|     adapt r = ((getField @l1 r, getField @l2 r), getField @l3 r) | ||||
| 
 | ||||
| -- | Combine three divisibles into a 'T3' with any three labelled fields. | ||||
| dt3 :: | ||||
|   forall l1 l2 l3 t1 t2 t3 d. | ||||
|   (Divisible d) => | ||||
|   d t1 -> | ||||
|   d t2 -> | ||||
|   d t3 -> | ||||
|   d (T3 l1 t1 l2 t2 l3 t3) | ||||
| dt3 a b c = adapt >$< a `divided` b `divided` c | ||||
|   where | ||||
|     adapt (T3 a' b' c') = ((getField @l1 a', getField @l2 b'), getField @l3 c') | ||||
|  | @ -140,6 +140,7 @@ module MyPrelude | |||
|     contramap, | ||||
|     (>$<), | ||||
|     (>&<), | ||||
|     module Divisive, | ||||
|     Profunctor, | ||||
|     dimap, | ||||
|     lmap, | ||||
|  | @ -222,6 +223,7 @@ import Data.Traversable (for) | |||
| import Data.Vector (Vector) | ||||
| import Data.Void (Void, absurd) | ||||
| import Data.Word (Word8) | ||||
| import Divisive | ||||
| import GHC.Exception (errorCallWithCallStackException) | ||||
| import GHC.Exts (Any, RuntimeRep, TYPE, raise#) | ||||
| import GHC.Generics (Generic) | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue