Подтвердить что ты не робот

Почему 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
4b9b3361

Ответ 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 и комментарий