Skip to content

Instantly share code, notes, and snippets.

@Profpatsch
Last active April 3, 2023 17:01
Show Gist options
  • Save Profpatsch/b49c5f4bace41fa07338e8c07252d712 to your computer and use it in GitHub Desktop.
Save Profpatsch/b49c5f4bace41fa07338e8c07252d712 to your computer and use it in GitHub Desktop.
Implement only `fromInteger` for types where num-literals are possible, with less boilerplate
-- | Implement this class if you want your type to only implement the part of 'Num'
-- that allows creating them from Integer-literals, then derive Num via 'NumLiteralOnly':
--
-- @
-- data Foo = Foo Integer
-- deriving (Num) via (NumLiteralOnly "Foo" Foo)
--
-- instance IntegerLiteral Foo where
-- integerLiteral i = Foo i
-- @
class IntegerLiteral a where
integerLiteral :: Integer -> a
-- | The same as 'IntegerLiteral' but for floating point literals.
class RationalLiteral a where
rationalLiteral :: Rational -> a
-- | Helper class for @deriving (Num) via …@, implements only literal syntax for integer and floating point numbers,
-- and throws descriptive runtime errors for any other methods in 'Num'.
--
-- See 'IntegerLiteral' and 'RationalLiteral' for examples.
newtype NumLiteralOnly (sym :: Symbol) num = NumLiteralOnly num
instance (IntegerLiteral num, KnownSymbol sym) => Num (NumLiteralOnly sym num) where
fromInteger = NumLiteralOnly . integerLiteral
(+) = error [fmt|Only use as numeric literal allowed for {symbolVal (Proxy @sym)}, you tried to add (+) (NumLiteralOnly)|]
(*) = error [fmt|Only use as numeric literal allowed for {symbolVal (Proxy @sym)}, you tried to multiply (*) (NumLiteralOnly)|]
(-) = error [fmt|Only use as numeric literal allowed for {symbolVal (Proxy @sym)}, you tried to subtract (-) (NumLiteralOnly)|]
abs = error [fmt|Only use as numeric literal allowed for {symbolVal (Proxy @sym)}, you tried to use `abs` (NumLiteralOnly)|]
signum = error [fmt|Only use as numeric literal allowed for {symbolVal (Proxy @sym)}, you tried to use `signum` (NumLiteralOnly)|]
instance (IntegerLiteral num, RationalLiteral num, KnownSymbol sym) => Fractional (NumLiteralOnly sym num) where
fromRational = NumLiteralOnly . rationalLiteral
recip = error [fmt|Only use as rational literal allowed for {symbolVal (Proxy @sym)}, you tried to use `recip` (NumLiteralOnly)|]
(/) = error [fmt|Only use as numeric literal allowed for {symbolVal (Proxy @sym)}, you tried to divide (/) (NumLiteralOnly)|]
as numeric literal allowed for {symbolVal (Proxy @sym)}, you tried to use `signum` (NumLiteralOnly)|]
data Foo = Foo Integer
deriving (Num) via (NumLiteralOnly "Foo" Foo)
instance IntegerLiteral Foo where
integerLiteral i = Foo i
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment