Зависимости класса Haskell
Следующий код не компилируется:
class Foo f where
getInt :: f -> Int
class Bar b where
getFooFromBar :: Foo f => b -> f
someFunction :: Bar b => b -> Int
someFunction bar = getInt $ getFooFromBar bar
Ошибка Could not deduce (Foo f) arising from a use of 'getInt' from the context (Bar b)
Я знаю, что могу исправить ошибку, изменив класс на Bar
следующим образом:
class Foo f => Bar f b where
getFooFromBar :: b -> f
Но я бы предпочел, чтобы мне не нужно было добавлять f
ко всем сигнатурам экземпляра Bar
.
Есть ли способ сделать это, сохраняя ограничение Foo f
только на сигнатуре getFooFromBar
, а не на всем классе?1 ответ:
Но я бы предпочел, чтобы мне не нужно было добавлятьf
ко всем сигнатурам экземпляраBar
.Технически говоря, вам не нужно этого делать, чтобы ваш пример компилировался. Вы можете использовать аннотацию типа, чтобы указать, какой экземпляр
Foo
вы используете вsomeFunction
, решая неоднозначную ошибку переменной типа. Однако у вас есть более глубокая проблема:Это, по всем практическим соображениям, невозможно. Типclass Foo f where getInt :: f -> Int class Bar b where getFooFromBar :: Foo f => b -> f
getFooFromBar
говорит, что вы можете использовать его для получения результата любого типаf
, имеющего экземплярFoo
. Но как вы материализуете эту ценность для любогоf
? Нет смысла тянуться к какому-либо конкретному экземпляру при определенииgetFooFromBar
, так как все, что вы получите от этого, - это ошибка типаCouldn't match type ‘f’ with ‘Blah’
. Немедленное решение этого вопроса-то, которое вы предложили по другой причине в вопросе: указание экземпляраFoo
для использования через экземплярBar
. Возможно, Вам будет удобнее работать с семейством типов, а не с многопараметрическим типом класс:{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleContexts #-} class Foo f where getInt :: f -> Int class Foo (FooBar b) => Bar b where type FooBar b :: * getFooFromBar :: b -> FooBar b instance Foo Char where getInt = const 99 instance Bar Char where type FooBar Char = Char getFooFromBar = const 'a' someFunction :: Bar b => b -> Int someFunction = getInt . getFooFromBar
GHCi> someFunction 'z' 99