этот вопрос заключается в том, что newtype
s являются коэрцибелами к их представлению до тех пор, пока конструктор находится в сфере действия - действительно, это большая часть мотивации для Coercible
. А ограничения Coercible
похожи на любые ограничения типа класса, и их автоматически ищут и объединяют для вас, тем более. Таким образом, coerce c3
находит, что у вас есть
instance Coercible (Container p) [Int]
instance Coercible [Int] (Container p')
для всех p
и p'
и счастливо строить композитное принуждение для вас через
instance (Coercible a b, Coercible b c) => Coercible a c
Если вы не экспортировать Container
конструктора - как вы, вероятно, хочу все равно! - то это уже не известно, что newtype
равно его представления (вы потеряете первые два экземпляра выше), и вы получите нужную ошибку в других модулях:
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
Однако, вы всегда будете иметь возможность разбейте свои инварианты в модуле, где вы определяете newtype
(через coerce
или иначе).
В качестве примечания, вы, вероятно, не хотите использовать аксессор запись стиле здесь и экспортировать его; который позволяет пользователям использовать синтаксис обновления записи, чтобы изменить код из-под вас, поэтому
c3 :: Container 3
c3 = mempty
c3' :: Container 3
c3' = c3{runContainer = []}
действителен. Вместо этого сделайте runContainer
отдельно стоящей функцией.
Мы можем убедиться, что мы получаем состав двух newtype
-представлением ограничений, глядя на Ядра (через -ddump-simpl
): внутри модуля, который определяет Container
(который я также называется Container
), выход (если мы удалим список экспорта)
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)
это немного трудно читать, но главное, чтобы увидеть это Container.NTCo:Container[0]
: NTCo
является newtype
принуждения между newtype
Container p
и этим тип представления.Sym
вращает это, и ;
составляет два ограничения.
Вызовите окончательное ограничение γₓ
; то весь типирование вывод является
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
γₓ = γ₃ ; γ₄′
Вот исходные файлы, которые я использовал:
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 выдает много ошибок из-за отсутствующих аннотаций «ЯЗЫК» в вашем исходном коде. Не могли бы вы добавить их? – Zeta
Вы проверили, что произойдет, если вы сделаете это в файле '.hs'? GHCi может быть странным относительно границ модуля. Я уверен, что «гиперлог» имеет собственную декларацию о роли. – dfeuer
Странно ... роль аннотации должна действительно предотвращать эти принуждения. – chi