1

我正在使用需要验证用户输入的 Web UI。实现双拇指滑块时,使用带有“约束”更新的镜头时会出现问题。使用时lens.set,需要验证新值,如果在给定的约束下它是无效的,则应该更新最接近的有效值。验证是多个简单标尺的组合。

我找不到合适的镜头来模拟这种“装饰”setmodify


我正在使用以下模型,(使用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

法律只适用于“有效”的新价值观。


另一种方法是使用单独的验证和校正逻辑,但我缺少使用镜头的两个优点:

  1. 从用户更新逻辑抽象实际状态表示(即组件接受通用状态TLens<T, number>工作)

  2. 组合多个约束(标尺)看起来就像组合镜头的提升版本。实际上还有其他约束,例如min,和的max约束。组成约束看起来像是一些“单子”的镜头链接,也许像startend

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))
  );
}

问题:

  • 是否存在满足此问题的正确光学原理?

  • 使用违反法律的镜头会发生什么?

4

0 回答 0