【问题标题】:why doesn't haskell have heterogeneous lists为什么haskell没有异构列表
【发布时间】:2011-06-03 20:24:30
【问题描述】:

我不明白为什么我不能在 haskell 中构建一个看起来像 [1,"1",1.1] 的列表。我不认为静态类型会妨碍我,因为我认为 head 现在会有一个定义错误的类型,但后来我想到了,运行时系统没有理由不实例化不同的版本的head 每当一个列表被输入到它,所以head [1,"1",1.1] 将被输入为List->Inthead (tail [1,"1",1.1]) 将被输入为List->String。既然运行时已经做了很多记账,为什么它不提供各种前奏函数的更高级的多态(或者它是通用的)版本?我在这里错过了什么?

【问题讨论】:

  • @davidk01: 告诉我列表变量的第四个元素的类型...换句话说,一个作为函数参数的列表?就此而言,在编译时告诉我它是否有 第 4 个元素!如果列表或索引是变量,则在编译时无法知道类型。
  • davidk01:这就是问题所在——编译器不知道具体数字。如果编译器遇到[1,"1",1.1] !! n,它无法判断表达式的类型。
  • davidk01:你不能告诉我它的类型是你不能构建这样一个列表的原因——你回答了你自己的问题。如果您知道您的列表将始终是 IntStringFloat,那么您将拥有一个元组而不是一个列表。
  • @davidk01:仔细阅读@Antal S-Z 所说的……异常不是返回值。
  • @davidk01:你并不是“太愚蠢以至于无法意识到什么对你有好处”,而且我很确定无论你尝试做什么都是完全合理的。但是,我怀疑您误解了一些术语——例如“静态类型”的真正含义以及运行时与编译时知识的含义——导致尴尬的沟通障碍。 Haskell 中的类型与 Javascript 或 Ruby 中的类型不同,只是提前知道。差异更深。我也许可以澄清一些事情,但这会很长而且不能真正回答这个问题本身......

标签: list haskell types polymorphism static-polymorphism


【解决方案1】:

因为在 Haskell 中,所有类型在编译时都是已知的。没有等到运行时才能看到类型是什么。并且因为这足以在动态类型的系统中做任何你想做的事情,同时更容易启动。

【讨论】:

  • @davidk01:它会在运行时知道,是的,但不是在编译时。很容易看出 [1, "1.1", 1.1] 的第二个元素的类型是什么,因为您正在查看列表文字,但是如果您尝试确定作为函数参数的任意列表的第 n 个元素怎么办?
  • @davidk01:[1, "1.1", 1.1] 的类型是什么?除了HCons Integer (HCons String (HCons Double HNil)) 之类的东西,你不能给它一个合理的类型。这可行(HList 模块支持此类列表,并为它们提供类似的类型),但您可以看到为什么它不是默认值。这样的列表远没有你的标准[a] 灵活,而且(恕我直言)你通常需要一个元组。
  • @davidk01:正如@Gabe 指出的那样,你想要一个元组,而不是一个列表......如果异构列表可以是可变长度的,那么就不可能知道每个元素的类型在编译时,因为您甚至不知道它在编译时有多少元素
  • @davidk01:这是一个已知长度的异构列表,你可以使用fstsnd代替headtail就可以了:(1, ("1.1", (1.1, 'c')))
  • @davidk01:我认为在静态编程语言中实际上可以有异构列表,但您可能需要依赖类型。
【解决方案2】:

确实是打字阻止了这种情况。考虑一个列表的定义(注意类型参数a,您的类型中缺少该参数):

data List a = Nil | Cons a (List a)

Cons a (List a) 中可以看到,列表头部的事物类型必须与它后面的元素类型相同。要回答您的问题,您并没有错过很多:正如您所说的运行时可以做到,但在 Haskell 中,您希望在编译时而不是运行时做出这些类型决定。

如果您想要异构列表,您可以在 Oleg Kiselyov 在 HList(= 异构列表)上的工作中看到一些魔法。它可能并不完全是你想要的,但它的方向大致相同。

【讨论】:

  • 为了清楚起见,HList 的“异构列表”实际上与右嵌套的 2 元组没有什么不同。有趣的部分不是列表本身,而是 Oleg 用来构建适用于任意“列表”的通用函数的疯狂类型级元编程深度魔法。
  • @davidk01:以上是你真正需要知道的。在您学习的这一点上,您不需要了解 HList 或其他类型黑客。首先学习基础知识。
【解决方案3】:

您知道,实际上有一个用于异构列表的包,使用非平凡的技术,您确实应该确保在深入研究之前充分了解类型系统。由于类型系统,默认情况下您没有这个。 Haskell 中的列表不仅仅是一个列表。它是a 的列表,'a' 是 Int、String,无论你想要什么。但是,一个列表只能包含一种类型的值。

请注意,您可以使用存在量化定义满足某些约束的元素的“异构列表”,但我认为您还没有做到这一点,并且在继续之前应该专注于理解这里的其他答案。

【讨论】:

  • +1 表示 Haskell 列表应该是 a 的列表
【解决方案4】:

有一种称为 HList 的异构列表类型(可在 Hackage 上获得),但请注意,您的列表内容可能有一个类型。考虑这样的事情:

history = [-12, "STATEMENT END", (-244, time January 4 2010), ...]

您的数据有一种难以出现的类型,例如:

data HistoryEntry = Withdrawal Int | StatementClosing | ScheduledPayment Int CalendarTime

history = [Withdrawal 12, StatementClosing, ScheduledPayment 244 (time January 4 2010)]

在许多情况下,您的数据具有您只需要寻找的类型。

【讨论】:

    【解决方案5】:

    Heterogenous collections

    {-# OPTIONS -fglasgow-exts #-}
    --
    -- An existential type encapsulating types that can be Shown
    -- The interface to the type is held in the show method dictionary
    --
    -- Create your own typeclass for packing up other interfaces
    --
    data Showable = forall a . Show a => MkShowable a
    
    --
    -- And a nice existential builder
    --
    pack :: Show a => a -> Showable
    pack = MkShowable
    
    --
    -- A heteoregenous list of Showable values
    --
    hlist :: [Showable]
    hlist = [ pack 3
            , pack 'x'
            , pack pi
            , pack "string"
            , pack (Just ()) ]
    
    --
    -- The only thing we can do to Showable values is show them
    --
    main :: IO ()
    main = print $ map f hlist
        where
            f (MkShowable a) = show a
    
    {-
    
    *Main> main
    ["3","'x'","3.141592653589793","\"string\"","Just ()"]
    
    -}
    

    【讨论】:

    • 这不是 HList,它是存在类型。
    猜你喜欢
    • 2012-04-20
    • 1970-01-01
    • 2015-02-26
    • 1970-01-01
    • 1970-01-01
    • 2012-12-02
    • 1970-01-01
    • 2011-01-17
    • 1970-01-01
    相关资源
    最近更新 更多