Как определить экземпляр Monad "m a" с "a" в Typeclass Show? - PullRequest
6 голосов
/ 20 июля 2011

Я хотел бы определить экземпляр монады с контейнером M как монадой и с содержащимся типом a, который должен быть членом класса Show.Это ограничение (a является членом Show) должно обеспечиваться системой типов.

Я попробовал вот так, но M, к сожалению, не подходит.

data M = forall a. Show a => M a 

instance Monad M where
 return x = M x

Все другие попытки добиться этого наталкиваются на следующую проблему: Так как Monad - это класс конструктора, у меня нет явного доступа к типу a содержащихся элементов, поэтому я не могу его ограничить.

Кто-нибудь знает решение этой проблемы без определения нового класса Monad?

Ответы [ 4 ]

7 голосов
/ 20 июля 2011

Что ж, на самом деле можно в некотором смысле ограничить параметры конструктора типов, используя GADT:

data M a where
    M :: (Show a) => a -> M a

К сожалению, на самом деле это вам здесь не поможет.В некотором смысле это на самом деле ухудшает ситуацию, потому что вместо того, чтобы иметь экземпляр Monad без ограничения, становится невозможным написать экземпляр вообще.

Если вы посмотрите на указанную выше сигнатуру типа конструктора, она явнонапоминает return - что показывает, почему то, что вы делаете, принципиально невозможно.Тип для возврата: (Monad m) => a -> m a, и, как всегда, переменные несвязанного типа неявно универсально определены количественно на самом внешнем уровне, так что вы можете прочитать это как "для всех возможных типов a и всех возможных типов m, которые являются экземплярамиMonad, учитывая значение типа a, вы можете создать значение типа m a ".Фраза "для всех" довольно буквальна - тип возврата не просто использует переменную типа, он активно утверждает, что любой тип a должен быть разрешен.

Итак, вкратце,нет.Нет способа сделать то, что вы хотите, потому что стандартный Monad класс явно указывает противоположное.

5 голосов
/ 20 июля 2011

Возможно, вы не сможете сделать именно то, что просите, но другая возможность для вашей конкретной монады - предоставить действие, которое явно делает то, что вы думаете делать с Show. То есть, если у вас есть:

data M a = {- ... -}
instance Monad M where -- notice: no Show constraint
    {- ... -}

Тогда вы могли бы дополнительно предоставить какое-то действие:

report :: Show a => M a -> M a

Я не могу придумать, как правильно использовать этот шаблон с Show, но я знаю похожий пример, в котором вы могли бы захотеть ограничение Ord. Настройка заключается в том, что вы хотите сделать монаду недетерминированной (например, [a]), но без дубликатов (например, Set a). Для удаления дубликатов требуется некоторый контекст, такой как Eq или Ord, но мы не можем требовать этого при каждой операции return / >>=. Поэтому вместо этого мы требуем, чтобы пользователь явно пометил точки, где дубликаты должны быть объединены:

newtype Setlike a = Setlike { toList :: [a] }
instance Monad Setlike where
    return x = Setlike [x]
    Setlike xs >>= f = [y | x <- xs, let Setlike ys = f x, y <- ys]

collapse :: Ord a => Setlike a -> Setlike a
collapse = Setlike . Data.Set.toList . Data.Set.fromList . toList

Это можно использовать так:

valuesOfInterest = collapse $ do
    v1 <- allValues
    v2 <- allValues
    doSomethingInteresting v1 v2

Тогда, даже если какое-либо спаривание v1 и v2 приведет к одинаковому значению интереса, это значение появится в результате только один раз.

Возможно, для вашего случая использования возможен аналогичный трюк.

2 голосов
/ 20 июля 2011

Нет. Это невозможно, хотя это легко объяснить. Посмотрите на тип подписи return:

return :: Monad m => a -> m a

Подпись не может быть изменена, поэтому ваша монада должна принимать любые типы контента. Поскольку сам класс типа не упоминает тип содержимого, нет никакого способа навязать ему ограничения.

Одна вещь, которую вы могли бы сделать, это написать это ограничение для всех функций, которые в нем нуждаются, и отбросить глобальное ограничение. Надежность системы по-прежнему гарантируется, если вы можете доказать существование ограничения Show только тогда, когда оно необходимо.

1 голос
/ 20 июля 2011

Это возможно с некоторыми экстремальными хитростями.Смотрите реализацию rmonad .Однако, это, вероятно, не стоит.

Зачем вам нужно ограничение, которое a способен Show?Было бы проще просто применить ограничение Show в этот момент, тогда оно будет естественно распространяться там, где это необходимо.

...