我不明白为什么我不能构建一个看起来像[1,"1",1.1]
haskell 的列表。我不认为是静态类型会妨碍我,因为我认为现在会有一个定义错误的类型,但后来我想到了,运行时head
系统没有理由不实例化一个不同的版本head
list 被输入它,因此head [1,"1",1.1]
将被输入为List->Int
并且head (tail [1,"1",1.1])
将被输入为List->String
. 既然运行时已经做了很多记账,为什么它不提供各种前奏函数的更高级的多态(或者它是通用的)版本?我在这里想念什么?
5 回答
确实是打字阻止了这种情况。考虑一个列表的定义(注意你的a
类型中缺少的类型参数):
data List a = Nil | Cons a (List a)
在中Cons a (List a)
您可以看到列表头部的事物类型必须与它后面的元素类型相同。要回答您的问题,您并没有错过很多:正如您所说的运行时可以做到,但在 Haskell 中,您希望在编译时而不是运行时做出这些类型的决定。
如果你想要异构列表,你可以看到 Oleg Kiselyov 在他关于HList(= 异构列表)的工作中的一些魔法。它可能不完全是你想要的,但它的方向大致相同。
因为在 Haskell 中,所有类型在编译时都是已知的。没有等到运行时才能看到类型是什么。而且因为这足以在动态类型系统中做任何你想做的事情,同时更容易启动。
正如您所知,实际上有一个用于异构列表的包,使用非平凡的技术,您确实应该确保在深入研究之前充分了解类型系统。由于类型系统,默认情况下您没有这个。Haskell 中的列表不仅仅是一个列表。这是一个 a's 的列表,'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)]
在许多情况下,您的数据具有您只需要寻找的类型。
查看异构集合
{-# 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 ()"]
-}