Я играю с монадой в стиле ConduitT, которая использует экзистенциальную квантификацию, и я играю в прятки, пытаясь объединить типы. Существует две версии этого сценария:
Здесь Await i
имеет количественно экзистенциально a
, что позволяет методу await
передавать его в любой тип i -> Await i -> a
, какой он пожелает.
{-# LANGUAGE RankNTypes #-}
newtype Piped r a = Piped { unPiped :: forall b. r -> (r -> a -> b) -> b }
instance Functor (Piped r) where
fmap f (Piped c) = Piped $ \r rest -> c r (\r -> rest r . f)
runPiped :: Piped r a -> r -> a
runPiped (Piped p) r = p r $ \_ -> id
newtype Await i = Await { unAwait :: forall a. Yield i a -> a }
newtype Yield i a = Yield { unYield :: i -> Await i -> a }
runAwait :: Await i -> (i -> Await i -> a) -> a
runAwait (Await await) = await . Yield
runYield :: Yield i a -> i -> (Yield i a -> a) -> a
runYield (Yield yield) i = yield i . Await
-- broke ^^^^^
-- because Await swallows the type of `a`
await :: forall i a y. Piped (Await i, y) i
await = Piped $
\(a, y) f -> runAwait a $ \i a' -> f (a', y) i
Сбой с:
• Couldn't match type ‘Yield i a -> a’
with ‘forall a1. Yield i a1 -> a1’
Expected type: (Yield i a -> a) -> Await i
Actual type: (forall a. Yield i a -> a) -> Await i
• In the second argument of ‘(.)’, namely ‘Await’
Метод runYield
не работает, поскольку он не может объединить экзистенциально определенный параметр типа в Await i
с a
.
Второй сценарий:
Для исправления runYield
, Await
теперь указывает a
, который объединяет Await i a
с Yield i a
. Однако теперь, когда указано a
, yield
не может передать ему Yield i b
с любым значением b
, которое ему нравится:
newtype Piped r a = Piped { unPiped :: forall b. r -> (r -> a -> b) -> b }
newtype Await i a = Await { unAwait :: Yield i a -> a }
newtype Yield i a = Yield { unYield :: i -> Await i a -> a }
runAwait :: Await i a -> (i -> Await i a -> a) -> a
runAwait (Await await) = await . Yield
runYield :: Yield i a -> i -> (Yield i a -> a) -> a
runYield (Yield yield) i = yield i . Await
await :: Piped (Await i a, y) i
await = Piped $
\(a, y) f -> runAwait a $ \i a' -> f (a', y) i
-- ^^^^^^
Сбой с:
• Couldn't match type ‘b’ with ‘a’
‘b’ is a rigid type variable bound by
a type expected by the context:
forall b. (Await i a, y) -> ((Await i a, y) -> i -> b) -> b
Expected type: (Await i a, y)
Actual type: (Await i b, y)
Так что, мне кажется, мне это нужно в обоих направлениях, иногда количественно, а иногда - конкретно. Я пытался создать оболочки, чтобы скрыть дополнительный параметр типа, переключив newtype
на data
, и мне также пришло в голову, что если бы я мог определить функцию Await i a -> Await i b
, которая также могла бы решить проблему, но я немного из моей глубины здесь. Есть идеи?