Haskell 异构GADT列表

Haskell 异构GADT列表,haskell,gadt,Haskell,Gadt,我有一个像下面这样的GADT data MyTypes = MyInt | MyDouble data Test (t :: MyTypes) where A :: Int -> Test 'MyInt B :: Double -> Test 'MyDouble 这允许我在类型级别跟踪Test值中包含的值,这样我还可以执行以下操作 data Test2 (t :: MyTypes) where A2 :: Test 'MyInt ->

我有一个像下面这样的GADT

data MyTypes
    = MyInt
    | MyDouble

data Test (t :: MyTypes) where
    A :: Int -> Test 'MyInt
    B :: Double -> Test 'MyDouble
这允许我在类型级别跟踪
Test
值中包含的值,这样我还可以执行以下操作

data Test2 (t :: MyTypes) where
    A2 :: Test 'MyInt -> Test2 'MyInt
    B2 :: Test 'MyDouble -> Test2 'MyDouble
并传递信息

但是,如果我想有一个具有不同
MyTypes
Test
值的列表,比如

myData :: [Test (t :: MyTypes)]
myData =
    [ A (3 :: Int)
    , B (5.0 :: Double)
    ]
我得到了预期的
't'是一个刚性类型变量绑定的
错误消息

我试图使用存在类型来克服僵化类型变量的问题,但随后我失去了传递有关
MyType
的类型级别信息的能力


我应该如何处理这样一个问题?

这里的解决方案是一个存在的问题:

data Test a where
  A :: Int -> Test Int
  B :: Double -> Test Double

data SomeTest where
  SomeTest :: Test a -> SomeTest

myData :: [SomeTest]
myData =
  [ SomeTest (A (3 :: Int))
  , SomeTest (B (5.0 :: Double))
  ]
这只是改变了您使用这种类型的方式。您可以通过模式匹配来恢复类型信息:

consume :: Test a -> Int
consume (A a) = a + 1
consume (B b) = truncate b

map (\ (SomeTest x) -> consume x) myData :: [Int]
使用
ranknype
s,您可以使用恢复类型的继续项将其展开:

test :: (forall a. Test a -> r) -> SomeTest -> r
test k (SomeTest x) = k x

test (\ x -> case x of
  A a -> a + 1 {- ‘a’ is known to be ‘Int’ here -}
  B b -> truncate b {- ‘b’ is known to be ‘Double’ here -})
  :: SomeTest -> Int
当您使用不带任何类型类约束的存在主义将多个事物打包在一种“模块”中时,它最有用,在这种“模块”中,它们都必须在类型上达成一致,但从外部看,该类型是不透明的。这限制了消费者可以做的操作,例如,考虑一对请求和一个变量来存储该请求的结果:

data SomeRequest where
  SomeRequest :: IO a -> IORef a -> SomeRequest

fetchRequests :: [SomeRequest] -> IO ()
fetchRequests = traverse_ fetchRequest
  where

    -- ‘fetchRequest’ controls the fetching strategy (sync/async)
    -- but can’t do anything with the fetched value
    -- other than store it in the ‘IORef’.
    fetchRequest :: SomeRequest -> IO ()
    fetchRequest (SomeRequest request result) = do
      value <- request
      writeIORef result value
然后,您可以通过添加typeclass约束来恢复有关类型的更有趣的信息。例如,如果需要完整的动态信息,可以使用
Typeable

data SomeTest where
  SomeTest :: Typeable a => Test a -> SomeTest

test :: (forall a. Typeable a => Test a -> r) -> SomeTest -> r
test k (SomeTest x) = k x

test (\ (Test a) -> case cast a of
  Just a' -> (a' :: Int) + 1
  Nothing -> case cast a of
    Just a' -> length (a' :: String)
    Nothing -> 0)

大多数情况下,根据实际需要的操作,您可以使用比此功能更小的typeclass。

您能否详细说明在使用具有异构标记的列表时,您希望如何“传递类型级别的信息”?我认为,要么你可以用存在主义来实现这一点,要么这项任务不可能以类型安全的方式实现;你只需要在传递了存在主义之后重新包装它。但是为了让你明白,我们必须看看你尝试了什么没有成功。另一个方向是一个列表类型,它公开了它的内容类型。例如,请参见
乙烯基
软件包中的
Data.venyly.Core
data SomeTest where
  SomeTest :: Typeable a => Test a -> SomeTest

test :: (forall a. Typeable a => Test a -> r) -> SomeTest -> r
test k (SomeTest x) = k x

test (\ (Test a) -> case cast a of
  Just a' -> (a' :: Int) + 1
  Nothing -> case cast a of
    Just a' -> length (a' :: String)
    Nothing -> 0)