Как здесь избежать явных подписей типов?

class Listy a b where
   fromList :: [b] -> a
   toList :: a -> [b]
   lifted :: ([b] -> [b]) -> (a -> a) 
   lifted f = fromList . f . toList

data MyString = MyString { getString :: String } deriving Show

instance Listy MyString Char where
  toList = getString
  fromList = MyString

Теперь мне нужно написать, например. lifted (reverse::(String -> String)) (MyString "Foobar"). Есть ли способ избежать необходимости подписи типов?


person Landei    schedule 17.10.2012    source источник


Ответы (2)


По сути, проблема в том, что установка типа a не сообщает компилятору, какой тип b. Вы можете думать, что, поскольку существует только один экземпляр класса (где a – это MyString, а b – Char), любой может добавить новые экземпляры в любое время. Таким образом, тот факт, что существует только один экземпляр now, не помогает компилятору решить, какие типы вам нужны.

Решением этого является использование функциональных зависимостей или семейств типов. Последнее является более новым решением и призвано в конечном итоге «заменить» первое, но в настоящее время оба они по-прежнему полностью поддерживаются. Уйдут ли FD когда-нибудь, еще неизвестно. Во всяком случае, с FD:

class Listy a b | a -> b where ...

По сути, это говорит о том, что «для каждого a может быть только один экземпляр класса». Другими словами, зная a, вы всегда можете определить b. (Но не наоборот.) Остальная часть класса выглядит так же, как и раньше.

Альтернативой являются ТФ:

class Listy a where
  type Element a :: *
  ...

instance Listy MyString where
  type Element MyString = Char
  ...

Теперь вместо того, чтобы называть второй тип b, он называется Element a. Слово Element действует как метод класса, который принимает тип списка и возвращает соответствующий тип элемента. Затем вы можете сделать

instance Listy ByteString where
  type Element ByteString = Word8
  ...

instance Listy [x] where
  type Element [x] = x
  ...

instance Ord x => Listy (Set x) where
  type Element (Set x) = x
  ...

и так далее. (Не то чтобы Listy обязательно имело смысл для всех вышеперечисленных типов; это просто примеры того, как определить класс.)

person MathematicalOrchid    schedule 17.10.2012

Вы можете попробовать -XFunctionalDependencies

class Listy a b | a -> b where
   fromList :: [b] -> a
   toList :: a -> [b]
   lifted :: ([b] -> [b]) -> (a -> a) 
   lifted f = fromList . f . toList
person Yuras    schedule 17.10.2012