如何使用 van Laarhoven 镜头删除聚焦的 属性 对象?
How can I delete a focused property of an object with a van Laarhoven Lens?
通过我简单的镜头实现,我可以执行通常的修改、设置、获取和删除操作:
// Lens type
const Lens = f => ({runLens: f, [Symbol.toStringTag]: "Lens"});
const objLens = map => k =>
Lens(f => o =>
map(x => Object.assign({}, o, {[k]: x})) (f(o[k]))); // object lens
// Id type
const Id = x => ({runId: x, [Symbol.toStringTag]: "Id"});
const idMap = f => tx => Id(f(tx.runId)); // functor
// Const type
const Const = x => ({runConst: x, [Symbol.toStringTag]: "Const"});
const constMap = f => tx => Const(tx.runConst); // functor
// auxiliary function
const _const = x => y => x;
// MAIN
const o = {foo: "abc", bar: 123};
const get = objLens(constMap) ("foo").runLens(x => Const(x)) (o),
set = objLens(idMap) ("bat").runLens(_const(Id(true))) (o),
mod = objLens(idMap) ("foo").runLens(s => Id(s.toUpperCase())) (o),
del = objLens(idMap) ("foo").runLens(_const(Id(null))) (o); //*
console.log("get", get.runConst);
console.log("set", set.runId);
console.log("mod", mod.runId);
console.log("del", del.runId);
然而,delete
并不令人满意,因为我想删除整个 属性 而不是仅仅用空值替换值。
我怎样才能做到这一点?
*请注意,我通常会使用适当的 Option
类型来表示没有值。
一种可能的方法是定义一个表示删除的特殊类型:
const Lens = f => ({runLens: f, [Symbol.toStringTag]: "Lens"});
const objLens = map => k =>
Lens(f => o => map(x => {
if (x[Symbol.toStringTag] === "Deleter") {
const p = Object.assign({}, o);
delete p[k];
return p;
}
else
return Object.assign({}, o, {[k]: x});
}) (f(o[k])));
const Id = x => ({runId: x, [Symbol.toStringTag]: "Id"});
const idMap = f => tx => Id(f(tx.runId)); // functor
const _const = x => y => x;
// deletion type
const Deleter =
({get runDeleter() {return Deleter}, [Symbol.toStringTag]: "Deleter"});
// MAIN
const o = {foo: "abc", bar: 123};
const del = objLens(idMap) ("foo").runLens(_const(Id(Deleter))) (o);
console.log("del", del.runId);
但是,_const(Id(Deleter))
不是特别直观,而且看起来很老套。希望有更好的方法。
这就是我要做的:
// type Lens a b = forall f. Functor f => (b -> f b) -> a -> f a
// newtype Const b a = Const { getConst :: b } deriving Functor
const Const = getConst => ({ getConst, map: _ => Const(getConst) });
// newtype Identity a = Identity { runIdentity :: a } deriving Functor
const Identity = runIdentity => ({ runIdentity, map: f => Identity(f(runIdentity)) });
// remove :: String -> Object -> Object
const remove = key => ({ [key]: _, ...rest }) => rest;
// prop :: String -> Lens Object (Maybe Value)
const prop = key => fun => obj =>
fun(obj.hasOwnProperty(key) ? { fromJust: obj[key] } : null)
.map(data => Object.assign(remove(key)(obj), data && { [key]: data.fromJust }));
// get :: Lens a b -> a -> b
const get = lens => data => lens(Const)(data).getConst;
// modify :: Lens a b -> (b -> b) -> a -> a
const modify = lens => fun => data => lens(x => Identity(fun(x)))(data).runIdentity;
// set :: Lens a b -> b -> a -> a
const set = lens => value => modify(lens)(_ => value);
// del :: Lens a (Maybe b) -> a -> a
const del = lens => set(lens)(null);
// foo :: Lens Object (Maybe Value)
const foo = prop("foo");
console.log(get(foo)({ foo: 10, bar: 20 })); // { fromJust: 10 }
console.log(del(foo)({ foo: 10, bar: 20 })); // { bar: 20 }
如图所示,属性 镜头的类型签名如 foo
是 Lens Object (Maybe Value)
。这是有道理的,因为如果你尝试 get(foo)({ bar: 20 })
你应该什么也得不到。 del
函数适用于任何关注可能值的镜头,并将其值设置为空(即 null
)。
功劳归功于 Bergi,因为 可以对计算属性进行模式匹配。
通过我简单的镜头实现,我可以执行通常的修改、设置、获取和删除操作:
// Lens type
const Lens = f => ({runLens: f, [Symbol.toStringTag]: "Lens"});
const objLens = map => k =>
Lens(f => o =>
map(x => Object.assign({}, o, {[k]: x})) (f(o[k]))); // object lens
// Id type
const Id = x => ({runId: x, [Symbol.toStringTag]: "Id"});
const idMap = f => tx => Id(f(tx.runId)); // functor
// Const type
const Const = x => ({runConst: x, [Symbol.toStringTag]: "Const"});
const constMap = f => tx => Const(tx.runConst); // functor
// auxiliary function
const _const = x => y => x;
// MAIN
const o = {foo: "abc", bar: 123};
const get = objLens(constMap) ("foo").runLens(x => Const(x)) (o),
set = objLens(idMap) ("bat").runLens(_const(Id(true))) (o),
mod = objLens(idMap) ("foo").runLens(s => Id(s.toUpperCase())) (o),
del = objLens(idMap) ("foo").runLens(_const(Id(null))) (o); //*
console.log("get", get.runConst);
console.log("set", set.runId);
console.log("mod", mod.runId);
console.log("del", del.runId);
然而,delete
并不令人满意,因为我想删除整个 属性 而不是仅仅用空值替换值。
我怎样才能做到这一点?
*请注意,我通常会使用适当的 Option
类型来表示没有值。
一种可能的方法是定义一个表示删除的特殊类型:
const Lens = f => ({runLens: f, [Symbol.toStringTag]: "Lens"});
const objLens = map => k =>
Lens(f => o => map(x => {
if (x[Symbol.toStringTag] === "Deleter") {
const p = Object.assign({}, o);
delete p[k];
return p;
}
else
return Object.assign({}, o, {[k]: x});
}) (f(o[k])));
const Id = x => ({runId: x, [Symbol.toStringTag]: "Id"});
const idMap = f => tx => Id(f(tx.runId)); // functor
const _const = x => y => x;
// deletion type
const Deleter =
({get runDeleter() {return Deleter}, [Symbol.toStringTag]: "Deleter"});
// MAIN
const o = {foo: "abc", bar: 123};
const del = objLens(idMap) ("foo").runLens(_const(Id(Deleter))) (o);
console.log("del", del.runId);
但是,_const(Id(Deleter))
不是特别直观,而且看起来很老套。希望有更好的方法。
这就是我要做的:
// type Lens a b = forall f. Functor f => (b -> f b) -> a -> f a
// newtype Const b a = Const { getConst :: b } deriving Functor
const Const = getConst => ({ getConst, map: _ => Const(getConst) });
// newtype Identity a = Identity { runIdentity :: a } deriving Functor
const Identity = runIdentity => ({ runIdentity, map: f => Identity(f(runIdentity)) });
// remove :: String -> Object -> Object
const remove = key => ({ [key]: _, ...rest }) => rest;
// prop :: String -> Lens Object (Maybe Value)
const prop = key => fun => obj =>
fun(obj.hasOwnProperty(key) ? { fromJust: obj[key] } : null)
.map(data => Object.assign(remove(key)(obj), data && { [key]: data.fromJust }));
// get :: Lens a b -> a -> b
const get = lens => data => lens(Const)(data).getConst;
// modify :: Lens a b -> (b -> b) -> a -> a
const modify = lens => fun => data => lens(x => Identity(fun(x)))(data).runIdentity;
// set :: Lens a b -> b -> a -> a
const set = lens => value => modify(lens)(_ => value);
// del :: Lens a (Maybe b) -> a -> a
const del = lens => set(lens)(null);
// foo :: Lens Object (Maybe Value)
const foo = prop("foo");
console.log(get(foo)({ foo: 10, bar: 20 })); // { fromJust: 10 }
console.log(del(foo)({ foo: 10, bar: 20 })); // { bar: 20 }
如图所示,属性 镜头的类型签名如 foo
是 Lens Object (Maybe Value)
。这是有道理的,因为如果你尝试 get(foo)({ bar: 20 })
你应该什么也得不到。 del
函数适用于任何关注可能值的镜头,并将其值设置为空(即 null
)。
功劳归功于 Bergi,因为