Haskell: определения экземпляров для семейств типов

Допустим, у нас есть следующий код:

class C t where
  g :: t

instance C Int where
  g = 42

Просто. Мы также можем определить функции на Int, например, так:

f1 :: Int -> Int
f1 x = x * x

Я работал с семействами типов, в частности потому, чтоData.Has использует их, и я хочу вставить их вIxSet.

Но здесь я собираюсь представить упрощенный пример. Допустим, мы хотим определить новый типX, что похоже на Int. Мы могли бы сделать это:

type family X
type instance X = Int

Затем мы можем определить функции наX вот так:

f2 :: X -> X
f2 x = x * x + 1

Пока проблем нет. Теперь давайте попробуем определить экземплярC Xкак мы сделали дляC Int:

instance C X where
  g = 43

Ой, теперь у нас есть следующая ошибка:

Illegal type synonym family application in instance: X
In the instance declaration for 'C X'

Теперь давайте попробуем что-то немного другое:

newtype NewX = NewX X

instance C NewX where
  g = 43

Теперь у нас есть другая ошибка, а именно:

No instance for (Num NewX)
arising from the literal '43'

Кажется, чтоnewtype Ключевое слово исключает любую информацию о том, к каким классам принадлежал предыдущий класс. Однако, кажется, что я не могу избежатьnewtype, поскольку я не могу использовать семейства типов в определениях экземпляров.

Есть ли лучший способ сделать это без необходимости переписывать определения экземпляров с дополнительными явными упоминаниями экземпляров, которые в противном случае были бы выведены?

Background information:

Причина, по которой мне нужно это работает, заключается в следующем:

import Data.Has
import Data.IxSet

data Col1 = Col1; type instance TypeOf Col1 = Text
data Col2 = Col2; type instance TypeOf Col2 = Text

type Row = FieldOf Col1 :&: FieldOf Col2;

instance Indexable Row where
  empty = ixSet [ixFun $ (\x -> [ Col1 ^. x ]) ] -- Maybe add some more indexes later

Это не с:

Illegal type synonym family application in instance: Row
In the instance declaration for 'Indexable Row'

ИзготовлениеRow newtype вызывает следующую ошибку:

No instance for (Contains (Labelled Col1 Text) Row) arising from a use of `^.' Possible fix: add an instance declaration for (Contains (Labelled Col1 Text) Row)

Единственный способ, которым я могу обойти это, - это добавить длинное производное предложение следующим образом:

newtype Row = Row (FieldOf Col1 :&: FieldOf Col2)
  deriving 
  (
    Contains (Labelled Col1 Text), -- Add this for every column
    Contains (Labelled Col2 Text)  -- ...
  )

Даже то, что позволяет мне "typedef"Contains (Labelled x (TypeOf x)) сказатьHasCol x было бы полезно.

Ответы на вопрос(2)

Решение Вопроса

newtype делает это - он определяет новый тип, в то время какtype определяет синоним. Если вам не нравится набор производных предложений, всегда можно использовать изоморфизм с базовым типом

instance C NewX where
   g = NewX  43

Причина, по которой синонимы типов не очень хорошо сочетаются с объявлениями экземпляров, заключается в том, что функции (включая функции типов) работают только в одном направлении. Вы можете сопоставить шаблон только с конструкторами, поэтомуnewtype позволяет вводить конструктор нового типа при нулевых затратах времени выполнения. В твоей проблеме почему нет

newtype Row = Row {runRow :: FieldOf Col1 :&: FieldOf Col2}

instance Indexable Row where
  empty = ixSet [ixFun $ (\x -> [ Col1 ^. (runRow x) ]) ]

Должен отметить, что в целомGeneralizedNewtypeDeriving несостоятельно. Это не означает, что вы должны избегать его использования, но подразумевает, что то, что вы хотите, вероятно, невозможно.


Edit (Question asker):

А еще лучше, не нужно даже менять тип данных Row

newtype Row = Row ( FieldOf Col1 :&: FieldOf Col2 )

instance Indexable Row where
  empty = ixSet [ixFun $ (\(Row x) -> [ Col1 ^. x ]) ]

Следующий файл компилируется здесь:

{-# LANGUAGE GeneralizedNewtypeDeriving, TypeFamilies #-}

class C a where g :: a
type family X
type instance X = Int
newtype NewX = NewX X deriving Num
instance C NewX where g = 43

Ваш ответ на вопрос