The Haskell programming language has a concept of newtypes
: If I write newtype Foo = Foo (Bar)
, then a new type Foo
is created that is isomorphic to Bar
, ie there are bijective conversions between the two. Properties of this construct are:
What other programming languages provide this feature?
One example seems to be single-value-structs in C when used with record accessors/constructors only. Invalid candidates would be single-valued-structs in C when used with casts, as the casts are not checked by the compiler, or objects with a single member in Java, as these would not share the same representation.
Related questions: Does F# have 'newtype' of Haskell? (No) and Does D have 'newtype'? (not any more).
Frege has this, though, unlike in Haskell there is no extra keyword. Instead, every product type with just one component is a newtype.
Example:
data Age = Age Int
Also, all langugaes that have nominal typing and allow to define a type in terms of another should have this feature. For example Oberon, Modula-2 or ADA. So after
type age = integer; {* kindly forgive syntax errors *}
one couldn't confuse an age and some other quantity.
I believe Scala's value classes satisfy these conditions.
For example:
case class Kelvin(k: Double) extends AnyVal
Edit: actually, I'm not sure that conversions have zero overhead in all cases. This documentation describes some cases where allocation of objects on the heap is necessary, so I assume in those cases there would be some runtime overhead in accessing the underlying value from the object.
Go has this:
If we declare
type MyInt int var i int var j MyInt
then i has type int and j has type MyInt. The variables i and j have distinct static types and, although they have the same underlying type, they cannot be assigned to one another without a conversion.
"The same underlying type" means that the representation in memory of a MyInt
is exactly that of an int
. Passing a MyInt
to a function expecting an int
is a compile-time error. The same is true for composite types, eg after
type foo struct { x int }
type bar struct { x int }
you can't pass a bar
to a function expecting a foo
( test ).
Mercury is a pure logic programming language, with a type system similar to Haskell's.
Evaluation in Mercury is strict rather than lazy, so there would be no semantic difference between Mercury's equivalents of newtype
and data
. Consequently any type which happens to have only one constructor with only one argument is represented identically to the type of that argument, but still treated as the same type; effectively "newtype" is a transparent optimisation in Mercury. Example:
:- type wrapped
---> foo(int)
; bar(string).
:- type wrapper ---> wrapper(wrapped).
:- type synonym == wrapped.
The representation of wrapper
will be identical to that of wrapped
but is a distinct type, as opposed to synonym
which is simply another name for the type wrapped
.
Mercury uses tagged pointers in its representations. 1 Being strict and being allowed to have different representations for different types, Mercury generally tries to do away with boxing where possible. eg
The "newtype" optimization is really just one particular application of that general idea. The "wrapper" type doesn't need any memory cells allocated above what is already holding the "wrapped" type. And since it needs zero tag bits, it can also fit any tags in the reference to the "wrapped" type. Therefore the whole reference to the "wrapped" type can be inlined into the reference to the wrapper type, which ends up being indistinguishable at runtime.
1 The details here may only apply to the low level C compilation grades. Mercury can also compile to "high level" C or to Java. There's obviously no bit fiddling going on in Java (though as far as I know the "newtype" optimization still applies), and I'm just less familiar with the implementation details in the high level C grades.
Rust has always allowed you to make single-field types, but with the recently stabilized repr(transparent)
attribute you can now be confident that the type created will have the exact data layout as the wrapped type, even across FFI and such.
#[repr(transparent)]
pub struct FooWrapper(Foo);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.