Вопрос может быть сведен к следующему: можем ли мы написать функцию, которая перемещает поля следующим образом?
suicidal :: f (forall n. n) -> forall n. f n
В конце концов, если мы сможем это сделать, то все остальное легко снесколько непредсказуемых типов:
hard' :: Maybe (f (forall n. n)) -> Maybe (forall n. f n)
hard' Nothing = Nothing
hard' (Just x) = Just (suicidal x)
hard :: (forall n. Maybe (f n)) -> Maybe (forall n. f n)
hard x = hard' x -- instantiate 'n' at type 'forall n. n', thank goodness for impredicativity!
(Если вы хотите попробовать это в GHC, обязательно определите новый тип, например
newtype Forall f = Forall { unForall :: forall n. f n }
, поскольку в противном случае GHC любит плавать forall
sвпереди стрелок и переверните.)
Но ответ на вопрос, можем ли мы написать suicidal
, ясен: нет, мы не можем!По крайней мере, не в параметрическом смысле над f
.Решение должно выглядеть примерно так:
suicidal x = /\ n. {- ... -}
... и тогда нам придется пройтись по «структуре» f
, найдя «места», где есть функции типа,и применить их к n
, который у нас теперь есть в наличии.Ответ на оригинальный вопрос о hard
оказывается таким же: мы можем написать hard
для любого конкретного f
, но не параметрически для всех f
.
Кстати, яЯ не уверен, что то, что вы сказали о параметричности, совершенно верно:
По параметризации для любого фиксированного f :: *->*
единственное общее число жителей (forall n . Maybe (f n))
примет одну из двух форм: Nothing
или Just z
, где z :: forall n . f n
.
На самом деле, я думаю, что вы получаете то, что жители (обсервационно эквивалентны) одной из двух форм:
/\ n. Nothing
/\ n. Just z
... где z
выше не полиморфный: он имеет особый тип f n
.(Примечание: нет скрытых forall
s.) То есть возможные условия последней формы зависят от f
!Вот почему мы не можем написать функцию параметрически в отношении f
.
edit: Кстати, если мы позволим себе экземпляр Functor
для f
, то, конечно, все будет проще.
notSuicidal :: (forall a b. (a -> b) -> (f a -> f b)) -> f (forall n. n) -> forall n. f n
notSuicidal fmap structure = /\ n. fmap (\v -> v [n]) structure
... но это обман, не в последнюю очередь потому, что я понятия не имею, как перевести это на Хаскелл.; -)