Почему 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 и в комментариях.

Другие вопросы по тегам