3

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

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

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

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 功能,尤其是涉及到类型系统时。

4

1 回答 1

3

这里的问题是您 Haskell 的类型变量是普遍量化的。

这意味着它们可以读作

forall a. C a => Int -> a

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

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

如何做到这一点的快速草图

{-# 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数据类型后面,然后我们将其传递而不是通用量化的类型变量。

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

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
于 2013-07-28T18:40:20.027 回答