Выполнение того, что вы просили
Если вы посмотрите в моем пакете ограничений на взлом, есть
Data.Constraint.Forall
Этоможет использоваться для создания количественных ограничений, и используя inst с другими комбинаторами ограничений из пакета, и, создав вспомогательное ограничение для установки аргумента в правильное положение, вы можете напрямую кодировать то, что вы запрашиваете.
Описание механизма отражения находится в моем блоге.
http://comonad.com/reader/2011/what-constraints-entail-part-1/
http://comonad.com/reader/2011/what-constraints-entail-part-2/
Однако для этого требуется GHC.
Во многих случаях вы часто можете справиться с этим, сделав версию вашего конкретного ограничения ранга 2.
class Monoid1 m where
mappend1 :: m a -> m a -> m a
mempty1 :: m a
, но в вашем случае вы хотите не только ограничение ранга 2, но и имплицитное ограничение.
Используя оборудование из этого пакета, мы можем сделать
class SuitableLowering t m where
lowerSuitability :: Suitable (t m) a :- Suitable m a
Затем вы можете использовать
instance (PrintSuitable m, SuitableLowering t m) => PrintSuitable (t m) where
и expr \\ lowerSuitability
, чтобы вручную ввести в сферу действияSuitable m a
экземпляр в контексте whпрежде чем вы знаете Suitable (t m) a
.
Но это действительно опасный способ выражения экземпляра, потому что он исключает возможность создания чего-либо вроде (* -> *) -> * -> * экземпляраPrintSuitable любым другим способом и может помешать определению базового случая, если вы не будете осторожны!
Делайте то, что вам нужно
The правильный способ сделать это - отказаться от определения одного экземпляра, охватывающего все случаи, и вместо этого определить printSuitableDefault
, который можно использовать для любого подходящего преобразователя.
Предполагая существованиеRMonadTrans, как упомянуто в ответе Даниэля
class RMonadTrans t where
rlift :: Suitable m a => m a -> t m a
, мы можем определить:
printSuitableDefault :: (RMonadTrans t, Suitable m a) => a -> t ()
printSuitableDefault = ...
instance PrintSuitable m => PrintSuitable (Foo m) where
printSuitable = printSuitableDefault
instance PrintSuitable m => PrintSuitable (Bar m) where
printSuitable = printsuitableDefault
У вас вряд ли будет слишком много преобразователей rmonad, и это гарантирует, что если вы захотите сделать одинраспечатайте по-другому, у вас есть такая гибкость.
Делать то, что вам нужно, немного приятнее под компилятором передовой кромки
Под 7.3.x (текущий заголовок GHC) или позжеВы даже можете использовать новый деобъявления о сбоях, чтобы сделать это немного менее болезненным.
class RMonad m => PrintSuitable m where
printSuitable :: a -> m ()
default printSuitable :: (RMonadTrans t, RMonad n, Suitable n a, m ~ t n) =>
a -> t n ()
printSuitable = <the default lifted definition>
тогда экземпляры для каждого трансформатора могут просто выглядеть так:
instance PrintSuitable m => PrintSuitable (Foo m)
instance PrintSuitable m => PrintSuitable (Bar m)
, и вы можете определить свой хороший базовый вариант printSuitable для некоторыхЗаброшенная монада без забот о перекрытии.