如何在Julia语言中实现newtype模式?
该概念拥有多个可用于描述它的不同名称。
"Newtype"通常特指在具备便捷支持的语言中实现强类型模式。
Rust就是这样的语言,它具备元组结构体的概念:
struct SecurityId(i64); struct SecurityInMarketId(i64);
“强类型模式”(我认为还有其他更合适的名称,但已记不清)也被称为“强类型”,它本质上是类型的别名,但语言会将其视为与原类型不同的独立类型。
顺便一提:如果你知道该模式的其他替代名称,请留言。
Julia支持类型别名:
const MyType = DataFrame
遗憾的是,这并非强类型的示例,因为Julia不会将MyType视为独立于DataFrame的类型,它仅仅是一个名称别名。
为了证明这一点,我们可以编写以下代码,说明它无法用于基于类型的函数分派:
function exampleFunction(df::DataFrame) println("DataFrame") end function exampleFunction(df::MyType) println("MyType") end
运行这段代码后,exampleFunction函数只会有1个方法(而非2个)。如果运行:
julia> exampleFunction(DataFrame()) MyType
如上所示,会打印MyType。这表明第二个方法实现覆盖了最初的定义。
据我所知,在C++中实现这种强类型模式的唯一方式是将类型包装在结构体(或类)中:
class SecurityInMarketId { private: int _security_in_market_id; };
题外话:实际上还可以通过模板元编程实现,但这种方式会增加理解和调试的难度,不像Rust的方案那样直观。
我发现在Julia中实现该模式的方式本质上相同,即将现有类型包装在结构体中:
mutable struct SecurityInMarketId _security_in_market_id::Int64 end
这种方式存在不足:虽然它能为类型系统提供新类型,但需要做出两种妥协之一:
- 访问本应“隐含私有”的数据
- 为新定义的结构体类型
SecurityInMarketId重新实现所有所需函数。例如,如果包装DataFrame,我们可能需要实现元素访问、过滤、选择行列、读写文件等函数。可能需要大量新实现才能使用结构体内部包装类型的函数。
选项2显然需要编写大量样板代码,这些代码没有实际功能,只是将函数调用转发给结构体的内部数据。
选项1会导致API看起来“不够优雅”,例如:
println("SecurityInMarketId=$(security_in_market_id._security_in_market_id)")
那么在Julia中实现该模式的标准方式是什么?(或者说只能是将类型包装在结构体中并做出上述两种妥协之一?)
其主要目的是在函数调用存在多个相同数据类型参数的场景中提供安全保障。
这种情况常出现在使用大量“ID”的场景中。
例如,我使用了SecurityId和SecurityInMarketId,它们在概念上是不同的事物,在调用函数时用错或交换它们显然是错误的:
function example(securityId::Int64, securityInMarketId::Int64) ... end
使用强类型模式可以避免将错误的变量传入错误的参数位置。
内容的提问来源于stack exchange,提问作者user2138149

