我正在为Javascript构建一个扩展的运行时类型系统,并且在实现参数多态时遇到一些麻烦。
我已经能够输入多态一阶函数:
const append = Fun("append :: [a] -> [a] -> [a]", xs => ys => xs.concat(ys));
append([1,2]) ([3,4]); // [1,2,3,4]
append([1,2]) (["a","b"]); // fails
顺序函数调用之间共享状态,强制类型注释中的所有a
被绑定到相同的具体类型。
然而,一旦我尝试输入更高阶的函数,事情变得更加困难:
const apply = Fun("apply :: (a -> b) -> a -> b", f => x => f(x));
const id = Fun("id :: a -> a", x => x);
apply(id) (2); // 2
问题是函数参数的a
/ b
与高阶函数的apply
/ const apply = Fun("apply :: (a -> b) -> a -> b", f => x => f(x) + "");
const inc = Fun("inc :: Number -> Number", x => x + 1);
apply(inc) (2); // "3"
不同,因为每个函数(在场景后面有一个应用陷阱的代理)都有它的在将范围变量绑定到具体类型的范围上。因此,使用显式类型转换的inc
的以下不需要的实现不会失败:
id
直观地,我试图在所有涉及的函数之间保持状态,但这很快就会变得混乱。
另一种方法是根据函数参数的类型注释调整高阶函数的类型注释。函数参数通常是单态的,如apply :: (a -> b) -> a -> b
apply f x = f x
inc :: Int -> Int
inc x = x + 1
:t apply inc -- Int -> Int (a becomes Int, b becomes Int)
id :: a -> a
id x = x
:t apply id -- b -> b (a becomes b, b remains b)
,但有时也是多态的,如sudo stop myapp
sleep 10
sudo start myapp
。在这里坚持我的例子是这两种情况的例子:
localhost:3001
好像我需要的只是我的类型注释的两个简单的替换规则 - 没有必要的额外状态。我知道我的推理很简单。问题是,为了获得可靠的参数多态性,它是否太天真。我是不是过分简化了?