MultiParamTypeClasses, FunctionalDependencies и вызов неоднозначных функций

С функциональными зависимостями я могу объявить класс Foo:

class Foo a b c | a -> b where
    foo1 :: a -> b -> c
    foo2 :: a -> c

и когда я звоню foo2, все работает нормально. Компилятор знает, какой экземпляр использовать из-за зависимости.

Но если я уберу зависимость для создания Foo':

class Foo' a b c where
    foo1' :: a -> b -> c
    foo2' :: a -> c

все по-прежнему компилируется нормально, но теперь всякий раз, когда я пытаюсь вызвать foo2', GHC выдает ошибку о невозможности решить, какой экземпляр использовать, потому что b неоднозначен.

Можно ли когда-нибудь вызвать foo2' без ошибок? Если да, то как? Если нет, то почему не выдает ошибку компиляции?


person Mike Izbicki    schedule 04.01.2013    source источник
comment
Ни один из них не должен компилироваться, типы foo1 и foo2 подразумевают разные типы для a. Вы имели в виду a -> b -> c и a -> c?   -  person Daniel Fischer    schedule 04.01.2013
comment
@DanielFischer Упс! Ты прав. Теперь это исправлено.   -  person Mike Izbicki    schedule 05.01.2013
comment
b недоступен из foo2', поэтому экземпляр можно было определить только в том случае, если существовал прямой способ указать экземпляр для использования. Это возможно с помощью некоторого ConstraintKinds волшебства, я этого не знаю, но сомневаюсь. Итак, нет, вы никогда не сможете звонить foo2'. Почему он не выдает ошибку компиляции, я точно не знаю, но думаю, компилятор не может доказать, что никогда нельзя однозначно вызвать foo2'.   -  person Daniel Fischer    schedule 05.01.2013


Ответы (1)


В этом контексте невозможно вызвать foo2', потому что, как говорит Даниэль Фишер, нет способа определить, какой экземпляр использовать. Например, если у вас было:

instance Foo' Int Int Int where
    foo2' x = x

instance Foo' Int Bool Int where
    foo2' x = x + 1

Оба этих foo2' имеют одинаковую сигнатуру типа, поэтому невозможно определить, какой из них вызывать.

Обычным способом решения этой проблемы является использование прокси:

data Proxy a = Proxy

class Foo'' a b c = where
    foo2'' :: Proxy b -> a -> c

Который вы используете так, чтобы выбрать экземпляр:

foo'' (Proxy :: Proxy Bool) 42
person luqui    schedule 05.01.2013
comment
Спасибо. Я просто очень хочу, чтобы ghc сказал, что Foo2' не может скомпилироваться, так как у него есть код, который никогда не запустится. Ну что ж. - person Mike Izbicki; 05.01.2013