发表于: 10/9/2022
:::default no-icon 气温骤降,天冷的似乎要把脑子都冻僵,不禁感叹当初为什么选了一个网面的椅子。 :::
类型,类型
在动态语言中扑腾久了,作为一个短时记忆倾向的人类,就会不由得怀念起确定的类型给人带来的温暖的安心感,从这个角度来说,动态的类型,其强也好弱也好,都很难说是’有一个类型’——既不能在按下[.]{.kbd}时提供可用的成员,也不能在运行前确定这个’东西’到底会是什么样子。
在静态语言里扑腾久了,就有点烦,屁大点事也要重新声明一个类型。
普通的来说,也很难说现有类型系统的极限普遍迫切的需要突破,大部分的需求都可以被<T, U, ...>
这样的类型参数解决,甚至说这些参数可以有提供的实参自动推导出来不需要再显式填写。
但是当你有了一些稀奇古怪的抽象,你想写最少的代码,你想把写过的东西随意组合一下形成新的东西,你就会发现这玩意既不直观也不方便,很快就直线撞上了边界——当然你总有办法绕过去,但那就是另外的东西了。
快给我变
最近是有些想法,比如抽象出一个允许施加/撤销修改的数值类型, 通过依次计算修改来得出最终的值
interface Modifiable{
object Value;
Modifiers[] Modifiers;
}
interface Modifier{
object Calculate(object val);
}
但在强类型语言里直接塞一个object进去显然不是什么好想法。 很容易想到的,给Value加一个类型
interface Modifiable<T>{
T Value;
Modifier<T>[] Modifiers;
}
interface Modifier<T>{
T Calculate(T val);
}
但是还不太够的感觉,Modifier显然是针对特定Modifiable,即便是value的类型相同,也未必会想要ValueA: Modifiable<int>
能够使用对ValueB: Modifiable<int>
编写的Modifier<int>
。
基于此,我理解了T: SomeInterface<T>
的意义。
class ValueA: Modifiable<ValueA>{
ValueA Value;
Modifier<ValueA>[] Modifiers;
}
class AMod: Modifier<ValueA>{ ... }
但是这样仍然排除不了之前的用法,所以在接口上添加如是约束
interface Modifiable<T>
where T: Modifiable<T>
{
T Value;
Modifier<T>[] Modifiers;
}
interface Modifier<T>
where T: Modifiable<T>
{
T Calculate(T val);
}
:::default 后期修订: 现在回过头来想想这个Value其实就没有任何意义了,而要添加一个类型泛型又很麻烦地需要每次多标注一个类型,还是得靠基础设施。 :::
若是有心继续深挖还可以进一步细化约束内部数据的类型
interface Modifiable<TSelf, TData>
where TSelf: Modifiable<TSelf, TData>
{
TData Value;
Modifier<TSelf, TData>[] Mods;
}
interface Modifier<TModifiable, TData>
where TModifiable: Modifiable<TModifiable,TData>
{
TData Calculate(TData lastCalculated);
}
理想情况下,对于Modifier的具体实现,应当从TModifiable中获取TData,而现有工具已经覆盖不到这种需求了,况且在本例来讲,分出这个TData的意义不大。
幻想时间
比如这种混乱的写法
interface Modifier<TModifiable<TModifiable, TData>>
似乎是叫高阶类型来着,不过通常举出的例子通常关心<T<_>>
高阶的类型本身,不过理应顺带可以提取出内层的类型。
也罢,我这半吊子函数式爱好者还是要学习一个。