Объявление "подкласса" в Haskell
У меня проблемы со следующим простым кодом в Haskell:
import Prelude hiding (cycle).
class ICycle a where
cycle :: a -> a
instance ICycle [a] where
cycle [] = []
cycle (x:xs) = xs ++ [x]
instance ICycle Bool where
cycle True = False
cycle False = True
instance Num a => ICycle a where
cycle n = n+1
main = do
print $ cycle $ [1,2,3]
print $ cycle $ True
print $ cycle $ 42
Здесь первые два объявления экземпляра работают как положено, но третье вызывает различные виды ошибок в зависимости от комбинации флагов.
я знаю это Num a
не короче ICycle a
и, следовательно, компилятор не может завершить проверку типа. В примерах, как я видел, это можно обойти, либо сделав правую часть большим термином, либо объявив интересующий класс подклассом других классов. Здесь, наоборот, я хочу объявить существующий класс подклассом нового.
Интересно, есть ли возражения против такого рода использования классов типов? Или, если есть естественное решение.
2 ответа
Для этого конкретного примера, я думаю, вам лучше использовать newtype
обернуть экземпляр:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Prelude hiding (cycle)
class ICycle a where
cycle :: a -> a
newtype Succ a = Succ { runSucc :: a }
deriving (Num, Eq, Ord, Bounded, Enum, Show, Read)
newtype Pred a = Pred { runPred :: a }
deriving (Num, Eq, Ord, Bounded, Enum, Show, Read)
instance Enum a => ICycle (Succ a) where
cycle = Succ . succ . runSucc
instance Enum a => ICycle (Pred a) where
cycle = Pred . pred . runPred
main = do
print $ cycle $ (42 :: Succ Int)
print $ cycle $ (42 :: Pred Int)
Есть несколько способов, которыми можно циклически перемещаться по числам - по succ, по pred, путем удвоения, вдвое. Преимущество использования newtype
например (что делает RHS "больше", как вы отметили в своем вопросе), это то, что он позволяет нам иметь их ВСЕ.
Стандартная библиотека делает то же самое с Product
а также Sum
за Monoid
,
Глядя на это по-другому, если бы можно было определить новый суперкласс для Num
, добавив реализацию по умолчанию для всех экземпляров Num
тогда вы бы отняли этот выбор у всех этих реализаций. Возможно, таким образом, что это не имеет смысла.
Согласно отчету Haskell 2010, глава 4, Объявления и привязки, вещь, которую нужно определить как экземпляр, должна быть конструктором типов. Таким образом,
instance Num a => ICycle a where
...
неверно, потому что a
является переменной типа, а не конструктором типа.
Поэтому, допустимый способ сделать это, к сожалению, тип по типу. Надо сказать:
instance ICycle Int where
...
instance ICycle Double where
...
и так далее.