DataKinds的麻烦

时间:2013-02-17 07:21:22

标签: haskell gadt data-kinds

我创建了一个使用GADT和DataKinds的问题的一个非常简单的例子。我的实际应用显然更复杂,但这清楚地捕捉了我的情况的本质。我正在尝试创建一个可以返回Test类型的任何值(T1,T2)的函数。有没有办法实现这一目标,还是我进入了依赖类型的领域?这里的问题看起来很相似,但我无法从他们那里找到(或理解)我的问题的答案。我刚开始理解这些GHC扩展。感谢。

similar question 1

similar question 2

{-# 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

2 个答案:

答案 0 :(得分:6)

您收到的错误消息是因为Test的类型参数需要 有类型TIdx,但只有TITD的类型。 类型 TIdx具有*种类。

如果我理解你要表达的是结果 prob的类型为Test TITest 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已删除了幻像类型,T1T2构造函数已足够 向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的值执行任何有趣的操作,但您可以在实际示例中使用。