Как избавиться от этой двусмысленности? - PullRequest
7 голосов
/ 07 декабря 2011

Я почти уверен, что об этом уже спрашивали, однако мне не удалось найти правильный ответ:

Я попытался устранить неоднозначность в следующем примерном фрагменте кода:

{-# LANGUAGE MultiParamTypeClasses #-}

class FooBar a b where
    foo :: a -> a
    foo = id
    bar :: a -> a
    bar = foo        -- ERROR AT THIS LINE

Я получаю сообщение об ошибке вроде:

Ambiguous type variable `b0' in the constraint:
      (FooBar a b0) arising from a use of `foo'
    Probable fix: add a type signature that fixes these type variable(s)
    In the expression: foo
    In an equation for `bar': bar = foo

, что понятно.Однако обратите внимание, что я на самом деле не могу последовать совету компилятора и исправить указанную переменную типа: foo не содержит переменную типа b в своей сигнатуре типа.Также это не работает:

    bar = (foo :: FooBar a b => a -> a) -- ERROR, the same error message

Даже с включенным -XScopedTypeVariables.

Как сообщить GHC, какой FooBar использовать?Это вообще возможно?

РЕДАКТИРОВАТЬ

После нескольких ответов: да, я слышал как о функциональных зависимостях, так и о связанных типах.

Что касаетсяфункциональные зависимости - я ищу способ явно сообщить GHC, какой экземпляр использовать (вместо того, чтобы просить его вывести правильный тип сам по себе).

Что касается обоих - в примере из реального мира, из которого он получен, мне нужно, чтобы оба параметра a и b были независимыми.

Опять же: я знаю этопример очень глупый, я знаю b не используется в теле класса type, что не имеет смысла.Это крайнее упрощение примера из реального мира, которое действительно имеет смысл.Реальный вариант использования включает в себя «заменяемость» типов a с использованием типов b и затем «унифицируемость» типов a и b с использованием типов c и, к сожалению, намного длиннее.

РЕДАКТИРОВАТЬ (2)

Хорошо, извините.Я думаю, что вы все-таки убедили меня, что я должен реорганизовать код, чтобы не иметь каких-либо плохо определенных функций (то есть тех, которые не содержат всех независимых типов в сигнатурах их типов).Разговор с тобой действительно помог мне подумать об этом.Оценил.

Ответы [ 2 ]

8 голосов
/ 07 декабря 2011

Ваш класс плохо определен, даже если вы полностью удалили bar из класса.Давайте просто предположим, что у вас есть следующий класс:

class FooBar a b
  where
    foo :: a -> a
    foo = id

Предположим, теперь вы хотели бы использовать foo вне определения этого класса.

e = .... foo x .....

GHC будет жаловаться с сообщением об ошибке, аналогичным вашему, что он не может найти тип для b.Просто потому, что ваш код не предоставляет такой тип.Ваше объявление класса типов говорит, что каждая пара типов a и b может получить экземпляр.Таким образом, даже когда a зафиксирован в вызове foo, все еще есть много возможных вариантов для выбора.

Функциональная зависимость a -> b решит эту проблему, потому что в ней говорится:тип a может быть только один экземпляр Foobar a b.Связанный тип устраняет проблему, не используя многопараметрические классы типов.

относительно EDIT OP:

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

6 голосов
/ 07 декабря 2011

Если вы введете функциональную зависимость, class FooBar a b | a -> b where, которая ее разрешит, но без этого (или связанных типов) я думаю, что невозможно разрешить использование экземпляра.

Да, один из способов, но довольно уродливо: введите фиктивный параметр типа b

class FooBar a b where
    foo' :: b -> a -> a
    foo' _ = id
    bar' :: b -> a -> a
    bar' b = foo b
...