简体   繁体   中英

Haskell - GeneralizedNewtypeDeriving pragma's interaction with constraints and datatypes

This is a question arising from my code at:

Haskell - Creating constraints and applying to datatypes

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
class NewConstraint a where
  getTrue :: a -> Bool
newtype Identity a = Identity a
  deriving (Eq, Show, Num)
instance (Num a, NewConstraint a) =>
  NewConstraint (Identity a) where
    getTrue x = True
test :: Bool
test = getTrue (Identity 1)

Is my use of the GeneralizedNewtypeDeriving pragma correct? I had intended for Identity a to take on Num a characteristics without doing up a Num a constrained instance for NewConstraint , but it didn't work. Is this approach possible? Where did I go wrong?

UPDATE on 30 June 2021

This is my current code:

class NewConstraint a where
  getTrue :: a -> a
newtype Identity a = Identity a
  deriving (Show)
instance NewConstraint (Identity a) where
  getTrue x = x
-- test :: Identity Integer
test = getTrue (Identity 1)

It works without the need for {-# LANGUAGE GeneralizedNewtypeDeriving #-} . When is the pragma needed?

The instance of Num derived via GeneralizedNewtypeDeriving for Identity has the form:

instance (Num a) => Num (Identity a) where
  fromInteger i = Identity (fromInteger i)
  Identity x + Identity y = Identity (x + y)
  -- …
  -- Similar for ‘(-)’, ‘(*)’, ‘abs’, ‘negate’, ‘signum’.
  -- …

That is, if the wrapped type X is in Num , so you can write 1 :: X , then Identity X is also in Num , so you can write 1 :: Identity X as well; similarly, you can use the Num arithmetic operators:

x, y, z :: Identity Int

x = Identity 5
y = Identity 7
z = x * y       -- == Identity 35

So instead of writing Identity 1 , which has the type Identity Integer by the defaulting of Num a => a to Integer , you can write just 1 . For illustration, if you remove the ambiguous Num a & NewConstraint a constraints from the NewConstraint (Identity a) instance, then you can write:

test :: Bool
test = getTrue (1 :: Identity Int)

Or, with the PartialTypeSignatures language option, you can omit the inner annotation, which defaults the wrapped type to Integer :

test = getTrue (1 :: Identity _)

You need some annotation in this particular case for the same reason that you need one in show (read "123") : you need to determine which overload of read :: Read a => String -> a and show :: Show a => a -> String you're calling. In this case, you have fromInteger :: Num a => Integer -> a implicitly due to the number literal, and getTrue :: NewConstraint a => a -> Bool . You must specify at least that a = Identity b , whereupon the type Num b => b is still ambiguous, but Num can be defaulted.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM