Можно ли применить ограничение типа для экземпляра класса для типа с более высоким родом? - PullRequest
3 голосов
/ 18 апреля 2019

У меня есть тип, определенный следующим образом:

newtype PrimeSet a = P Integer
    deriving Eq

Я также определил функцию, которая преобразует простое множество в список, учитывая, что параметром его типа является Integral.

toList :: Integral a => PrimeSet a -> [a]

Я теперь, что дать PrimeSet экземпляр Foldable, так что это была моя первая попытка (после импорта fold из Data.Foldable):

instance Foldable PrimeSet where
    foldMap f = fold . map f . toList

Однако это не сработало, и компилятор сказал мне, что это Could not deduce (Integral a) arising from a use of ‘toList’. Насколько я понимаю, это сообщение гласит, что toList требует, чтобы его аргумент был типа Integral a => PrimeSet a, но это не обязательно так в случае Foldable.

В сообщении также говорилось, что возможным исправлением будет добавление Integral a в контекст сигнатуры типа для моей реализации foldMap, но, конечно, мне тогда сказали, что мне не разрешено предоставлять свой собственный тип Определения для методов класса, если я не использую InstanceSigs, поэтому я попробовал это, но это тоже не сработало.

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

instance (Integral a) => Foldable (PrimeSet a) where

(Это, конечно, не работает, потому что PrimeSet a имеет вид *, тогда как Foldable требует * -> *)

Ответы [ 2 ]

3 голосов
/ 18 апреля 2019

Нет, это невозможно.Весь смысл типов с более высоким родом - работать с любым типом параметра.В то время как PrimeSet на самом деле не является параметрическим, в основном это всегда PrimeSet Integer.Почему у вас вообще есть этот a параметр?

Однако существует другой класс для типов, которые являются «своего рода контейнерами», но не для произвольных типов: MonoTraversable или фактически MonoFoldable в этом случае.

{-# LANGUAGE FlexibleInstances, TypeFamilies #-}

import Data.MonoTraversable

type instance Element (PrimeSet a) = a
-- or, if `PrimeSet` is not parameterised,
-- type instance Element PrimeSet = Integer

instance (Integral a) => MonoFoldable (PrimeSet a) where
  otoList = YourImplementation.toList

Альтернативой может быть использование параметризованных типов, фактически функторов, но не в обычной Hask категории все типы Haskell, но только в подкатегории, тип которых s равен равно Integer.У меня есть такой класс в моем пакете ограниченных категорий .Но, особенно для этого типа, это действительно не имеет никакого смысла.

2 голосов
/ 18 апреля 2019

Вы можете использовать GADT, чтобы ограничить тип параметра:

{-# LANGUAGE GADTs #-}

data PrimeSet a where
    PrimeSet :: Integral a => Integer -> PrimeSet a

instance Foldable PrimeSet where
    foldr f b (PrimeSet x) = f (fromInteger x) b

Вы можете немного обобщить с помощью ConstraintKinds (и одноразового класса):

data Monomorphic f c a where
    Monomorphic :: c a => f -> Monomorphic f c a

instance (item ~ Item f, MonoFoldable f) => Foldable (Monomorphic f ((~) item)) where
    foldr f b (Monomorphic xs) = ofoldr f b xs

data PrimeSet = PrimeSet Integer

instance Foldable (Monomorphic PrimeSet Integral) where
    foldr f b (Monomorphic (PrimeSet i)) = f (fromInteger i) b
...