diff --git a/CHANGELOG.md b/CHANGELOG.md index cb49b2b..b1fa457 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [PVP versioning](https://pvp.haskell.org/). +## v0.1.2 _(2024-11-29)_ + +### Added +- Added type aliases `SomesF` and `SomeF` for existentials with fixed containers +- Added `mapSome` with infix `(<~$>)` and `traverseSome` with infix `(<~*>)` + ## v0.1.1 _(2024-11-29)_ ### Added diff --git a/README.md b/README.md index e837654..837c0c9 100644 --- a/README.md +++ b/README.md @@ -7,19 +7,25 @@ It allows you to enforce multiple constraints on polymorphic types and container ## Core -- **Existential Types**: `Somes` and `Somes1` provide existential wrappers for types with multiple constraints. -- **Convenient Aliases**: Simplified types `Some` and `Some1` for scenarios where just one constraint is needed. - +- **Existential types**: `Somes` and `Somes1` provide existential wrappers for types with multiple constraints. +- **Convenient aliases**: Simplified types `Some` and `Some1` for scenarios where just one constraint is needed. +- **More convenient aliases**: Simplified types `SomeF` and `SomesF` for scenarios where the container of a `Somes1` is known. +- **Natural transformations with constraints** + - `mapSome :: (forall a. AllC csa a => f a -> g a) -> SomesF f csa -> SomesF g csa` + - `traverseSome :: Functor m => (forall a. AllC csa a => f a -> m (g a)) -> SomesF f csa -> m (SomesF g csa)` ## Usage ```haskell import Data.Some.Constraint someShowableOrd :: Somes '[Show, Ord] -someShowableOrd = Some (mempty :: [Double]) +someShowableOrd = Some [1, 2, 3 :: Int] someNumFunctor :: Some1 Functor Num -someNumFunctor = Some1 $ [1, 2, 3 :: Int] +someNumFunctor = Some1 [1, 2, 3 :: Int] + +someListShowable :: SomeF [] Show +someListShowable = Some1 [1, 2, 3 :: Int] ``` ## Contact information diff --git a/constrained-some.cabal b/constrained-some.cabal index 7b5e938..9cfb201 100644 --- a/constrained-some.cabal +++ b/constrained-some.cabal @@ -1,6 +1,6 @@ cabal-version: 3.0 name: constrained-some -version: 0.1.1 +version: 0.1.2 synopsis: Existential type that can be constrained description: This library provides utilities for working with existential types and type-level constraints. It allows you to enforce multiple constraints on polymorphic types and containers complementing the package some. diff --git a/src/Data/Some/Constraint.hs b/src/Data/Some/Constraint.hs index c709e58..0930131 100644 --- a/src/Data/Some/Constraint.hs +++ b/src/Data/Some/Constraint.hs @@ -9,8 +9,27 @@ {-# LANGUAGE TypeApplications #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE QuantifiedConstraints #-} -module Data.Some.Constraint where +module Data.Some.Constraint +( + -- * Combining constraints + AllC + + -- * Existentials + -- ** Flat existentials + , Somes(..), Some + + -- ** Containerized existentials + -- *** Constrained containers + , Somes1(..), Some1 + + -- *** Fixed containers + , SomesF, SomeF + , mapSome, (<~$>) + , traverseSome, (<~*>) +) where import Data.Kind @@ -34,6 +53,12 @@ data Somes cs where (a :: Type). AllC cs a => a -> Somes cs +instance {-# OVERLAPPING #-} Show (Somes (Show ': cs)) where + showsPrec d (Some x) = showParen (d > 10) $ showString "Some " . showsPrec 11 x + +instance {-# OVERLAPPABLE #-} Show (Somes cs) => Show (Somes (c ': cs)) where + showsPrec d (Some x) = showsPrec d (Some @cs x) + -- | Alias for 'Somes' with just one 'Constraint'. type Some c = Somes '[c] @@ -57,8 +82,32 @@ data Somes1 csf csa where -- | Alias for 'Somes1' with just one 'Constraint'. type Some1 cf ca = Somes1 '[cf] '[ca] -instance {-# OVERLAPPING #-} Show (Somes (Show ': cs)) where - showsPrec d (Some x) = showParen (d > 10) $ showString "Some " . showsPrec 11 x +-- | Alias for 'Somes1' with a container @f@ and multiple 'Constraint's @csa@ for its elements. +type SomesF f csa = Somes1 '[(~) f] csa -instance {-# OVERLAPPABLE #-} Show (Somes cs) => Show (Somes (c ': cs)) where - showsPrec d (Some x) = showsPrec d (Some @cs x) +-- | Alias for 'SomeF' with just one 'Constraint' for its elements. +type SomeF f c = SomesF f '[c] + +-- | Natural transformation of one container to another. +mapSome :: (forall a. AllC csa a => f a -> g a) -> SomesF f csa -> SomesF g csa +mapSome f (Some1 x) = Some1 (f x) + +infixl 4 <~$> +-- | Infix version of 'mapSome'. +(<~$>) :: (forall a. AllC csa a => f a -> g a) -> SomesF f csa -> SomesF g csa +(<~$>) = mapSome + +-- | Natural transformation of one container to another - with side effects in @m@. +traverseSome :: Functor m => (forall a. AllC csa a => f a -> m (g a)) -> SomesF f csa -> m (SomesF g csa) +traverseSome f (Some1 x) = Some1 <$> f x + +infixl 4 <~*> +-- | Infix version of 'traverseSome'. +(<~*>) :: Functor m => (forall a. AllC csa a => f a -> m (g a)) -> SomesF f csa -> m (SomesF g csa) +(<~*>) = traverseSome + +instance {-# OVERLAPPING #-} (forall a. Show a => Show (f a)) => Show (SomesF f (Show ': cs)) where + showsPrec d (Some1 x) = showParen (d > 10) $ showString "Some " . showsPrec 11 x + +instance {-# OVERLAPPABLE #-} Show (SomesF f cs) => Show (SomesF f (c ': cs)) where + showsPrec d (Some1 x) = showsPrec d (Some1 @_ @('[(~) f]) @cs x)