Почему 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
Ответы
Ответ 1
Частично применяемые синонимы типа вообще не допускаются в 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)
Чтобы решить, равны ли два частично применяемых синонима типа, проверяющий тип должен будет решить, равны ли функции. Это сложная проблема, и в целом она неразрешима.
Ответ 2
Еще одна проблема с разрешением частично применяемых синонимов типов заключается в том, что они сделают вывод типа и выбор экземпляра практически невозможным. Например, предположим, что в контексте какой-то программы я хотел использовать 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
).
Ответ 3
Как мы знаем, 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 ab = 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
класса типов принимает тип, имеющий вид * → * → *
FuncSynonym
похож на частично примененную функцию:
FuncSynonym :: e -> a -> b -> (a -> e a b)
FuncSynonym e
как частично примененная функция:
(FuncSynonym e):: a -> b -> ( a -> e a b)
вид является *
,
b
вид *
,
a → eab
kind *
(FuncSynonym e)
вид это * → * → *
Example
класса типов принимает тип, который имеет вид * → * → *
, но почему все еще не работает?
Это другая причина в GHC выпуск 785 и комментарий