Haskell编程语言的概念为newtypes
:如果我写newtype Foo = Foo (Bar)
,则创建一个与Foo
同构的新类型Bar
,即两者之间的双向转换。该构造的属性是:
还有哪些编程语言提供此功能?
当与记录访问器/构造函数一起使用时,一个示例似乎是C中的单值结构。当与强制转换一起使用时,无效的候选者将是C中的单值结构,因为编译器不会检查强制转换,或者在Java中使用单个成员的对象,因为它们不会共享相同的表示。
相关问题:Does F# have 'newtype' of Haskell?(否)和Does D have 'newtype'?(不再是)。
答案 0 :(得分:11)
Frege有这个,与Haskell不同,没有额外的关键字。相反,只有一个组件的每种产品类型都是新类型。
示例:
data Age = Age Int
此外,所有具有标称类型且允许根据另一种类型定义类型的languga都应该具有此功能。例如Oberon,Modula-2或ADA。所以
之后type age = integer; {* kindly forgive syntax errors *}
人们不能混淆年龄和其他数量。
答案 1 :(得分:8)
我相信Scala的value classes符合这些条件。
例如:
case class Kelvin(k: Double) extends AnyVal
编辑:实际上,我不确定转换在所有情况下都是零开销。这个documentation描述了一些需要在堆上分配对象的情况,所以我假设在这些情况下从对象访问底层值会有一些运行时开销。
答案 2 :(得分:7)
Go有这个:
如果我们宣布
type MyInt int var i int var j MyInt
然后我有类型int,j有类型MyInt。变量i和j具有不同的静态类型,虽然它们具有相同的基础类型,但如果没有转换,它们就无法相互分配。
“相同的基础类型”意味着MyInt
的内存中的表示正好是int
的表示。将MyInt
传递给期望int
的函数是编译时错误。对于复合类型也是如此,例如之后
type foo struct { x int }
type bar struct { x int }
您无法将bar
传递给期望foo
(test)的函数。
答案 3 :(得分:4)
Mercury是一种纯逻辑编程语言,类型系统类似于Haskell。
Mercury中的评估是严格的而不是懒惰的,因此Mercury等同于newtype
和data
之间不存在语义差异。因此,恰好只有一个构造函数只有一个参数的任何类型都与该参数的类型相同,但仍被视为相同的类型;有效的“newtype”是Mercury中的透明优化。例如:
:- type wrapped
---> foo(int)
; bar(string).
:- type wrapper ---> wrapper(wrapped).
:- type synonym == wrapped.
wrapper
的表示形式与wrapped
的表示形式相同,但它是一种不同的类型,而synonym
只是wrapped
类型的另一个名称。
Mercury在其表示中使用标记指针。 1 严格并允许对不同类型使用不同的表示,Mercury通常会尝试尽可能地取消拳击。 e.g。
“newtype”优化实际上只是该一般概念的一个特定应用。 “包装器”类型不需要在已经保持“包装”类型的上方分配任何存储器单元。由于它需要零标签位,因此它也可以适应“包装”类型的引用中的任何标签。因此,对“wrapped”类型的整个引用可以内联到对包装器类型的引用中,最终在运行时无法区分。
1 此处的详细信息可能仅适用于低级别C编译等级。 Mercury也可以编译为“高级”C或Java。在Java中显然没有任何小问题(尽管我知道“newtype”优化仍然适用),而且我对高级C级别的实现细节不太熟悉。
答案 4 :(得分:2)
Rust始终允许您创建单字段类型,但是借助最近稳定的repr(transparent)
属性,您现在可以确信创建的类型将具有与包装类型完全相同的数据布局,即使在FFI和这样的。
#[repr(transparent)]
pub struct FooWrapper(Foo);