Ограничение класса типа другого вида

Я возился с классами общего типа для списков в Haskell.

class HasEmpty a where
  empty :: a
  isEmpty :: a -> Bool

class HasEmpty (l a) => List l where
  cons :: a -> l a -> l a
  uncons :: l a -> (a, l a)

Чтобы дать вам представление о том, как это может работать, вот примеры для []:

instance HasEmpty [a] where
  empty = []
  isEmpty [] = True
  isEmpty _  = False

instance List [] where
  cons = (:)
  uncons (x:xs) = (x,xs)

Однако это вызывает ошибку:

Not in scope: type variable 'a'

Это вызвано ограничением HasEmpty (l a). Меня не особо интересует этот конкретный пример, но меня интересует концепция в целом. HasEmpty — это класс для типов вида *, а List — это класс для типов вида * -> *. Могу ли я сделать ограничение класса типов другого типа, чем класс типов, который он ограничивает?


person Dan Burton    schedule 19.10.2011    source источник


Ответы (2)


В любом случае вы всегда можете выразить базовую логику либо с помощью многопараметрических классов типов (как на самом деле это делается в ListLike):

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}

class HasEmpty a where
  empty :: a
  isEmpty :: a -> Bool

class HasEmpty (l a) => List l a where
  cons :: a -> l a -> l a
  uncons :: l a -> (a, l a)


instance HasEmpty [a] where
  empty = []
  isEmpty [] = True
  isEmpty _  = False

instance List [] a where
  cons = (:)
  uncons (x:xs) = (x,xs)

Или более элегантно через семейства типов:

{-# LANGUAGE TypeFamilies #-}

class HasEmpty a where
  empty :: a
  isEmpty :: a -> Bool


class HasEmpty a => List a where
  type Elem a :: *
  cons :: Elem a -> a -> a
  uncons :: a -> (Elem a, a)


instance HasEmpty [a] where
  empty = []
  isEmpty [] = True
  isEmpty _  = False


instance List [a] where
  type Elem [a] = a
  cons = (:)
  uncons (x:xs) = (x,xs)
person Ed'ka    schedule 19.10.2011

Конечно вы можете. Например. это будет отлично работать для тех же двух классов:

class HasEmpty (l ()) => List l where
  cons :: a -> l a -> l a
  uncons :: l a -> (a, l a)

или (где List1 :: (* -> *) -> * -> *)

class HasEmpty1 (l a) => List1 l a where
  cons :: a -> l a -> l a
  uncons :: l a -> (a, l a)

Чего вы не можете сделать, так это добавить новые переменные в ограничения.

person Alexey Romanov    schedule 19.10.2011
comment
Что, вероятно, мешает вам делать то, что вы хотите. знание HasEmpty (l ()) не так уж полезно, когда вы говорите об общих l a - person luqui; 19.10.2011
comment
Да, на самом деле он хочет class (forall a. HasEmpty (l a)) => ..., что не разрешено (и я не думаю, что его можно разумно добавить). Но второй вариант может быть более рабочим. - person Alexey Romanov; 19.10.2011
comment
Возможно, вы захотите добавить туда фондеп l -> a. Не уверен. - person Thomas Eding; 20.10.2011