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 ./. [
|
src = depot.users.Profpatsch.exactSource ./. [
|
||||||
./my-prelude.cabal
|
./my-prelude.cabal
|
||||||
./src/Aeson.hs
|
./src/Aeson.hs
|
||||||
|
./src/Builder.hs
|
||||||
./src/Comparison.hs
|
./src/Comparison.hs
|
||||||
./src/Debug.hs
|
./src/Debug.hs
|
||||||
|
./src/Divisive.hs
|
||||||
./src/Json.hs
|
./src/Json.hs
|
||||||
./src/Json/Enc.hs
|
./src/Json/Enc.hs
|
||||||
./src/Arg.hs
|
./src/Arg.hs
|
||||||
|
|
@ -34,6 +36,7 @@ pkgs.haskellPackages.mkDerivation {
|
||||||
pkgs.haskellPackages.pa-pretty
|
pkgs.haskellPackages.pa-pretty
|
||||||
pkgs.haskellPackages.pa-field-parser
|
pkgs.haskellPackages.pa-field-parser
|
||||||
pkgs.haskellPackages.aeson-better-errors
|
pkgs.haskellPackages.aeson-better-errors
|
||||||
|
pkgs.haskellPackages.contravariant
|
||||||
pkgs.haskellPackages.foldl
|
pkgs.haskellPackages.foldl
|
||||||
pkgs.haskellPackages.resource-pool
|
pkgs.haskellPackages.resource-pool
|
||||||
pkgs.haskellPackages.error
|
pkgs.haskellPackages.error
|
||||||
|
|
|
||||||
|
|
@ -61,8 +61,10 @@ library
|
||||||
Aeson
|
Aeson
|
||||||
Arg
|
Arg
|
||||||
AtLeast
|
AtLeast
|
||||||
|
Builder
|
||||||
Comparison
|
Comparison
|
||||||
Debug
|
Debug
|
||||||
|
Divisive
|
||||||
Json
|
Json
|
||||||
Json.Enc
|
Json.Enc
|
||||||
Test
|
Test
|
||||||
|
|
@ -92,6 +94,7 @@ library
|
||||||
, base64-bytestring
|
, base64-bytestring
|
||||||
, bytestring
|
, bytestring
|
||||||
, containers
|
, containers
|
||||||
|
, contravariant
|
||||||
, foldl
|
, foldl
|
||||||
, unordered-containers
|
, unordered-containers
|
||||||
, resource-pool
|
, 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,
|
contramap,
|
||||||
(>$<),
|
(>$<),
|
||||||
(>&<),
|
(>&<),
|
||||||
|
module Divisive,
|
||||||
Profunctor,
|
Profunctor,
|
||||||
dimap,
|
dimap,
|
||||||
lmap,
|
lmap,
|
||||||
|
|
@ -222,6 +223,7 @@ import Data.Traversable (for)
|
||||||
import Data.Vector (Vector)
|
import Data.Vector (Vector)
|
||||||
import Data.Void (Void, absurd)
|
import Data.Void (Void, absurd)
|
||||||
import Data.Word (Word8)
|
import Data.Word (Word8)
|
||||||
|
import Divisive
|
||||||
import GHC.Exception (errorCallWithCallStackException)
|
import GHC.Exception (errorCallWithCallStackException)
|
||||||
import GHC.Exts (Any, RuntimeRep, TYPE, raise#)
|
import GHC.Exts (Any, RuntimeRep, TYPE, raise#)
|
||||||
import GHC.Generics (Generic)
|
import GHC.Generics (Generic)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue