Reputation: 16484
This concept has a several different possible names which might be used to describe the concept.
"Newtype" usually specifically refers to the implementation of the strong type pattern in a language which has convenient support for it.
One such language is Rust, which has the concept of tuple-structs.
struct SecurityId(i64);
struct SecurityInMarketId(i64);
The "strong type pattern" (I think there are other, better names for this, but I don't recall what they are) aka so-called "strong-typing" is effectively an alias for a type, except that the language treats that type as being different to the type it is aliased from.
BTW - if you know of alternative names for this pattern please post a comment.
Julia has type aliasing.
const MyType = DataFrame
Unfortunately, this is not an example of strong typing, because Julia does not treat MyType
as an independent type to DataFrame
. It really is just a name alias.
To show this, we can write the following code to prove this cannot be used for type-based function dispatch.
function exampleFunction(df::DataFrame)
println("DataFrame")
end
function exampleFunction(df::MyType)
println("MyType")
end
Running this code will result in 1 function exampleFunction
with only 1 (not 2) methods. If you run
julia> exampleFunction(DataFrame())
MyType
as shown above, MyType
will be printed. This demonstrates the second method implementation of exampleFunction
replaced the original definition.
In C++, as far as I recall, the only way to implement this strong typing pattern is to wrap a type in a struct (or class).
class SecurityInMarketId {
private:
int _security_in_market_id;
};
Aside: Actually, there are other ways to do it using template metaprogramming. However, this approach is likely to make it more difficult to understand and debug. It is not a straightforward solution in the same way that Rust provides.
The only way I have found to do it in Julia is essentially the same. Wrapping an existing type in a struct.
mutable struct SecurityInMarketId
_security_in_market_id::Int64
end
This approach has shortcomings. While it does provide a new type for the type system, there is a choice of two compromises to be made:
SecurityInMarketId
. For example, if wrapping a DataFrame
, we are likely to require implementations of functions for element access, filtering, selecting columns and rows, reading and writing to file, etc. There could be a large number of functions which require new implementations just to use the functions of the internal data type which we have wrapped in a struct.Option 2 clearly requires significant amounts of boilerplate code to be written. Code which has no functionality, it just forwards function calls to the internal data of the struct.
Option 1 results in an "ugly" looking API. For example
println("SecurityInMarketId=$(security_in_market_id._security_in_market_id)")
What would be the canonical way to implement this in Julia? (Or is it simply wrapping in a struct and making one of the two above compromises?)
The main purpose of this is as a safety rail in contexts where there are function calls which take multiple parameters of the same data type.
This is likely to occur when there are lots of "ids" in use.
For example, I used a SecurityId
and a SecurityInMarketId
. These are conceptually different things and it is obviously an error to use the wrong one or to swap them when calling a function.
function example(securityId::Int64, securityInMarketId::Int64) ... end
We can prevent use of the wrong variable in the wrong slot with the strong type pattern.
Upvotes: 0
Views: 73