异构 GADT 列表
list of heterogeneous GADTs
我有如下的 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’ is a rigid type variable bound
错误消息。
我尝试使用存在类型来克服刚性类型变量的问题,但后来我失去了传递有关 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))
]
这只是改变了您消费这种类型的方式。你可以通过pattern-matching恢复类型信息:
consume :: Test a -> Int
consume (A a) = a + 1
consume (B b) = truncate b
map (\ (SomeTest x) -> consume x) myData :: [Int]
使用 RankNType
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
如果你有一个完全多态的类型,比如:
data Test a where
Test :: a -> Test a
然后您可以通过添加类型类约束来恢复有关该类型的更多有趣信息。例如,如果你想要完整的动态信息,你可以用 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)
大多数时候你可以使用比这更强大的类型类,这取决于你实际需要的操作。
我有如下的 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’ is a rigid type variable bound
错误消息。
我尝试使用存在类型来克服刚性类型变量的问题,但后来我失去了传递有关 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))
]
这只是改变了您消费这种类型的方式。你可以通过pattern-matching恢复类型信息:
consume :: Test a -> Int
consume (A a) = a + 1
consume (B b) = truncate b
map (\ (SomeTest x) -> consume x) myData :: [Int]
使用 RankNType
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
如果你有一个完全多态的类型,比如:
data Test a where
Test :: a -> Test a
然后您可以通过添加类型类约束来恢复有关该类型的更多有趣信息。例如,如果你想要完整的动态信息,你可以用 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)
大多数时候你可以使用比这更强大的类型类,这取决于你实际需要的操作。