Почему TypeSynonymInstances не позволяет использовать частично примененные синонимы типов в заголовках экземпляров?
Я знаю, что TypeSynomymInstances позволяет использовать только полностью примененные синонимы типов в заголовках экземпляров, но кажется, что было бы удобно, если бы я мог также использовать и синонимы с примененным партией типов.
Например:
class Example e where
thingy :: a -> b -> e a b
-- legit, but awkward
newtype FuncWrapper e a b = FuncWrapper { ap :: a -> e a b }
instance (Example e) => Example (FuncWrapper e) where
thingy _ = FuncWrapper . flip thingy
funcWrapperUse :: (Example e) => e Int String
funcWrapperUse = thingy 1 "two" `ap` 3 `ap` 4 `ap` 5
-- not legal, but a little easier to use
type FuncSynonym e a b = a -> e a b
instance (Example e) => Example (FuncSynonym e) where
thingy _ = flip thingy
funcSynonymUse :: (Example e) => e Int String
funcSynonymUse = thingy 1 "two" 3 4 5
2 ответа
Частично примененные синонимы типов вообще не допускаются в Haskell. Частично примененный синоним - это, по сути, функция, чьи входные данные являются неприменяемыми типами, а выходные данные - типом. Например, вот кодировка логической логики:
type True x y = x
type False x y = y
type Not b x y = b y x
type And b1 b2 x y = b1 (b2 x y) y
type Or b1 b2 x y = b1 x (b2 x y)
Чтобы решить, равны ли два частично примененных синонима типа, средство проверки типов должно будет решить, равны ли функции. Это сложная проблема, и в целом она неразрешима.
Другая проблема, связанная с разрешением частично примененных синонимов типов, заключается в том, что они делают вывод типов и выбор экземпляров практически невозможными. Например, предположим, в контексте какой-то программы, которую я хотел использовать thingy
по типу Int -> String -> Int -> (Int, String)
, thingy
имеет тип forall a b e. a -> b -> e a b
так что мы можем объединить a
с Int
а также b
с String
, но если e
разрешено быть частично применяемым синонимом типа, мы могли бы иметь
e = FuncSynonym (,)
или же
e = FuncSynonym' Int (,) where type FuncSynonym' x f a b = x -> f a b
или даже
e = Const2 (Int -> (Int, String)) where Const2 a x y = a
Проблема вывода типов станет еще хуже, чем решение равенства функций; это потребовало бы рассмотрения всех функций с указанным выводом на конкретном входе или аналогичных более сложных проблем (представьте себе, просто пытаясь объединить a b
с Int
).
Как известно Maybe
добрый *->*
.
Так что это может быть экземпляр Functor
instance Functor Maybe where
fmap :: f -> Maybe a -> Maybe b
fmap f Nothing = Nothing
fmap f (Maybe x) = Maybe (f x)
Первый пример:
{-# LANGUAGE TypeSynonymInstances #-}
type MaybeAlias a = Maybe
instance {-# OVERLAPPING #-} Functor (MaybeAlias Int) where
fmap f functor = undefined
Под действием TypeSynonymInstances
расширение (почти как замена String), оно равно
instance {-# OVERLAPPING #-} Functor Maybe where
fmap f functor = undefined
Это нормально, потому что allow fully applied type synonyms to be used in instance heads
См. Другой пример:
{-# LANGUAGE TypeSynonymInstances #-}
type MaybeAlias a b = Maybe
Что за MaybeAlias Int
сейчас? Это мило*->*->*
.
Почему?
Как комментарий @heatsink выше:
Частично примененный синоним - это фактически функция, входными данными которой являются непримененные типы, а выходными данными - тип.
Объясни это сейчас:
Под определением type MaybeAlias a b = Maybe
:
MaybeAlias
как частично примененная функция:
(MaybeAlias) :: a -> b -> Maybe
MaybeAlias Int
как частично примененная функция:
(MaybeAlias Int) :: b -> Maybe
В Maybe
добрый * -> *
, b
добрый *
.
Так MaybeAlias Int
добрый * -> (* -> *)
.
А также * -> (* -> *)
равно * -> * -> *
.
Основная причина, по которой приведенный ниже код не работает, потому что Functor
класс типов принимает только те типы, которые имеют вид * -> *
не * -> * ->*
!
{-# LANGUAGE TypeSynonymInstances #-}
type MaybeAlias a b = Maybe
instance {-# OVERLAPPING #-} Functor (MaybeAlias Int) where
fmap f functor = undefined
Почему приведенный ниже код не работает?
class Example e where
thingy :: a -> b -> e a b
-- legit, but awkward
newtype FuncWrapper e a b = FuncWrapper { ap :: a -> e a b }
instance (Example e) => Example (FuncWrapper e) where
thingy _ = FuncWrapper . flip thingy
funcWrapperUse :: (Example e) => e Int String
funcWrapperUse = thingy 1 "two" `ap` 3 `ap` 4 `ap` 5
-- not legal, but a little easier to use
type FuncSynonym e a b = a -> e a b
instance (Example e) => Example (FuncSynonym e) where
thingy _ = flip thingy
funcSynonymUse :: (Example e) => e Int String
funcSynonymUse = thingy 1 "two" 3 4 5
Example
typeclass принимает тип, имеющий вид * -> * -> *
FuncSynonym
как частично примененная функция:
FuncSynonym :: e -> a -> b -> (a -> e a b)
FuncSynonym e
как частично примененная функция:
(FuncSynonym e):: a -> b -> ( a -> e a b)
a
добрый *
,
b
добрый *
,
a -> e a b
добрый *
(FuncSynonym e)
добрый * -> * -> *
Example
typeclass принимает тип, имеющий вид * -> * -> *
а почему до сих пор не работает?
Это другая причина в выпуске 785 ghc и в комментариях.