我有一个纯功能的 Javascript 转换器实现,支持循环融合和短路。请注意,虽然我使用的是 JS,但这并不是理解问题的必要条件。只有类型很重要。
// ((a -> r) -> r) -> Cont r a
const Cont = k => ({run: k});
// (a -> b -> Cont b b) -> b -> [a] -> b
const foldk = f => init => xs => {
let acc = init;
for (let i = xs.length - 1; i >= 0; i--)
acc = f(xs[i]) (acc).run(id);
return acc;
};
// (a -> b) -> (b -> t b -> Cont (t b) (t b)) -> a -> t b -> Cont (t b) (t b)
const map = f => cons => x => acc =>
Cont(k => cons(f(x)) (acc).run(k));
// (a -> Bool) -> (a -> t a -> Cont (t a) (t a)) -> a -> t a -> Cont (t a) (t a)
const filter = p => cons => x => acc =>
Cont(k =>
p(x)
? cons(x) (acc).run(k)
: k(acc));
// (b -> c) -> (a -> b) -> a -> c
const comp = f => g => x => f(g(x));
const liftCont2 = f => x => y => Cont(k => k(f(x) (y)));
const unshift = x => xs => (xs.unshift(x), xs);
const includes = sub => s => s.includes(sub);
const len = arrayLike => arrayLike.length;
const id = x => x;
// (String -> t String -> Cont (t String) (t String))
// -> String -> t String -> Cont (t String) (t String)
const filterO = filter(includes("o"));
// (Number -> t Number -> Cont (t Number) (t Number))
// -> String -> t Number -> Cont (t Number) (t Number)
const mapLen = map(len);
// type error
const transducer = comp(filterO) (mapLen);
const reducer = transducer(liftCont2(unshift));
const main = foldk(reducer) ([]) (["f", "fo", "foo", "fooo"]);
console.log(main); // [2, 3, 4] with only a single traversal
如您所见,折叠有效,但不进行类型检查。揭示类型错误需要一些手动统一:
unify `comp(filterO)`:
(b -> c) -> (a -> b) -> a -> c
(String -> t String -> Cont (t String) (t String))
-> String -> t String -> Cont (t String) (t String)
yields
(a -> String -> t String -> Cont (t String) (t String))
-> a -> String -> t<String> -> Cont (t String) (t String)
unify result of `comp(filterO)` with `mapLen` (contravariant):
a -> String -> t String -> Cont (t String) (t String)
(Number -> t Number -> Cont (t Number) (t Number))
-> String -> t Number -> Cont (t Number) (t Number)
substitutes
a ~ Number -> t Number -> Cont (t Number) (t Number)
unify (covariant):
String -> t String -> Cont (t String) (t String)
String -> t Number -> Cont (t Number) (t Number)
这两个术语显然不能统一。
转换器是动态语言独有的概念并且无法键入,是我在统一过程中犯了错误还是我的转换器类型(map
/ filtert
)完全错误?