Объявление "подкласса" в 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
    ...

и так далее.

Другие вопросы по тегам