Почему GHC делает вывод об унификации из принуждения связанных данных и почему он противоречит своей собственной сигнатуре проверенного типа, чтобы сделать это?
Эта проблема
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeFamilies #-}
module Lib
(
) where
import Data.Coerce
class Foo a where
data Bar a
data Baz a = Baz
{ foo :: a
, bar :: Bar a
}
type BarSame a b = (Coercible (Bar a) (Bar b), Coercible (Bar b) (Bar a))
withBaz :: forall a b. BarSame a b => (a -> b) -> Baz a -> Baz b
withBaz f Baz{..} = Baz
{ foo = f foo
, bar = coerce bar
}
Это все хорошо - GHC с радостью скомпилирует этот код и уверен, что withBaz
имеет заявленную подпись.
А теперь попробуем им воспользоваться!
instance (Foo a) => Foo (Maybe a) where
data Bar (Maybe a) = MabyeBar (Bar a)
toMaybeBaz :: Baz a -> Baz (Maybe a)
toMaybeBaz = withBaz Just
Это дает ошибку, но очень странную:
withBaz Just
^^^^^^^^^^^^
cannot construct the infinite type: a ~ Maybe a
Действительно, если я войду в GHCi и попрошу дать мне тип withBaz
:
ghc>:t withBaz
withBaz :: (b -> b) -> Baz b -> Baz b
Это не моя подпись.
Принуждение
Я подозреваю, что GHC обрабатывает аргументы типа withBaz
, как будто они должны объединяться, потому что он выводит Coercible a b
из Coercible (Bar a) (Bar b)
. Но поскольку это семейство данных, они даже не должны быть Coercible
- уж точно не унифицируемыми.
Обновлять!
Следующее изменение исправляет компиляцию:
instance (Foo a) => Foo (Maybe a) where
newtype Bar (Maybe a) = MabyeBar (Bar a)
То есть объявить семейство данных как newtype
вместо data
. Это похоже на то, как GHC рассматривает Coercible
в языке в целом, в том смысле, что
data Id a = Id a
не вызовет генерацию экземпляра Coercible
, хотя он определенно должен быть принудительным к a
. С указанным выше объявлением это приведет к ошибке:
wrapId :: a -> Id a
wrapId = coerce
Но с объявлением newtype
:
newtype Id a = Id a
тогда экземпляр Coercible
существует, и wrapId
компилируется.
test :: forall a b. (Coercible (Bar a) (Bar b)) => Bar a -> Bar b
с реализациейtest = coerce
заканчивается типомtest :: Bar b -> Bar b
в GHCi. Тем не менее, можете ли вы привести пример использованияwithBaz
для любого конкретного типа? Например, дляtoMaybeBaz
, какой тип вы имеете в виду, который может быть приведен кMabyeBar (Bar a)
? - person DDub   schedule 24.12.2020MabyeBar (Bar a)
? -Bar a
, которыйBar (Maybe a)
- это оболочка. У них явно есть одно и то же представление в памяти, поэтому они должны быть принудительными. - person Isaac van Bakel   schedule 24.12.2020coerce
таким образом, и я обнаружил, что в нем есть объявлениеnewtype
для связанных данных семья, а неdata
. - person Isaac van Bakel   schedule 24.12.2020