【问题标题】:Functions to Polymorphic data types多态数据类型的函数
【发布时间】:2016-08-25 21:01:35
【问题描述】:

数据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 属于Show,所以我可以使用getFoo,例如:

main = getLine >>= (print . getFoo)

【问题讨论】:

    标签: haskell polymorphism gadt existential-type


    【解决方案1】:

    您可以使用existential types 隐藏数据类型并“携带”一个类型类,例如Show

    请注意,使用像这样的存在类型在 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
    

    【讨论】:

    • data Foo a --- a 在这里是多余的,forall 中的“内部”a 是一个不同的变量!
    • 另外,GADT 解决方案与存在解决方案几乎完全相同(我使用 GADT 只是因为 OP 使用了一个)难道 GADT 不包含存在类型吗?
    【解决方案2】:

    也许你想省略 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'
    

    【讨论】:

    • 我喜欢这个,但我想知道 OP 是否真的需要它——很容易陷入existential antipattern。不过,只有 OP 知道。
    • @chi 是的,这是存在主义的东西,但我不认为它是一种反模式。我知道我在这里是少数人。
    • 我们是否在这里将Foo 设为联合类型?这是否等同于data Foo = IFoo Int | BFoo Bool
    • @n.m.好吧,我也认为 Luke Palmer 在其博客文章中有点过于极端。不过,我认为使用例如Show-ables 列表而不是字符串列表是毫无意义的过度复杂化。在我看来,上面的Foo 类型很有价值,但这只是因为Typeable 约束,它允许稍后cast 并恢复该类型。否则,Foo 与字符串同构(或者,至多与 shows 中的字符串生成器函数同构),因此使用存在主义看起来有点矫枉过正。
    • @homam 不,它不等同。 Foo 还可以包含字符串、整数对、整数列表列表等。它可以包装任何可显示(和可键入)的内容。
    【解决方案3】:
    getFoo :: (Show a, Typeable a) => String -> Foo a
    getFoo "five" = fiveFoo
    getFoo "false" = falseFoo
    

    如果fiveFoo :: Foo IntfalseFoo :: Foo Bool,您实际上是在要求getFoo 根据您在运行时提供的值返回不同的类型。你不能那样做。在 Haskell 中,所有类型都必须在编译时知道。

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

    【讨论】:

    • 你是对的。我试图将我的问题归结为最简单的问题。我想创建一系列Nodes a b(如类别),以便每个节点处理来自用户的输入并继续处理另一个Node b a' 或最终结果b
    猜你喜欢
    • 1970-01-01
    • 2011-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-14
    • 2022-09-23
    相关资源
    最近更新 更多