Использование видов ограничений и семейств типов с "ограниченными" ограничениями

Я работаю над аппликативным функтором, который содержит моноид для "просмотра" выполнения. Тем не менее, иногда я вообще не забочусь об этой части, поэтому выбор моноида не имеет значения, так как он никогда не будет потребляться. Я упростил то, что у меня есть:

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}

import GHC.Exts

class Render a b where render :: a -> b
instance Render a () where render = const ()

class Merge a where
  type Renderer a b :: Constraint
  merge :: Renderer a b => a -> b

data Foo = Foo Bool

instance Merge Foo where
  type (Renderer Foo) m = (Render Bool m)
  merge (Foo b) = render b

Render используется для преобразования различных aв один b, Merge это большое упрощение моего фактического функтора, но дело в том, что он содержит семейство типов / ограничение, и я намерен это точно указать, что Renderэрс Merge требует.

Теперь я мог бы "запустить" Merge, но откажитесь от представления, которое сродни чему-то вроде:

runFoo :: Merge a => a -> Int
runFoo x = case merge x of () -> 5

Но это не удастся, потому что:

Не мог вывести (Renderer a ()) вытекающие из использования merge

Я выбрал () как мой моноид, потому что aу нас есть пример Render a (), Так что, если бы был способ сказать, что Merge a просто означает коллекцию Render ограничения, то это будет работать нормально. Конечно, Merge a является более общим, чем это - это может добавить произвольные ограничения, что объясняет ошибку компиляции.

Есть ли способ добиться того, чего я хочу, не меняя подписи runFoo?

1 ответ

Решение

Это может не масштабироваться, если у вас много таких случаев, но это работает:

class Renderer a () => Merge a where
  ...
Другие вопросы по тегам