是否存在在应用程序中将某个值设置为常量一次,或者以其他方式允许它被覆盖但仍然被视为静态类型语言中的常量之类的事情?我正在想象依赖注入,当被注入的东西位于其注入位置的外部时,如何使其静态类型化。
我想象像一个
logger
,在应用程序初始化时全局设置它,就像 Rails.logger = Logger.STDOUT
之类的事情,但在整个代码中将 Rails.logger
视为常量。
这在任意静态类型语言中如何工作?我看到的问题是,依赖项注入的记录器变量不是常量,因为它是在运行时设置的,但出于所有意图和目的,一旦设置,它基本上就是一个常量。那么有没有一种方法可以将它视为代码中各处的常量,但能够在外部某个地方设置它呢?对于任意(甚至是虚构的)语言来说,这在伪代码中看起来会是什么样子?
还存在类似的其他东西,例如将 ORM 的适配器设置为特定数据库(如 Postgres),或 UI 库的渲染器等。我正在开发一种自定义静态类型编程语言(还有很长的路要走),并且正在寻找与依赖注入相关的关键模式,以便以某种理想的方式设置“全局常量”。
所以基本上,你如何设计(用伪代码或其他描述)一种处理结构的方法,使记录器就像一个静态常量?其中通用记录器实现代码在 X 库内部实现,记录器的具体实例在 Y 应用程序中实现,并注入到库中。
我正在想象做的(JavaScript 伪代码)基本上是这样的:
// log-library.js
let LOGGER
export function setLogger(logger) {
LOGGER = logger;
}
export function trace(message) {
LOGGER.trace(message)
}
export function warn(message) {
LOGGER.warn(message)
}
// app-config.js
import { setLogger } from 'log-library'
setLogger({
warn: (message) {
console.warn(message)
},
trace: (message) {
console.log(message)
}
})
// hello-world.js
import './app-config' // bootstrap
import { warn } from 'log-library'
warn('foo')
但是这种事情并没有我想象的静态保证。任意类型系统/编程语言是否可以使
LOGGER
在设置后以某种方式被视为常量,也许静态地/在编译时?也许类似这样的东西是可以实现的,这就是我的来源。
// log-library.js
let LOGGER
export function setLogger(logger) {
LOGGER = logger;
constantify LOGGER // COMPILER DIRECTIVE?
}
export function trace(message) {
LOGGER.trace(message)
}
export function warn(message) {
LOGGER.warn(message)
}
如果这种事情是可行的,那么在编译器中的实现中我应该确保考虑什么?
在单个模块内,不难强制执行属于该模块的常量必须由该模块初始化一次的规则;但执行程序范围常量必须在整个程序中只初始化一次的规则比较棘手。
在程序中的所有模块在执行开始时都已知的语言中(或者您可以对运行时稍后检测到的任何模块施加额外的约束),一种方法是跟踪每个模块,它初始化哪些程序范围常量以及它依赖哪些程序范围常量。这使得强制每个常量只初始化一次(或者最多一次,如果有默认/后备值可用),并确保模块以有效的顺序初始化(或者如果没有有效的值则预先拒绝程序)变得很简单。订单已存在)。