#haskell
#haskell
Вопрос:
Я работаю с классом типа, в котором я могу «измерить» некоторое свойство типа v
для объекта типа a
. Например, рассмотрим определения, приведенные ниже:
{-# LANGUAGE MultiParamTypeClasses #-}
data SizedElem a =
SizedElem
{ getSize :: Size
, getElem :: Elem a
}
newtype Size =
Size Integer
newtype Elem a =
Elem a
class Measured a v where
measureTypeClass :: a -> v
instance Measured (Elem a) (SizedElem a) where
measureTypeClass (Elem a) = SizedElem (Size 1) (Elem a)
Попытка объединить функции для измерения элемента и последующего извлечения размера следующим образом
broken :: Elem a -> Size
broken = getSize . measureTypeClass
приводит к жалобе от GHC:
Ambiguous type variable ‘a0’ arising from a use of ‘measureTypeClass’
prevents the constraint ‘(Measured
(Elem a) (SizedElem a0))’ from being solved.
...
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instance exist:
instance Measured (Elem a) (SizedElem a)
Наивно, я просто попытался аннотировать тип, который я ожидаю в середине:
broken' :: Elem a -> Size
broken' xs =
let meas = measureTypeClass xs :: SizedElem a
in getSize meas
Однако это приводит к несколько иной ошибке:
No instance for (Measured (Elem a) (SizedElem a2))
arising from a use of ‘measureTypeClass’
In the expression: measureTypeClass xs :: SizedElem a
In an equation for ‘meas’:
meas = measureTypeClass xs :: SizedElem a
Если я правильно понимаю, это означает, что a
интерпретируемый из аннотации :: SizedElem a
тип отличается от того, который указан в сигнатуре типа. Как я могу сообщить компилятору, что это должна быть переменная того же типа? Простое применение {-# LANGUAGE ScopedTypeVariables #-}
не решило проблему.
Кроме того, все работает нормально для определенного типа, например
specificWorks :: Elem Char -> Size
specificWorks xs =
let meas = measureTypeClass xs :: SizedElem Char
in getSize meas
или, если сигнатура типа имеет переменную одного и того же типа как для ввода, так и для вывода
elemWorks :: Elem a -> Elem a
elemWorks = getElem . measureTypeClass
Кроме того, также работает полный отказ от класса type, но для моего реального приложения мне нужен класс type по другим причинам:
measureDirect :: Elem a -> SizedElem a
measureDirect (Elem a) = SizedElem (Size 1) (Elem a)
directWorks :: Elem a -> Size
directWorks = getSize . measureDirect
Ответ №1:
Чтобы использовать расширение ScopedTypeVariables
, добавьте forall
квантификатор к сигнатуре функции:
getSize' :: forall a. Elem a -> Size
getSize' xs =
let meas = measureTypeClass xs :: SizedElem a
in getSize meas
Другой метод, который применим здесь, — это «трюк с ограничением». Эмпирическое правило состоит в том, чтобы никогда не иметь заголовка экземпляра (справа от =>
), где переменная встречается дважды, и вместо этого использовать ограничение равенства.
instance (a ~ b) => Measured (Elem a) (SizedElem b) where
measureTypeClass (Elem a) = SizedElem (Size 1) (Elem a)
Комментарии:
1. Это решило проблему! Спасибо, что также указали на трюк с ограничением. Я не знал об этом, но, похоже, это гораздо лучший способ справиться с проблемой такого типа в целом