如何为 GADT 实现棱镜?

How to implement prism for GADT?

我有这样的 GADT 数据类型:

data TValue = TInt | TBool

data Value (t :: TValue) where
    I :: Int  -> Value 'TInt
    B :: Bool -> Value 'TBool

而且我想在这种数据类型上使用棱镜进行模式匹配。为简单起见,我使用数据棱镜方法。所以以前我有这样的事情:

data ValuePrism (tag :: TValue) a = ValuePrism
    { matchValue :: forall t . Value t -> Maybe a
    , buildValue :: a -> Value tag
    }

boolPrism :: ValuePrism 'TBool Bool
boolPrism = ValuePrism matchBool B
  where
    matchBool :: Value t -> Maybe Bool
    matchBool (B b) = Just b
    matchBool _     = Nothing

我想将这个用例放入棱镜界面。我可以通过以下方式指定类型:

data Prism s t a b = Prism
    { preview :: s -> Either t a
    , review  :: b -> t
    }

type ValuePrism (tag :: TValue) a = 
    forall t . Prism (Value t) (Value tag) a a

但我无法根据旧接口编写创建 ValuePrism 的函数:

mkValuePrism :: (forall t . Value t -> Maybe a)
             -> (a -> Value tag)
             -> ValuePrism tag a
mkValuePrism = =<< (ツ) >>=

有什么方法可以为 GADT 创建棱镜吗?

您需要更改 ValuePrism 的第二个定义。

您的第一个 ValuePrism 'TBool Bool 不包含 Bool -> Value 'TInt 类型的函数,但您的第二个 ValuePrism 'TBool Bool 包含(通过专门化 forallt'TInt 并呼叫 review)。这样的函数可以从头开始编写,但它必然是我所说的 "unreasonable"。你可以定义

type ValuePrism tag a = Prism (Value tag) (Value tag) a a

相反;你会发现翻译起来容易多了。