Зависимости класса 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 3

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