我正在使用需要验证用户输入的 Web UI。实现双拇指滑块时,使用带有“约束”更新的镜头时会出现问题。使用时lens.set
,需要验证新值,如果在给定的约束下它是无效的,则应该更新最接近的有效值。验证是多个简单标尺的组合。
我找不到合适的镜头来模拟这种“装饰”set
或modify
。
我正在使用以下模型,(使用monocle-ts)
import { Lens } from "monocle-ts";
interface State {
start: number;
end: number;
}
// assuming start < end always holds
const center = ({ start, end }: State): number => (start + end) / 2;
const length = ({ start, end }: State): number => end - start;
const startLens: Lens<State, number> = Lens.fromProp<State>()("start");
// Models direct drag start (left) thumb
const endLens: Lens<State, number> = Lens.fromProp<State>()("end");
// Models direct drag end (right) thumb
const centerLens: Lens<State, number> = new Lens<State, number>(
center,
(newCenter) => (s) => {
const l = length(s);
return { start: newCenter - l / 2, end: newCenter + l / 2 };
}
);
// Models direct drag slider, i.e. move the 'track' of two thumb slider, only chage center while prserve length.
const lengthLens: Lens<State, number> = new Lens<State, number>(
length,
(newLength) => (s) => {
const c = center(s);
return { start: c - newLength / 2, end: c + newLength / 2 };
}
);
// Models scale the range, change length while preserve center
在长度受到限制之前,每件事都可以正常工作。我想得到类似“步长”的东西,因此状态的长度需要是给定步长的整数倍。第一次尝试类似于以下内容:
const lengthStepLens = (step: number): Lens<State, number> =>
new Lens<State, number>(length, (newLength) => (s) => {
const stepLength = Math.round(newLength / step) * step;
const c = center(s);
return { start: c - stepLength / 2, end: c + stepLength / 2 };
});
尽管它似乎可以满足保持长度步长约束,但lengthStepLens
上面似乎违反了镜头定律:
get(set(a)(s)) = a
例如step = 1
,如果
const l = lengthStepLens(1);
const s: State = { start: 0.0, end: 1.0 };
const a: number = 0.9;
console.log(l.get(l.set(a)(s))); // prints 1, != 0.9
法律只适用于“有效”的新价值观。
另一种方法是使用单独的验证和校正逻辑,但我缺少使用镜头的两个优点:
从用户更新逻辑抽象实际状态表示(即组件接受通用状态
T
并Lens<T, number>
工作)组合多个约束(标尺)看起来就像组合镜头的提升版本。实际上还有其他约束,例如
min
,和的max
约束。组成约束看起来像是一些“单子”的镜头链接,也许像start
end
function merge<S, A>(la: Lens<S, A>, lb: Lens<S, A>): Lens<S, A> {
// assumes:
// forall s, if s is valid on both la and lb, la.get(s) === lb.get(s)
// forall a, s, lb.set(a)(la.set(a)(s)) is still valid on la
return new Lens(
(s) => la.get(s),
(a) => (s) => lb.set(a)(la.set(a)(s))
);
}
问题:
是否存在满足此问题的正确光学原理?
使用违反法律的镜头会发生什么?