多态数据类型的函数

Functions to Polymorphic data types

数据Foo a定义如下:

data Foo a where
  Foo :: (Typeable a, Show a) => a -> Foo a
  -- perhaps more constructors

instance Show a => Show (Foo a) where
  show (Foo a) = show a

在某些情况下:

fiveFoo :: Foo Int
fiveFoo = Foo 5

falseFoo :: Foo Bool
falseFoo = Foo False

如何从 b -> Foo a 定义任何函数,例如:

getFoo :: (Show a, Typeable a) => String -> Foo a
getFoo "five" = fiveFoo
getFoo "false" = falseFoo

此处 getFoo 不使用 Couldn't match type ‘a’ with ‘Bool’ 进行类型检查。

我唯一感兴趣的是 a 属于 class Show 所以我可以像这样使用 getFoo:

main = getLine >>= (print . getFoo)

也许您想从 Foo 中省略类型参数。

data Foo where
  Foo :: (Typeable a, Show a) => a -> Foo

instance Show Foo where
  show (Foo a) = show a

fiveFoo :: Foo
fiveFoo = Foo (5 :: Int) -- (Foo 5) doesn't work because of ambiguity

falseFoo :: Foo
falseFoo = Foo False

getFoo :: String -> Foo
getFoo "five" = fiveFoo
getFoo "false" = falseFoo

print $ getFoo "five" -- prints '5'
print $ getFoo "false" -- prints 'False'
getFoo :: (Show a, Typeable a) => String -> Foo a
getFoo "five" = fiveFoo
getFoo "false" = falseFoo

如果 fiveFoo :: Foo IntfalseFoo :: Foo Bool,您实际上是在要求 getFoo 到 return 不同的类型,具体取决于您在 [=22= 处输入的值]-时间。你不能那样做。在 Haskell 中,所有类型必须在 编译时 时已知。

如果您只想将其转换为字符串,为什么不首先将其存储为字符串呢?我猜答案是这实际上是对您要解决的实际问题的简化...(?)

您可以使用existential types隐藏数据类型,"carry"像Show这样的类型class。

请注意,像这样使用存在类型被认为是 Haskell 中的 anti-pattern,您可能需要仔细考虑是否真的要这样做:更明确地说明您的类型通常更简单、更好,而且更不容易出错。

然而,话虽如此,如果您真的想这样做,下面是您如何在示例中使用存在类型:

{-# LANGUAGE ExistentialQuantification #-}

-- This Foo can only be constructed with instances of Show as its argument.
data Foo = forall a. Show a => Foo a

-- Note that there is no "Show a => ..." context here:
-- Foo itself already carries that constraint around with it.
instance Show Foo where
  show (Foo a) = show a


getFoo :: String -> Foo
getFoo "five" = Foo 5
getFoo "false" = Foo False

main = print . getFoo =<< getLine

示范:

ghci> main
five
5
ghci> main
false
False