我听说Haskell被描述为具有结构类型。据我了解,记录是一个例外。例如,即使foo
和HRec2
在其字段中名义上不同,也无法使用HRec
类型调用HRec2
。
data HRec = HRec { x :: Int, y :: Bool }
data HRec2 = HRec2 { p :: Int, q :: Bool }
foo :: HRec -> Bool
是否有一些解释拒绝将结构类型扩展到包括记录在内的所有内容?
是否存在结构类型的静态类型语言,即使是记录?对于所有静态类型的语言,我可以阅读一下这个问题吗?
答案 0 :(得分:12)
Haskell有结构化类型,但不是结构类型,而且不太可能改变。*
拒绝允许名义上不同但结构相似的类型作为可互换的论据被称为类型安全。那是一件好事。 Haskell甚至有一个newtype声明来提供名义上不同的类型,以允许您强制执行更多的类型安全。类型安全是一种很容易早期捕获错误的方法,而不是在运行时允许它们。
除了amindfv的好答案,包括通过类型类的特殊多态(有效地是程序员声明的特征等价),还有参数多态,你允许绝对任何类型,所以[a]
允许列表中的任何类型和{ {1}}允许二叉树中的任何类型。
这给出了“这些类型可以互换吗?”的三个答案。
没有4:编译器否决了程序员,因为他们碰巧使用了一些Ints和一个像其他函数一样的字符串。
*我说Haskell不太可能改为结构类型。有一些讨论要介绍某种形式的可扩展记录,但没有计划将BTree a
计为与(Int,(Int,Int))
相同或(Int, Int, Int)
与Triple {one::Int, two::Int, three::Int}
相同。
答案 1 :(得分:9)
Haskell记录实际上并不比其他类型系统“结构化”。每种类型都是完全指定的,或“特别模糊”(即用类型类定义)。
要同时允许HRec
和HRec2
到f
,您可以选择以下几种方式:
代数类型:
在此,您可以将HRec
和HRec2
条记录定义为HRec
类型的一部分:
data HRec = HRec { x :: Int, y :: Bool }
| HRec2 { p :: Int, q :: Bool }
foo :: HRec -> Bool
(或者,也许更惯用:)
data HRecType = Type1 | Type2
data HRec = HRec { hRecType :: HRecType, x :: Int, y :: Bool }
<强>类型类强>
在这里,您可以将foo
定义为能够接受任何类型作为输入,只要为该类型编写了类型类实例:
data HRec = HRec { x :: Int, y :: Bool }
data HRec2 = HRec2 { p :: Int, q :: Bool }
class Flexible a where
foo :: a -> Bool
instance Flexible HRec where
foo (HRec a _) = a == 5 -- or whatever
instance Flexible HRec2 where
foo (HRec2 a _) = a == 5
使用类型类允许你比常规结构类型更进一步 - 你可以接受嵌入了必要信息的类型,即使这些类型看起来不相似,例如:
data Foo = Foo { a :: String, b :: Float }
data Bar = Bar { c :: String, d :: Integer }
class Thing a where
doAThing :: a -> Bool
instance Thing Foo where
doAThing (Foo x y) = (x == "hi") && (y == 0)
instance Thing Bar where
doAThing (Bar x y) = (x == "hi") && ((fromInteger y) == 0)
我们可以运行fromInteger
(或任意函数)来获取我们所需的数据!
答案 2 :(得分:2)
我知道Haskell中结构类型记录的两个库实现:
HList年龄较大,在一篇优秀论文中有解释: Haskell被忽视的对象系统(免费在线,但不会让我包含更多链接)
vinyl更新,并使用了新的GHC功能。至少有一个图书馆,即vinyl-gl,使用它。
但我无法回答你问题的语言设计部分。
答案 3 :(得分:0)
要回答你的上一个问题,Go和Scalas肯定有结构类型。有些人(包括我)会称之为“静态不安全的打字”,因为它隐含地声明程序中所有同名的方法具有相同的语义,这意味着“远距离的幽灵行为”,将源文件中的代码与程序从未见过的某个库中的代码。
IMO,最好要求相同命名的方法明确声明它们符合行为的命名语义“模型”。
是的,编译器会保证该方法是可调用的,但它并不比说:
更安全 f :: [a] -> Int
让编译器选择可能是length
的任意实现。
(使用Scala“implicits”或Haskell(GHC?)“反射”包可以使类似的想法变得安全。)