exon-1.7.3.0: lib/Exon/Class/ToSegment.hs
{-# options_haddock prune #-}
-- | Description: Internal
module Exon.Class.ToSegment where
import GHC.TypeLits (ErrorMessage)
import Exon.Generic (IsNewtype, Unwrap (unwrap))
class NewtypeSegment (wrapped :: Maybe Type) a b where
newtypeSegment :: a -> b
instance (
Unwrap a b,
ToSegment b c
) => NewtypeSegment ('Just b) a c where
newtypeSegment = toSegment . unwrap
type family Q (a :: k) :: ErrorMessage where
Q a = "‘" <> a <> "’"
type family NoGenericMessage (a :: Type) (b :: Type) :: Constraint where
NoGenericMessage a a = a ~ a
NoGenericMessage a b =
TypeError (
"Found an expression of type " <> Q a <> " in a quote of type " <> Q b <> "." %
"If " <> Q a <> " is a newtype of " <> Q b <> " that should be converted automatically," %
"you need to add " <> Q "deriving (Generic)" <> " to its declaration." %
"You can also implement " <> Q ("instance ToSegment " <> a <> " " <> b) <> " for custom interpolation."
)
instance (
NoGenericMessage a b,
a ~ b
) => NewtypeSegment 'Nothing a b where
newtypeSegment = id
-- | This class determines how an expression is converted to an interpolation quote's result type.
--
-- For a quote like @[exon|a #{exp :: T} c|] :: R@, the instance @ToSegment T R@ is used to turn @T@ into @R@.
-- Aside from specialized instances for stringly types, the default implementation uses 'Generic' to unwrap newtypes
-- that either match the result type exactly, or uses 'fromString' for result types that implement 'IsString'.
--
-- So given:
--
-- >>> newtype T = T Text deriving newtype (Generic)
-- >>> newtype R = R Text deriving newtype (IsString, Semigroup, Monoid)
--
-- the quote from earlier would generically unwrap @T@ and use 'fromString' to construct an @R@.
class ToSegment a b where
toSegment :: a -> b
instance {-# incoherent #-} (
IsNewtype a wrapped,
NewtypeSegment wrapped a b
) => ToSegment a b where
toSegment =
newtypeSegment @wrapped
instance {-# overlappable #-} (
IsString a
) => ToSegment String a where
toSegment =
fromString . toString
instance {-# overlappable #-} (
IsString a
) => ToSegment Text a where
toSegment =
fromString . toString
instance {-# overlappable #-} (
IsString a
) => ToSegment LText a where
toSegment =
fromString . toString
instance {-# overlappable #-} (
IsString a
) => ToSegment ByteString a where
toSegment =
fromString . decodeUtf8
instance {-# overlappable #-} (
IsString a
) => ToSegment LByteString a where
toSegment =
fromString . decodeUtf8