如果我没有注意到目前最好的解决方案仍然是singletons
包,我会失职,它提供了demote
做你想做的事情的功能(见最后的例子)。但是,该实现在内部使用 Template Haskell 来提升现有Prelude
类型,并要求您显式使用 TH 来提升其他类型。而且,你说你不想那样做...
所以,我想在技术上使用泛型是可行的。您可以使用Data.Typeable
or来破坏kind的Type.Reflection
类型级别,然后使用(或或其他)构造一个类型的术语。t
k
t
k
GHC.Generics
Data.Data
Data.Typeable
考虑以下用于类型破坏和Data.Data
术语构造的概念证明:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module Reify where
import Data.Typeable
import Data.Data
demote :: forall k t. (Typeable t, Data k) => Proxy (t :: k) -> k
demote pxy
= let rep = typeRep pxy
in fromConstr $ mkConstr (dataTypeOf @k undefined)
(removeTick $ tyConName . typeRepTyCon $ rep)
[]
Prefix
where removeTick ('\'':xs) = xs
这只是部分实现,但它会降级任意空值前缀构造函数:
data MyType = A | B Int deriving (Show, Data)
main = do
print $ (demote (Proxy @'Nothing) :: Maybe Int)
print $ (demote (Proxy @'False) :: Bool)
print $ (demote (Proxy @'A) :: MyType)
Int#
我认为将其推广到处理任意数量的构造函数并添加对等的支持没有任何技术障碍。
也许有人在某个地方的包中实现了这个,但我不知道在哪里。
无论如何,singletons
使用 TH 的解决方案已准备好运行,看起来像:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module Reify where
import Data.Singletons
import Data.Singletons.TH
import Data.Kind
import GHC.Generics
import GHC.Natural
-- define a new type supporting "demote"
$(singletons [d|
data MyType = A | B Bool deriving (Show)
|])
-- add "demote" support to an existing type (e.g., imported from a library)
data LibraryType = C | D Bool deriving (Show)
$(genSingletons [''LibraryType])
main = do
print $ (demote @('Just 5) :: Maybe Natural)
print $ (demote @'False :: Bool)
print $ (demote @'A :: MyType)
print $ (demote @('D 'False) :: LibraryType)