List 为什么';t haskell具有异构列表

List 为什么';t haskell具有异构列表,list,haskell,types,polymorphism,static-polymorphism,List,Haskell,Types,Polymorphism,Static Polymorphism,我不明白为什么我不能构造一个类似haskell中的[1,“1”,1.1]的列表。我不认为是静态类型妨碍了我,因为我认为head现在会有一个定义不正确的类型,但后来我考虑了它,并且没有理由运行时系统不实例化不同版本的head,每当一个列表被输入到它中,所以head[1,“1”,1.1]将被键入List->Int和头(尾[1,“1”,1.1])将被键入List->String。既然运行时已经做了大量的簿记工作,为什么它不提供各种prelude函数的更丰富的多态(或者是通用)版本呢?我在这里遗漏了什么

我不明白为什么我不能构造一个类似haskell中的
[1,“1”,1.1]
的列表。我不认为是静态类型妨碍了我,因为我认为
head
现在会有一个定义不正确的类型,但后来我考虑了它,并且没有理由运行时系统不实例化不同版本的
head
,每当一个列表被输入到它中,所以
head[1,“1”,1.1]
将被键入
List->Int
头(尾[1,“1”,1.1])
将被键入
List->String
。既然运行时已经做了大量的簿记工作,为什么它不提供各种prelude函数的更丰富的多态(或者是通用)版本呢?我在这里遗漏了什么?

因为在Haskell中,所有类型在编译时都是已知的。不存在等待运行时才能看到类型的情况。因为这足以在动态类型系统中执行任何您可能希望执行的操作,同时更易于进行引导推理。

事实上,正是类型阻止了这一点。考虑列表的定义(注意类型参数<代码> A<代码>,这是您的类型中缺少的):

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


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

正如您所知,实际上有一个用于异构列表的包,使用的是非平凡的技术,您确实应该确保在深入研究此问题之前充分了解类型系统。由于类型系统的原因,默认情况下没有此选项。Haskell中的列表不仅仅是一个列表。这是一个a的列表,“a”是Int,String,任何你想要的。但是,一个列表只能包含一种类型的值


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

有一种称为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)]
在许多情况下,您的数据有一种类型,您只需查找即可。

查看


@davidk01:是的,它会在运行时被知道,但不会在编译时被知道。很容易看出
[1,“1.1”,1.1]
的第二个元素的类型是什么,因为您正在查看一个列表文字,但是如果您试图确定作为函数参数的任意列表的第n个元素,该怎么办?@davidk01:那么
[1,“1.1”,1.1]
的类型是什么?除了像
HCons Integer(HCons String(HCons Double HNil))
之类的东西之外,您不能给它一个合理的类型。这是可行的(HList模块支持这样的列表,并提供类似的类型),但是您可以看到为什么它不是默认的列表。这样的列表远不如您的标准
[a]
灵活,而且(IMHO)您通常需要一个元组来代替。@davidk01:正如@Gabe所指出的,您需要的是元组,而不是列表。。。如果异构列表的长度可变,则在编译时不可能知道每个元素的类型,因为您甚至不知道它在编译时有多少个元素。@davidk01:这是一个具有已知长度的异构列表,您可以使用
fst
snd
而不是
head
tail
(1,('1.1',(1.1,'c'))
@davidk01:我认为在静态编程语言中,实际上可以有异构列表,但是您可能需要依赖类型。@davidk01:告诉我列表变量的第四个元素的类型。。。换句话说,是函数的参数列表?对于这个问题,请在编译时告诉我它是否有第4个元素!如果列表或索引是变量,则在编译时无法知道类型。davidk01:这就是问题所在——编译器不知道具体的数字。如果编译器遇到
[1,“1”,1.1]!!n
它不能告诉表达式的类型。davidk01:你不能告诉我它的类型,这就是你不能构建这样一个列表的原因——你回答了你自己的问题。如果你知道你的列表总是一个
Int
、一个
String
和一个
Float
,那么你就有了一个元组而不是列表。@davidk01:仔细阅读@Antal S-Z所说的内容。。。异常不是返回值。@davidk01:你并不是“太蠢而意识不到什么对你有好处”,我很确定你所做的一切都是完全合理的。然而,我怀疑您误解了一些术语,例如“静态类型”的真正含义以及运行时和编译时知识的含义,从而导致了尴尬的沟通障碍。Haskell中的类型不仅仅与Javascript或Ruby中的类型相似,除非提前知道;差别更大。我也许能澄清一些事情,但这会很长,而且不会真正回答这个问题本身…+1因为重要的一点是Haskell列表是a的列表。为了清楚起见,HList的“异构列表”与右嵌套的2元组没有什么不同。有趣的不是列表本身,而是疯狂的类型级元编程deep magic Oleg用来构造在任意“列表”上工作的泛型函数。@davidk01:以上是您实际需要知道的。在你学习的这一点上,你不需要了解H
data HistoryEntry = Withdrawal Int | StatementClosing | ScheduledPayment Int CalendarTime

history = [Withdrawal 12, StatementClosing, ScheduledPayment 244 (time January 4 2010)]
{-# 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 ()"]

-}