基于一些谓词的Haskell中的泛型函数结果

时间:2013-07-28 18:32:19

标签: generics haskell types typeclass type-systems

是否可以在haskell中编写函数,该函数在以下情况下返回泛​​型类型:

  • 在此函数的主体中,我们输出2个单独的类型AB(基于某些计算)
  • 类型AB有一个共同的type class C

让我们看一下示例代码。类型检查器应该能够检查这个代码是否正确 - 函数test输出类型为AB的实例,因此我们可以对结果执行f

data A = A 
data B = B

class C a where
    f :: a -> Int

instance C A where
    f x = 2

instance C B where
    f x = 3

-- This function fails to compile:
-- I want something like:
-- test :: C a => Int -> a
test x = if x < 1  
        then A
        else B

main = do
    print $ f $ test 0
    print $ f $ test 1

我知道这看起来像反模式或类似的东西,但我想知道答案,因为我喜欢测试Haskell功能,特别是在类型系统方面。

1 个答案:

答案 0 :(得分:3)

这里的问题是你对Haskell的类型变量进行了普遍量化。

这意味着它们可以被理解为

forall a. C a => Int -> a

这意味着调用者选择具体类型a。不是被叫者。

要解决此问题,您需要一些称为“存在”变量的东西。普遍量化的变量应该被理解为“forall”,存在量化的变量应该被理解为“存在”。

如何执行此操作的快速草图

{-# LANGUAGE ExistentialQuantification, GADTs #-}

-- Use existentials 
data Box = forall a. C a => Box a

-- Use GADTs
data Box' where
  Box' :: C a => a -> Box'

instance C Box where
    f (Box a) = f a

test :: Int -> Box
test x = if x < 1  
    then Box A
    else Box B

这个的本质是我们隐藏了这个C数据类型后面的具体Box实例,然后我们传递它而不是普遍量化的类型变量。

或者,您可以使用简单的延续来避免Rank2Types的Box类型。

test :: Int -> (forall a. C a => a -> r) -> r
test x c = if x < 1 then c A else c B
main = do
  test 1 (print . f)
  test 0 (print . f)

有趣的存在变量的怪癖,用GHC尝试这个代码

fun = foo
  where (Box foo) = test 1