【问题标题】:Haskell : How to build the Heterogeneous Type AnyHaskell:如何构建异构类型 Any
【发布时间】:2015-01-11 09:15:46
【问题描述】:

我想构建一种类型来匹配任何东西,但永远不会被使用。

例子:

type Any = forall a. a
f :: (x, Any) -> (Any, y) -> (x,y)
f (x,_) (_,y) = (x,y)

这与 {-# LANGUAGE ImpredicativeTypes #-} 编译得很好,但如果我尝试

f ("hi", 2) (3, (1, 2))

我得到错误:

<interactive>:19:9:
    No instance for (Num a) arising from the literal `2'
    Possible fix:
      add (Num a) to the context of a type expected by the context: a
    In the expression: 2
    In the first argument of `f', namely `("hi", 2)'
    In the expression: f ("hi", 2) (3, (1, 2))

<interactive>:19:13:
    No instance for (Num a) arising from the literal `3'
    Possible fix:
      add (Num a) to the context of a type expected by the context: a
    In the expression: 3
    In the second argument of `f', namely `(3, (1, 2))'
    In the expression: f ("hi", 2) (3, (1, 2))

如果我只是想让 x 和 y 成为 Num,那会很好,但我打算用这个做的事情需要比那个灵活得多。我知道forall a. a 匹配所有类型,但只能传递一个永远无法计算和底部的thunk。但是,我不想研究 Any 类型。

【问题讨论】:

  • 你为什么不能只使用另一个类型变量,比如z 而不是Any
  • 这会变得非常脆弱,如果你永远无法将它用于任何特定的事情,我不确定你为什么想要一个存在的 Any 类型。 Data.Dynamic 是处理直到运行时才知道的类型的首选方法。
  • 你不能通过2,因为它是forall a. Num a =&gt; a类型,而不是Any类型。唯一可以通过的是undefined 和类似的底部。
  • 我认为您需要在问题中添加有关您计划如何使用您的类型的这些详细信息,并且也许还指出您将如何生成将由您的函数过滤的数据。
  • @BT。为什么你真的想要这个?如果您为您的问题提供更多背景信息,您可能会得到一些好的建议。

标签: haskell types


【解决方案1】:

我认为对Any 类型存在根本性的误解。让我通过几个例子来解释。

“任何生产者”功能

f :: ... -> Any

可用于生成任何类型的值:它返回一个字符串,该字符串同时也是一个整数和一对和一头大象。具体来说,它返回底部(或者它根本不返回,如果你愿意的话)。

“任何消费者”功能

f :: Any -> ...

期望 输入任何类型的值:调用者 必须同时提供一个字符串,该字符串也是一个整数以及一对和一头大象.具体来说,调用者必须通过底部。

您正在尝试传递2,它不是 任何类型——它只是任何数字 类型。因此出现类型错误。

如果你想写一个接受任何东西的函数,你应该写

type Any = exists a. a  -- INVALID Haskell
f :: Any -> ...

但是,可惜的是,Haskell 不允许这种存在类型。如果你想要那种类型,你必须把它装箱:

data Any = forall a . Any a
f :: Any -> ...

caller = f (Any 'd')

或者,您可以将exists 提升到顶层。既然是负数,就变成forall

f :: (exists a. a) -> ...
-- becomes
f :: forall a. (a -> ...)

【讨论】:

  • 呃,我忘了制作人了。那将完全破坏类型安全。伤心。
【解决方案2】:

从 cmets 看来,真正的问题是:如何键入使用文字语法 ["a", False] 编写的列表?

答案(幸运的是!)是“你不能”。

可以创建一个存在类型,并用存在包装每个元素。如果你想这样做,你可以这样做:

{-# LANGUAGE GADTs #-}
data Box where
    Box :: a -> Box

然后列表[Box "a", Box False] 将很好地键入[Box]。但是,如果您愿意为每个元素应用一个函数,那么您不妨跳过所有类型的恶作剧,改为执行以下操作:

toss :: a -> ()
toss _ = ()

那么[toss "a", toss False] 的类型很容易理解[()]

【讨论】:

    【解决方案3】:

    它不能工作,因为你的Any 实际上是All。它只能由具有各种类型的表达式构造(例如undefined)。

    您需要使用{-# LANGUAGE ExistentialQuantification #-} to build a realAny`:

    data Any = forall a . Any a
    

    这需要是一种数据类型,因此您必须使用 Any 构造函数创建值,但现在您可以执行以下操作:

    f :: [(x, Any)] -> [(Any, y)] -> [(x,y)]
    f ((x, _) : xs) ((_, y) : ys) = (x,y) : f xs ys
    f _ _ = []
    
    > f [("hi", Any 'a'),("you", Any 3)] [(Any "a", 2),(Any Nothing, 4)]
    [("hi",2),("you",4)]
    

    【讨论】:

    • 让我尝试做的所有事情都变得复杂了。但是,谢谢你的回答。这很酷。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-26
    • 1970-01-01
    • 1970-01-01
    • 2014-10-07
    • 1970-01-01
    相关资源
    最近更新 更多