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

2

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

<code>class C t where
  g :: t

instance C Int where
  g = 42
</code>

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

<code>f1 :: Int -> Int
f1 x = x * x
</code>

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

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

<code>type family X
type instance X = Int
</code>

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

<code>f2 :: X -> X
f2 x = x * x + 1
</code>

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

<code>instance C X where
  g = 43
</code>

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

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

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

<code>newtype NewX = NewX X

instance C NewX where
  g = 43
</code>

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

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

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

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

Background information:

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

<code>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
</code>

Это не с:

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)

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

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

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

@DanielWagner: Что ты имеешь в виду? Clinton
Есть ли причина, по которой вы не оцениваетеRow вы сами и пишете экземпляр для этого типа? Daniel Wagner
Похоже, проблема в синонимах обычного типа, а не в экземплярах типа.-XTypeSynonymInstances -XFlexibleInstances может быть то, что вам нужно. n.m.
@ N.m. Я добавил некоторую справочную информацию в свой вопрос. Clinton
Не ясно, что вы пытаетесь сделать. Синонимы типа не допускаются в заголовках экземпляров, иX это синонимInt (вид). Однако вместо этого вы можете использовать оригинальный тип (Int в этом случае). Возможно, более реальный пример прояснит картину. n.m.

Ваш Ответ

2   ответа
5

{-# 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
Знаете ли вы лучший способ решить мою проблему, учитывая справочную информацию, которую я добавил к вопросу? Clinton
Спасибо! Я полагаю нетderiving EVERYTHING вещь стиля (способ сделатьNewX в основном копияX)? Clinton
@ Клинтон Нет, нет.
3

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

Причина, по которой синонимы типов не очень хорошо сочетаются с объявлениями экземпляров, заключается в том, что функции (включая функции типов) работают только в одном направлении. Вы можете сопоставить шаблон только с конструкторами, поэтому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 ]) ]
Я добавил еще одно изменение, которое, кажется, делает свое дело, с минимальным дополнительным кодом. Я думаю, что это ответ (это было намного проще, чем я думал!) Clinton
Это похоже на работу, спасибо! Хотя я оставлю вопрос открытым, если есть ответ, который не требует косвенного обращения к полю.runRow (даже если он имеет нулевую стоимость выполнения). Clinton

Похожие вопросы