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

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

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 требует * -> *)


person Zac    schedule 18.04.2019    source источник


Ответы (2)


Нет, это невозможно. Весь смысл типов более высокого типа заключается в том, чтобы работать над любым типом параметра. Принимая во внимание, что 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 are равно Integer. У меня есть такой класс в моем ограниченном -категории пакет. Но, особенно для вашего типа, это действительно не имеет никакого смысла.

person leftaroundabout    schedule 18.04.2019
comment
Ну, причина, по которой я ввел параметр типа, изначально заключалась в том, чтобы я мог сделать его экземпляром Foldable. Но он используется в другом месте (например, в другой функции: toList :: Integral a => PrimeSet a -> [a]), хотя на самом деле это не нужно, потому что он будет работать так же хорошо с непараметризованным типом PrimeSet. Причина, по которой тип объявлен как P Integer, заключается не в том, что сам набор хранит целое число, а в том, что значение Integer на самом деле кодирует набор из любого количества значений Integral, если это имеет смысл. - person Zac; 18.04.2019
comment
Ok. Что ж, тогда откажитесь от параметра a и сделайте его просто instance MonoFoldable PrimeSet. - person leftaroundabout; 18.04.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 (и класс monofoldable):

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
person oisdk    schedule 18.04.2019