witch 0.0.0.2 → 0.0.0.3
raw patch · 3 files changed
+145/−119 lines, 3 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Witch: class From a b
- Witch: instance GHC.Classes.Ord a => Witch.From [a] (Data.Set.Internal.Set a)
- Witch: instance GHC.Classes.Ord k => Witch.From [(k, v)] (Data.Map.Internal.Map k v)
- Witch: instance Witch.From (Data.Map.Internal.Map k v) [(k, v)]
- Witch: instance Witch.From (Data.Sequence.Internal.Seq a) [a]
- Witch: instance Witch.From (Data.Set.Internal.Set a) [a]
- Witch: instance Witch.From (GHC.Base.NonEmpty a) [a]
- Witch: instance Witch.From (a, b) (b, a)
- Witch: instance Witch.From (a, x) a
- Witch: instance Witch.From (x, a) a
- Witch: instance Witch.From Data.ByteString.Internal.ByteString Data.ByteString.Lazy.Internal.ByteString
- Witch: instance Witch.From Data.ByteString.Internal.ByteString [GHC.Word.Word8]
- Witch: instance Witch.From Data.ByteString.Lazy.Internal.ByteString Data.ByteString.Internal.ByteString
- Witch: instance Witch.From Data.Text.Internal.Lazy.Text Data.Text.Internal.Text
- Witch: instance Witch.From Data.Text.Internal.Text Data.Text.Internal.Lazy.Text
- Witch: instance Witch.From Data.Text.Internal.Text GHC.Base.String
- Witch: instance Witch.From Data.Void.Void x
- Witch: instance Witch.From GHC.Base.String Data.Text.Internal.Text
- Witch: instance Witch.From GHC.Int.Int16 GHC.Int.Int32
- Witch: instance Witch.From GHC.Int.Int32 GHC.Int.Int64
- Witch: instance Witch.From GHC.Int.Int8 GHC.Int.Int16
- Witch: instance Witch.From GHC.Integer.Type.Integer GHC.Real.Rational
- Witch: instance Witch.From GHC.Natural.Natural GHC.Integer.Type.Integer
- Witch: instance Witch.From GHC.Types.Bool GHC.Types.Int
- Witch: instance Witch.From GHC.Types.Char GHC.Types.Int
- Witch: instance Witch.From GHC.Types.Float GHC.Types.Double
- Witch: instance Witch.From GHC.Types.Int GHC.Integer.Type.Integer
- Witch: instance Witch.From GHC.Types.Word GHC.Natural.Natural
- Witch: instance Witch.From GHC.Word.Word16 GHC.Word.Word32
- Witch: instance Witch.From GHC.Word.Word32 GHC.Word.Word64
- Witch: instance Witch.From GHC.Word.Word8 GHC.Word.Word16
- Witch: instance Witch.From [GHC.Word.Word8] Data.ByteString.Internal.ByteString
- Witch: instance Witch.From [a] (Data.Sequence.Internal.Seq a)
- Witch: instance Witch.From a (Data.Either.Either a x)
- Witch: instance Witch.From a (Data.Either.Either x a)
- Witch: instance Witch.From a (GHC.Maybe.Maybe a)
- Witch: instance Witch.From a (x -> a)
- Witch: instance Witch.From a [a]
- Witch: instance Witch.From a a
+ Witch: cast :: (Cast source target, Coercible source target) => source -> target
+ Witch: class Cast source target
+ Witch: instance GHC.Classes.Ord a => Witch.Cast [a] (Data.Set.Internal.Set a)
+ Witch: instance GHC.Classes.Ord k => Witch.Cast [(k, v)] (Data.Map.Internal.Map k v)
+ Witch: instance Witch.Cast (Data.Map.Internal.Map k v) [(k, v)]
+ Witch: instance Witch.Cast (Data.Sequence.Internal.Seq a) [a]
+ Witch: instance Witch.Cast (Data.Set.Internal.Set a) [a]
+ Witch: instance Witch.Cast (GHC.Base.NonEmpty a) [a]
+ Witch: instance Witch.Cast (a, b) (b, a)
+ Witch: instance Witch.Cast (a, x) a
+ Witch: instance Witch.Cast (x, a) a
+ Witch: instance Witch.Cast Data.ByteString.Internal.ByteString Data.ByteString.Lazy.Internal.ByteString
+ Witch: instance Witch.Cast Data.ByteString.Internal.ByteString [GHC.Word.Word8]
+ Witch: instance Witch.Cast Data.ByteString.Lazy.Internal.ByteString Data.ByteString.Internal.ByteString
+ Witch: instance Witch.Cast Data.Text.Internal.Lazy.Text Data.Text.Internal.Text
+ Witch: instance Witch.Cast Data.Text.Internal.Text Data.Text.Internal.Lazy.Text
+ Witch: instance Witch.Cast Data.Text.Internal.Text GHC.Base.String
+ Witch: instance Witch.Cast Data.Void.Void x
+ Witch: instance Witch.Cast GHC.Base.String Data.Text.Internal.Text
+ Witch: instance Witch.Cast GHC.Int.Int16 GHC.Int.Int32
+ Witch: instance Witch.Cast GHC.Int.Int32 GHC.Int.Int64
+ Witch: instance Witch.Cast GHC.Int.Int8 GHC.Int.Int16
+ Witch: instance Witch.Cast GHC.Integer.Type.Integer GHC.Real.Rational
+ Witch: instance Witch.Cast GHC.Natural.Natural GHC.Integer.Type.Integer
+ Witch: instance Witch.Cast GHC.Types.Bool GHC.Types.Int
+ Witch: instance Witch.Cast GHC.Types.Char GHC.Types.Int
+ Witch: instance Witch.Cast GHC.Types.Float GHC.Types.Double
+ Witch: instance Witch.Cast GHC.Types.Int GHC.Integer.Type.Integer
+ Witch: instance Witch.Cast GHC.Types.Word GHC.Natural.Natural
+ Witch: instance Witch.Cast GHC.Word.Word16 GHC.Word.Word32
+ Witch: instance Witch.Cast GHC.Word.Word32 GHC.Word.Word64
+ Witch: instance Witch.Cast GHC.Word.Word8 GHC.Word.Word16
+ Witch: instance Witch.Cast [GHC.Word.Word8] Data.ByteString.Internal.ByteString
+ Witch: instance Witch.Cast [a] (Data.Sequence.Internal.Seq a)
+ Witch: instance Witch.Cast a (Data.Either.Either a x)
+ Witch: instance Witch.Cast a (Data.Either.Either x a)
+ Witch: instance Witch.Cast a (GHC.Maybe.Maybe a)
+ Witch: instance Witch.Cast a (x -> a)
+ Witch: instance Witch.Cast a [a]
+ Witch: instance Witch.Cast a a
- Witch: from :: (From a b, Coercible a b) => a -> b
+ Witch: from :: forall s target source. (Ambiguous s ~ source, Cast source target) => source -> target
- Witch: into :: forall b a. From a b => a -> b
+ Witch: into :: forall t source target. (Ambiguous t ~ target, Cast source target) => source -> target
- Witch: via :: forall b a c. (From a b, From b c) => a -> c
+ Witch: via :: forall through source target. (Cast source through, Cast through target) => source -> target
Files
- README.markdown +4/−0
- src/lib/Witch.hs +139/−118
- witch.cabal +2/−1
README.markdown view
@@ -1,3 +1,7 @@ # Witch +[](https://github.com/tfausak/witch/actions)+[](https://hackage.haskell.org/package/witch)+[](https://www.stackage.org/package/witch)+ :mage_woman: Convert values from one type into another.
src/lib/Witch.hs view
@@ -3,8 +3,9 @@ {-# language FlexibleInstances #-} {-# language MultiParamTypeClasses #-} {-# language ScopedTypeVariables #-}+{-# language TypeFamilies #-} --- | This module provides the 'From' type class for converting values between+-- | This module provides the 'Cast' type class for converting values between -- various types. This aims to be a common interface for the various @xToY@ or -- @yFromX@ functions you might write instead. It is inspired by the -- @std::convert::From@ trait that the Rust programming language provides.@@ -35,7 +36,7 @@ -- - <https://github.com/kframework/kore/blob/626f230/kore/src/From.hs>: -- This package is not available on Hackage, but otherwise is very similar to -- this one.-module Witch (From(from), into, via) where+module Witch (Cast(cast), from, into, via) where import qualified Data.ByteString as ByteString import qualified Data.ByteString.Lazy as LazyByteString@@ -54,7 +55,7 @@ import qualified Numeric.Natural as Natural -- | This type class represents a way to convert values from some type into--- another type. The constraint @From a b@ means that you can convert from a+-- another type. The constraint @Cast a b@ means that you can convert from a -- value of type @a@ into a value of type @b@. -- -- This is primarily intended for "zero cost" conversions like @newtype@s. For@@ -62,8 +63,8 @@ -- say: -- -- > newtype Name = Name String--- > instance From String Name--- > instance From Name String+-- > instance Cast String Name+-- > instance Cast Name String -- -- And then you could convert back and forth between @Name@s and @String@s: --@@ -75,8 +76,8 @@ -- This type class does not have any laws, but it does have some expectations: -- -- - Conversions should be total. A conversion should never fail or crash.--- Avoid writing instances like @From Int (Maybe Char)@. (It might be--- worthwhile to have a separate @TryFrom@ type class for this.)+-- Avoid writing instances like @Cast Int (Maybe Char)@. (It might be+-- worthwhile to have a separate @TryCast@ type class for this.) -- -- - Conversions should be unambiguous. For example there are many ways to -- decode a @ByteString@ into @Text@, so you shouldn't provide an instance@@ -86,213 +87,233 @@ -- @String@ to @Text@ is probably fine, but converting from a UTF-8 encoded -- @ByteString@ to @Text@ is problematic. ----- - Conversions should be lossless. In other words if you have @From a b@ then+-- - Conversions should be lossless. In other words if you have @Cast a b@ then -- no two @a@ values should be converted to the same @b@ value. For example--- @From Int Integer@ is fine because every @Int@ can be mapped to a--- corresponding @Integer@, but @From Integer Int@ is not good because some+-- @Cast Int Integer@ is fine because every @Int@ can be mapped to a+-- corresponding @Integer@, but @Cast Integer Int@ is not good because some -- @Integer@s are out of bounds and would need to be clamped. ----- - If you have both @From a b@ and @From b a@, then @from . from@ should be+-- - If you have both @Cast a b@ and @Cast b a@, then @cast . cast@ should be -- the same as 'id'. In other words @a@ and @b@ are isomorphic. ----- - If you have both @From a b@ and @From b c@, then it's up to you if you--- want to provide @From a c@. Sometimes using 'via' is ergonomic enough,+-- - If you have both @Cast a b@ and @Cast b c@, then it's up to you if you+-- want to provide @Cast a c@. Sometimes using 'via' is ergonomic enough, -- other times you want the extra instance. (It would be nice if we could--- provide @instance (From a b, From b c) => From a c where from = via \@b@.)-class From a b where- -- | This method converts a value from one type into another. This is- -- intended to be used with the @TypeApplications@ language extension. For- -- example, here are a few ways to convert from an 'Int' into an 'Integer':- --- -- > from @Int @Integer 123- -- > from @_ @Integer (123 :: Int)- -- > from @Int @_ 123 :: Integer- -- > from @Int 123 :: Integer- -- > from (123 :: Int) :: Integer- --- -- Often the context around an expression will make the explicit type- -- signatures unnecessary. If you find yourself using a partial type- -- signature, consider using 'into' instead. For example:- --- -- > let someInt = 123 :: Int- -- > from @_ @Integer someInt -- avoid this- -- > into @Integer someInt -- prefer this+-- provide @instance (Cast a b, Cast b c) => Cast a c where cast = via \@b@.)+class Cast source target where+ -- | This method implements the conversion of a value between types. In+ -- practice most instances don't need an explicit implementation. At call+ -- sites you'll usually want to use 'from' or 'into' instead of 'cast'. --- -- The default implementation of 'from' simply calls 'Coerce.coerce', which+ -- The default implementation of 'cast' simply calls 'Coerce.coerce', which -- works for types that have the same runtime representation.- from :: a -> b- default from :: Coerce.Coercible a b => a -> b- from = Coerce.coerce+ cast :: source -> target+ default cast :: Coerce.Coercible source target => source -> target+ cast = Coerce.coerce +-- This ugly hack is used to require type applications when calling 'from' and+-- 'into'. See <https://twitter.com/taylorfausak/status/1329084033003782148>.+data Never+type family Ambiguous a where+ Ambiguous Never = ()+ Ambiguous a = a++-- | This function converts a value from one type into another. This is+-- intended to be used with the @TypeApplications@ language extension. The+-- @Ambiguous@ type in the signature makes a type application required. If+-- you'd prefer not to provide a type application, use 'cast' instead.+--+-- As an example, here are a few ways to convert from an 'Int' into an+-- 'Integer':+--+-- > from @Int @Integer 123+-- > from @_ @Integer (123 :: Int)+-- > from @Int @_ 123 :: Integer+-- > from @Int 123 :: Integer+--+-- Often the context around an expression will make the explicit type+-- signatures unnecessary. If you find yourself using a partial type+-- signature, consider using 'into' instead. For example:+--+-- > let someInt = 123 :: Int+-- > from @_ @Integer someInt -- avoid this+-- > into @Integer someInt -- prefer this+--+from :: forall s target source . (Ambiguous s ~ source, Cast source target) => source -> target+from = cast+ -- | This function converts a value from one type into another. This is the -- same as 'from' except that the type variables are in the opposite order.-into :: forall b a . From a b => a -> b-into = from+into :: forall t source target . (Ambiguous t ~ target, Cast source target) => source -> target+into = cast -- | This function converts a value from one type into another by going through--- some third type. This is the same as calling 'from' (or 'into') twice, but--- can sometimes be more convenient.+-- some third type. This is the same as calling 'cast' (or 'from' or 'into')+-- twice, but can sometimes be more convenient. -- -- Note that the type in the middle of the conversion is the first type -- variable of this function. In other words, @via \@b \@a \@c@ first converts -- from @a@ to @b@, and then from @b@ to @c@. Often both @a@ and @c@ will be -- inferred from context, which means you can just write @via \@b@.-via :: forall b a c . (From a b, From b c) => a -> c-via = from . (\ x -> x :: b) . from+via :: forall through source target . (Cast source through, Cast through target) => source -> target+via = cast . (\ x -> x :: through) . cast -- | 'id'-instance From a a where- from = id+instance Cast a a where+ cast = id -- | 'const'-instance From a (x -> a) where- from = const+instance Cast a (x -> a) where+ cast = const -- | 'pure'-instance From a [a] where- from = pure+instance Cast a [a] where+ cast = pure -- | 'Just'-instance From a (Maybe a) where- from = Just+instance Cast a (Maybe a) where+ cast = Just -- | 'Left'-instance From a (Either a x) where- from = Left+instance Cast a (Either a x) where+ cast = Left -- | 'Right'-instance From a (Either x a) where- from = Right+instance Cast a (Either x a) where+ cast = Right -- | 'Void.absurd'-instance From Void.Void x where- from = Void.absurd+instance Cast Void.Void x where+ cast = Void.absurd -- | 'fst'-instance From (a, x) a where- from = fst+instance Cast (a, x) a where+ cast = fst -- | 'snd'-instance From (x, a) a where- from = snd+instance Cast (x, a) a where+ cast = snd -- | 'Tuple.swap'-instance From (a, b) (b, a) where- from = Tuple.swap+instance Cast (a, b) (b, a) where+ cast = Tuple.swap -- | 'NonEmpty.toList'-instance From (NonEmpty.NonEmpty a) [a] where- from = NonEmpty.toList+instance Cast (NonEmpty.NonEmpty a) [a] where+ cast = NonEmpty.toList -- | 'fromIntegral'-instance From Word.Word8 Word.Word16 where- from = fromIntegral+instance Cast Word.Word8 Word.Word16 where+ cast = fromIntegral -- | 'fromIntegral'-instance From Word.Word16 Word.Word32 where- from = fromIntegral+instance Cast Word.Word16 Word.Word32 where+ cast = fromIntegral -- | 'fromIntegral'-instance From Word.Word32 Word.Word64 where- from = fromIntegral+instance Cast Word.Word32 Word.Word64 where+ cast = fromIntegral -- | 'fromIntegral'-instance From Word Natural.Natural where- from = fromIntegral+instance Cast Word Natural.Natural where+ cast = fromIntegral -- | 'fromIntegral'-instance From Natural.Natural Integer where- from = fromIntegral+instance Cast Natural.Natural Integer where+ cast = fromIntegral -- | 'fromIntegral'-instance From Int.Int8 Int.Int16 where- from = fromIntegral+instance Cast Int.Int8 Int.Int16 where+ cast = fromIntegral -- | 'fromIntegral'-instance From Int.Int16 Int.Int32 where- from = fromIntegral+instance Cast Int.Int16 Int.Int32 where+ cast = fromIntegral -- | 'fromIntegral'-instance From Int.Int32 Int.Int64 where- from = fromIntegral+instance Cast Int.Int32 Int.Int64 where+ cast = fromIntegral -- | 'fromIntegral'-instance From Int Integer where- from = fromIntegral+instance Cast Int Integer where+ cast = fromIntegral -- | 'fromIntegral'-instance From Integer Rational where- from = fromIntegral+instance Cast Integer Rational where+ cast = fromIntegral -- | 'realToFrac'-instance From Float Double where- from = realToFrac+instance Cast Float Double where+ cast = realToFrac -- | 'fromEnum'-instance From Bool Int where- from = fromEnum+instance Cast Bool Int where+ cast = fromEnum -- | 'fromEnum'-instance From Char Int where- from = fromEnum+instance Cast Char Int where+ cast = fromEnum -- | 'ByteString.pack'-instance From [Word.Word8] ByteString.ByteString where- from = ByteString.pack+instance Cast [Word.Word8] ByteString.ByteString where+ cast = ByteString.pack -- | 'ByteString.unpack'-instance From ByteString.ByteString [Word.Word8] where- from = ByteString.unpack+instance Cast ByteString.ByteString [Word.Word8] where+ cast = ByteString.unpack -- | 'LazyByteString.fromStrict'-instance From ByteString.ByteString LazyByteString.ByteString where- from = LazyByteString.fromStrict+instance Cast ByteString.ByteString LazyByteString.ByteString where+ cast = LazyByteString.fromStrict -- | 'LazyByteString.toStrict'-instance From LazyByteString.ByteString ByteString.ByteString where- from = LazyByteString.toStrict+instance Cast LazyByteString.ByteString ByteString.ByteString where+ cast = LazyByteString.toStrict -- | 'Text.pack'-instance From String Text.Text where- from = Text.pack+--+-- Note that some 'Char' values cannot be represented in 'Text' and will be+-- replaced by U+FFFD.+instance Cast String Text.Text where+ cast = Text.pack -- | 'Text.unpack'-instance From Text.Text String where- from = Text.unpack+instance Cast Text.Text String where+ cast = Text.unpack -- | 'LazyText.fromStrict'-instance From Text.Text LazyText.Text where- from = LazyText.fromStrict+instance Cast Text.Text LazyText.Text where+ cast = LazyText.fromStrict -- | 'LazyText.toStrict'-instance From LazyText.Text Text.Text where- from = LazyText.toStrict+instance Cast LazyText.Text Text.Text where+ cast = LazyText.toStrict -- | 'Seq.fromList'-instance From [a] (Seq.Seq a) where- from = Seq.fromList+instance Cast [a] (Seq.Seq a) where+ cast = Seq.fromList -- | 'Foldable.toList'-instance From (Seq.Seq a) [a] where- from = Foldable.toList+instance Cast (Seq.Seq a) [a] where+ cast = Foldable.toList -- | 'Set.fromList' -- -- Note that this will remove duplicate elements from the list.-instance Ord a => From [a] (Set.Set a) where- from = Set.fromList+instance Ord a => Cast [a] (Set.Set a) where+ cast = Set.fromList -- | 'Set.toAscList'-instance From (Set.Set a) [a] where- from = Set.toAscList+instance Cast (Set.Set a) [a] where+ cast = Set.toAscList -- | 'Map.fromList' -- -- Note that if there are duplicate keys in the list, the one closest to the -- end will win.-instance Ord k => From [(k, v)] (Map.Map k v) where- from = Map.fromList+instance Ord k => Cast [(k, v)] (Map.Map k v) where+ cast = Map.fromList -- | 'Map.toAscList'-instance From (Map.Map k v) [(k, v)] where- from = Map.toAscList+instance Cast (Map.Map k v) [(k, v)] where+ cast = Map.toAscList
witch.cabal view
@@ -9,7 +9,7 @@ maintainer: Taylor Fausak name: witch synopsis: Convert values from one type into another.-version: 0.0.0.2+version: 0.0.0.3 source-repository head location: https://github.com/tfausak/witch@@ -26,6 +26,7 @@ ghc-options: -Weverything -Wno-implicit-prelude+ -Wno-redundant-constraints -Wno-safe -Wno-unsafe hs-source-dirs: src/lib