Получение функций в качестве аргумента с ограниченными экзистенциальными объектами в Haskell

Я играл с некоторыми расширениями GHC, чтобы определить функцию, которая может делать следующее:

let a = A :: A  -- Show A
    b = B :: B  -- Show B
  in
    myFunc show a b -- This should return (String, String)

myFunc должен быть полностью полиморфным в сигнатуре show, чтобы он мог принимать a и b с разными типами, удовлетворяющими Show.

Вот моя попытка с расширениями GHC RankNTypes, ConstraintKinds, KindSignatures:

myFunc :: forall (k :: * -> Constraint) a b d. (k a, k b) 
            => (forall c. k c => c -> d) -> a -> b -> (d, d)

Моя главная цель — понять, как работают эти расширения; но на мой взгляд, кажется, что я говорю GHC, что есть ограничение k, которому удовлетворяют некоторые a и b, а также есть функция (forall c. k c => c -> d), которая может принимать любой тип c, удовлетворяющий k, и возвращать конкретное d, теперь, при этих условиях , я хочу применить функцию к a и b, чтобы получить кортеж (d,d)

Вот как GHC жалуется:

Could not deduce (k0 a, k0 b)
from the context (k a, k b)
  bound by the type signature for
             myFunc :: (k a, k b) =>
                       (forall c. k c => c -> d) -> a -> b -> (d, d)
  at app/Main.hs:(15,11)-(16,56)
In the ambiguity check for the type signature for ‘myFunc’:
  myFunc :: forall (k :: * -> Constraint) a b d.
            (k a, k b) =>
            (forall c. k c => c -> d) -> a -> b -> (d, d)
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
In the type signature for ‘myFunc’:
  myFunc :: forall (k :: * -> Constraint) a b d. (k a, k b) =>
            (forall c. k c => c -> d) -> a -> b -> (d, d)

...

Could not deduce (k c)
from the context (k a, k b)
  bound by the type signature for
             myFunc :: (k a, k b) =>
                       (forall c. k c => c -> d) -> a -> b -> (d, d)
  at app/Main.hs:(15,11)-(16,56)
or from (k0 c)
  bound by the type signature for myFunc :: k0 c => c -> d
  at app/Main.hs:(15,11)-(16,56)
In the ambiguity check for the type signature for ‘myFunc’:
  myFunc :: forall (k :: * -> Constraint) a b d.
            (k a, k b) =>
            (forall c. k c => c -> d) -> a -> b -> (d, d)
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
In the type signature for ‘myFunc’:
  myFunc :: forall (k :: * -> Constraint) a b d. (k a, k b) =>
            (forall c. k c => c -> d) -> a -> b -> (d, d)
app/Main.hs15:40

Что мне не хватает?


person enobayram    schedule 22.09.2016    source источник
comment
Я предполагаю, что вы имеете в виду полиморфность в ограничениях, которые может иметь аргумент функции, потому что в противном случае вы могли бы просто написать (Show a, Show b) => (forall x. Show x => x -> String) -> a -> b -> (String,String)? Для этого требуется только RankNTypes.   -  person Cubic    schedule 22.09.2016
comment
Это правда, я не хочу упоминать Show.   -  person enobayram    schedule 22.09.2016


Ответы (1)


Проблема в том, что просто передать функцию (forall c . k c => c -> d) в качестве аргумента недостаточно, чтобы средство проверки типов однозначно определило, что такое k на самом деле. Явная передача ограничения работает, и вам даже не нужны внешние forall или явные типы:

import Data.Proxy

myFunc :: (k a, k b) => Proxy k -> (forall c. k c => c -> d) -> a -> b -> (d, d)
myFunc _ f a b = (f a, f b)

а потом

let (c, d) = myFunc (Proxy :: Proxy Show) show a b
person shang    schedule 22.09.2016
comment
Понятно, большое спасибо; это также объясняет, что GHC подразумевает под To defer the ambiguity check to use sites, enable AllowAmbiguousTypes, потому что я обычно ожидаю, что проверка произойдет на сайте вызова. Конечно, если подумать об этом сейчас, система типов Haskell всегда пытается отловить проблемы локально, в отличие, скажем, от шаблонов C++, которые откладывают почти все до создания экземпляра. - person enobayram; 22.09.2016
comment
В GHC8 использование -XAllowAmbiguousTypes и -XTypeApplications предлагает другое решение; аргумент Proxy исчезает, и тогда у вас есть myFunc f a b = (f a, f b) и myFunc @Show show a b в месте вызова. - person Antal Spector-Zabusky; 22.09.2016