La questione è che newtype
s sono coercibili alla loro rappresentazione purché il costruttore sia in ambito - anzi, questa è una grande parte della motivazione per Coercible
. E i vincoli Coercible
sono come qualsiasi altro vincolo di classe di tipo, e vengono automaticamente cercati e messi insieme per te, solo ancora di più. Così, coerce c3
sta trovando che avete
instance Coercible (Container p) [Int]
instance Coercible [Int] (Container p')
per tutti p
e p'
, e felicemente la costruzione della coercizione composito per voi via
instance (Coercible a b, Coercible b c) => Coercible a c
Se non si esporta il costruttore Container
- come probabilmente voglio fare comunque! - allora non è più noto che il newtype
è pari alla sua rappresentazione (si perdono i primi due casi sopra), e si ottiene l'errore desiderato in altri moduli:
ContainerClient.hs:13:6:
Couldn't match type ‘4’ with ‘3’
arising from trying to show that the representations of
‘Container 3’ and
‘Container 4’ are the same
Relevant role signatures: type role Container nominal nominal
In the expression: coerce c3
In an equation for ‘c4’: c4 = coerce c3
Tuttavia, sarai sempre in grado di rompere i tuoi invarianti nel modulo in cui definisci il newtype
(via coerce
o altro).
Come nota a margine, probabilmente non desidera utilizzare una funzione di accesso record di stile qui ed esportarla; che consente agli utenti di utilizzare la sintassi di registrazione-update per cambiare fuori il codice da sotto, così
c3 :: Container 3
c3 = mempty
c3' :: Container 3
c3' = c3{runContainer = []}
diventa valido. Rendi invece runContainer
una funzione indipendente.
Possiamo verificare che stiamo ottenendo la composizione delle due newtype
-Rappresentazione vincoli, cercando al centro (via -ddump-simpl
): all'interno del modulo che definisce Container
(che ho anche chiamato Container
), l'uscita è (se togliamo la lista di esportazione)
c4 :: Container 4
[GblId, Str=DmdType]
c4 =
c3
`cast` (Container.NTCo:Container[0] <GHC.TypeLits.Nat>_N <3>_N
; Sym (Container.NTCo:Container[0] <GHC.TypeLits.Nat>_N <4>_N)
:: Container 3 ~R# Container 4)
è un po 'difficile da leggere, ma la cosa importante da vedere è Container.NTCo:Container[0]
: il NTCo
è un newtype
coercizione tra il newtype
Container p
e s tipo di rappresentazione.Sym
gira questo intorno e ;
compone due vincoli.
Chiamare il vincolo finale γₓ
; allora l'intera digitazione derivazione è
Sym :: (a ~ b) -> (b ~ a)
-- Sym is built-in
(;) :: (a ~ b) -> (b ~ c) -> (a ~ c)
-- (;) is built-in
γₙ :: k -> (p :: k) -> Container p ~ [Int]
γₙ k p = Container.NTCo:Container[0] <k>_N <p>_N
γ₃ :: Container 3 ~ [Int]
γ₃ = γₙ GHC.TypeLits.Nat 3
γ₄ :: Container 4 ~ [Int]
γ₄ = γₙ GHC.TypeLits.Nat 4
γ₄′ :: [Int] ~ Container 4
γ₄′ = Sym γ₄
γₓ :: Container 3 ~ Container 4
γₓ = γ₃ ; γ₄′
Qui ci sono i file di origine che ho usato:
Container.hs:
{-# LANGUAGE FlexibleContexts, UndecidableInstances, ScopedTypeVariables,
RoleAnnotations, PolyKinds, DataKinds #-}
module Container (Container(), runContainer) where
import Data.Proxy
import Data.Reflection
import Data.Coerce
newtype Container p = Container { runContainer :: [Int] }
deriving (Eq, Show)
type role Container nominal
instance Reifies p Integer => Monoid (Container p) where
mempty = Container $ replicate (fromIntegral (reflect (Proxy :: Proxy p))) 0
mappend (Container l) (Container r) = Container $ l ++ r
c3 :: Container 3
c3 = mempty
-- Works
c4 :: Container 4
c4 = coerce c3
ContainerClient.hs:
{-# LANGUAGE DataKinds #-}
module ContainerClient where
import Container
import Data.Coerce
c3 :: Container 3
c3 = mempty
-- Doesn't work :-)
c4 :: Container 4
c4 = coerce c3
GHC 7.10 genera molti errori a causa della mancanza di annotazioni 'LANGUAGE' nel tuo codice originale Potresti aggiungerli? – Zeta
Hai controllato cosa succede se lo fai in un file '.hs'? GHCi può essere strano riguardo ai limiti del modulo. Sono abbastanza sicuro che 'hyperloglog' ha una propria dichiarazione di ruolo. – dfeuer
Strano ... l'annotazione del ruolo dovrebbe davvero impedire queste coercizioni. – chi