Рассмотрим функтор выражения, определенный следующим GADT:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
import Control.Monad.Free
data ExprF :: * -> * where
Term :: Foo a -> (a -> r) -> ExprF r
instance Functor ExprF where
fmap f (Term d k) = Term d (f . k)
type Expr = Free ExprF
где Foo
определяется как
data Foo :: * -> * where
Bar :: Int -> Foo Int
Baz :: Double -> Foo Double
instance Show a => Show (Foo a) where
show (Bar j) = show j
show (Baz j) = show j
Комбинация поля (a -> r)
в ExprF
и (в противном случае желательно) ограничительных конструкторов GADT, похоже, делает невозможным создание красивого интерпретатора печати:
pretty (Pure r) = show r
pretty (Free (Term f k)) = "Term " ++ show f ++ pretty (k _)
Типовое отверстие - это то, что можно было бы ожидать:
Found hole ‘_’ with type: a1
Where: ‘a1’ is a rigid type variable bound by
a pattern with constructor
Term :: forall r a. Foo a -> (a -> r) -> ExprF r,
in an equation for ‘pretty’
at Test.hs:23:15
Relevant bindings include
k :: a1 -> Free ExprF a (bound at Test.hs:23:22)
f :: Foo a1 (bound at Test.hs:23:20)
pretty :: Free ExprF a -> String (bound at Test.hs:22:1)
In the first argument of ‘k’, namely ‘_’
In the first argument of ‘pretty’, namely ‘(k _)’
In the second argument of ‘(++)’, namely ‘pretty (k _)’
Кажется, нет способа присвоить продолжению значение того типа, который ему нужен. Этот тип закодирован в f
, и в других интерпретаторах я как-то использую весь дескриптор f
, чтобы извлечь значение соответствующего типа. Но путь к String
представлению кажется заблокированным.
Есть ли какая-то общая идиома, которую я здесь упускаю? Как можно красиво напечатать значение Expr
, если это действительно возможно? Если это невозможно, какая альтернативная конструкция ExprF
может фиксировать ту же структуру, но также поддерживать красивый принтер?