我创建了一个使用GADT和DataKinds的问题的一个非常简单的例子。我的实际应用显然更复杂,但这清楚地捕捉了我的情况的本质。我正在尝试创建一个可以返回Test类型的任何值(T1,T2)的函数。有没有办法实现这一目标,还是我进入了依赖类型的领域?这里的问题看起来很相似,但我无法从他们那里找到(或理解)我的问题的答案。我刚开始理解这些GHC扩展。感谢。
{-# LANGUAGE GADTs, DataKinds, FlexibleInstances, KindSignatures #-}
module Test where
data TIdx = TI | TD
data Test :: TIdx -> * where
T1 :: Int -> Test TI
T2 :: Double -> Test TD
type T1 = Test TI
type T2 = Test TD
prob :: T1 -> T2 -> Test TIdx
prob x y = undefined
----这是错误---- Test.hs:14:26:
Kind mis-match
The first argument of `Test' should have kind `TIdx',
but `TIdx' has kind `*'
In the type signature for `prob': prob :: T1 -> T2 -> Test TIdx
答案 0 :(得分:6)
您收到的错误消息是因为Test
的类型参数需要
有类型TIdx
,但只有TI
和TD
的类型。
类型 TIdx
具有*
种类。
如果我理解你要表达的是结果
prob
的类型为Test TI
或Test TD
,但实际类型为
在运行时确定。但是,这不会直接起作用。返回类型
通常必须在编译时知道。
你可以做什么,因为GADT构造函数每个映射到特定的phatom类型TIdx
,是返回一个删除幻像类型的结果
存在或另一个GADT然后使用模式恢复类型
匹配。
例如,如果我们定义两个需要特定类型Test
的函数:
fun1 :: T1 -> IO ()
fun1 (T1 i) = putStrLn $ "T1 " ++ show i
fun2 :: T2 -> IO ()
fun2 (T2 d) = putStrLn $ "T2 " ++ show d
此类型检查:
data UnknownTest where
UnknownTest :: Test t -> UnknownTest
prob :: T1 -> T2 -> UnknownTest
prob x y = undefined
main :: IO ()
main = do
let a = T1 5
b = T2 10.0
p = prob a b
case p of
UnknownTest t@(T1 _) -> fun1 t
UnknownTest t@(T2 _) -> fun2 t
值得注意的是,在case
- 表达式中,尽管如此
UnknownTest
GADT已删除了幻像类型,T1
和T2
构造函数已足够
向t
恢复其确切类型Test TI
的编译器输入信息
在case-expression的分支内Test TD
,允许我们例如呼叫
期望这些特定类型的函数。
答案 1 :(得分:1)
这里有两种选择。您可以从参数类型推断出返回值的类型,也可以不用。
在前一种情况下,您可以优化类型:
data Which :: TIdx -> * where
Fst :: Which TI
Snd :: Which TD
prob :: Which i -> T1 -> T2 -> Test i
prob Fst x y = x
prob Snd x y = y
在后一种情况下,您必须删除类型信息:
prob :: Bool -> T1 -> T2 -> Either Int Double
prob True (T1 x) y = Left x
prob False x (T2 y) = Right y
您还可以使用存在类型删除类型信息:
data SomeTest = forall i . SomeTest (Test i)
prob :: Bool -> T1 -> T2 -> SomeTest
prob True x y = SomeTest x
prob False x y = SomeTest y
在这种情况下,您无法使用SomeTest
的值执行任何有趣的操作,但您可以在实际示例中使用。