Exposing newtype coercions to Haskell
Simon Peyton-Jones
simonpj at microsoft.com
Tue Jul 23 20:58:59 CEST 2013
If you add -XIncoherentInstances *just to the module that has instance IsNT a a*, then it'll work fine I think. This says "pick this instance even though an instantiation of the constraint might match a more specific instance". You don't need the flag in importing modules.
I think that's fine. For the IsNT class, where users cannot define their own instances, it really is not incoherent to pick the IsNT a a instance. The operational effect is always a no-op.
In short, I think this'll work fine.
Simon
| -----Original Message-----
| From: ghc-devs [mailto:ghc-devs-bounces at haskell.org] On Behalf Of Joachim
| Breitner
| Sent: 23 July 2013 12:45
| To: ghc-devs at haskell.org
| Subject: Re: Exposing newtype coercions to Haskell
|
| Hi,
|
| Am Dienstag, den 23.07.2013, 09:51 +0100 schrieb Richard Eisenberg:
| > I think that thinking about the base case is also
| > productive, but I don't have a clear enough opinion to express on that
| > front.
|
| and it seems to tricky; Here we are hitting problems that we did not
| have with the non-class-approach.
|
| tl;dr: "IsNT a a" only works with IncoherentInstances, which are in
| principle fine for this class (it is univalent in a sense), but
| requiring this extension would be quite unfortunate.
|
|
| Consider this ghci session (this already works here):
|
| Prelude GHC.NT> newtype Age = Age Int deriving Show
| Prelude GHC.NT> deriving instance IsNT Int Age
| Prelude GHC.NT> castNT (1::Int) :: Age -- good, works
| Age 1
| Prelude GHC.NT> deriving instance (IsNT a a', IsNT b b') => IsNT (Either a b)
| (Either a' b')
| Prelude GHC.NT> castNT (Left 1::Either Int Int) :: Either Age Age -- also works
| Left (Age 1)
| Prelude GHC.NT> castNT (Left 1::Either Int Int) :: Either Age Int -- does not work
| yet
|
| <interactive>:12:1:
| No instance for (IsNT Int Int) arising from a use of ‛castNT’
| In the expression:
| castNT (Left 1 :: Either Int Int) :: Either Age Int
| In an equation for ‛it’:
| it = castNT (Left 1 :: Either Int Int) :: Either Age Int
| Prelude GHC.NT> deriving instance IsNT Int Int
| Prelude GHC.NT> castNT (Left 1::Either Int Int) :: Either Age Int -- works now
| Left (Age 1)
| Prelude GHC.NT> :t castNT :: (Either Int Int) -> (Either Age Int)
| castNT :: (Either Int Int) -> (Either Age Int) :: Either Int Int -> Either Age Int
|
| But we are not able to cast such that one type parameter changes while
| leaving another type _variable_ untouched:
|
| Prelude GHC.NT> :t castNT :: (Either Int a) -> (Either Age a)
|
| <interactive>:1:1:
| No instance for (IsNT a1 a1) arising from a use of ‛castNT’
| Possible fix:
| add (IsNT a1 a1) to the context of
| an expression type signature: Either Int a1 -> Either Age a1
| or the inferred type of it :: Either Int a -> Either Age a
| In the expression: castNT :: (Either Int a) -> (Either Age a)
|
| and if we add "IsNT a a" as a base case:
|
| Prelude GHC.NT> instance IsNT a a
|
| <interactive>:34:10: Warning:
| No explicit method or default declaration for ‛coercion’
| In the instance declaration for ‛IsNT a a’
|
| we get
|
| Prelude GHC.NT> :t castNT :: (Either Int a) -> (Either Age a)
|
| <interactive>:1:1:
| Overlapping instances for IsNT a1 a1 arising from a use of ‛castNT’
| Matching instances:
| instance [overlap ok] IsNT a a -- Defined at <interactive>:34:10
| instance IsNT Int Int -- Defined at <interactive>:13:1
| instance (IsNT a a', IsNT b b') => IsNT (Either a b) (Either a' b')
| -- Defined at <interactive>:10:1
| (The choice depends on the instantiation of ‛a1’
| To pick the first instance above, use -XIncoherentInstances
| when compiling the other instance declarations)
| In the expression: castNT :: (Either Int a) -> (Either Age a)
|
|
| Re-doing the session with -XIncoherentInstances set helps, but requiring
| that for every module that wants to derive a IsNT class is probably not
| nice.
|
|
| The one-parameter-variant does not help either.
|
|
| One way to avoid the issue is to have two instances for Either:
|
| Prelude GHC.NT> deriving instance (IsNT a a') => IsNT (Either a b) (Either a' b)
| Prelude GHC.NT> deriving instance (IsNT b b') => IsNT (Either a b) (Either a b')
|
| This still requires OverlappingInstances, but I can get the desired cast
| without IncoherentInstances:
|
| Prelude GHC.NT> :t castNT :: Either Int a -> Either Age a
| castNT :: Either Int a -> Either Age a
| :: Either Int a -> Either Age a
|
| Other casts that should be “normal” are more complicated now:
|
| Prelude GHC.NT> let c = (castNT :: Either Age Int -> Either Age Age) . (castNT ::
| Either Int Int -> Either Age Int)
| Prelude GHC.NT> :t c
| c :: Either Int Int -> Either Age Age
| Prelude GHC.NT> c (Left 1)
| Left (Age 1)
|
|
|
| In any case, the whole thing needs a bit more massaging until it becomes
| sufficiently elegant.
|
| Greetings,
| Joachim
|
| --
| Joachim Breitner
| e-Mail: mail at joachim-breitner.de
| Homepage: http://www.joachim-breitner.de
| ICQ#: 74513189
| Jabber-ID: nomeata at joachim-breitner.de
More information about the ghc-devs
mailing list