on

# Typing Optics (4): Getters and Const

This is the 4th post documenting my tentative to add typings to my focused lens library.

So far, I have type definitions for

- base typeclasses/interfaces (Functor and Applicative)
- Isos
- Lenses
- Prisms
- Traversals
- Lens & Traversal Composition
`over`

(and by extension`set`

)

Next I’ll be adding typing for accessor functions `view`

, `preview`

, … this requires typing Getters.

### Gettings/Getters

For context, `focused`

defines four accessor functions.

`view(optic, state)`

is used to access a single focused value. (for now) The value must exist or it’ll throw an Error.`preview(optic, state)`

same as`view`

but returns`null`

if there is no value. If there are many focused values returns the first one.`toList(optic, state)`

returns all focused values (0 or more).`has(optic, state)`

returns false if there is no value under the focus, or true otherwise.

In Haskell, all the above functions take a `Getting`

as first parameter. The (simplified) definition is

```
type Getting r s a = (a -> Const r a) -> s -> Const r s
```

Again lens over tea explains in detail the motivation behind the above representation.

In this post I’ll be ..ahem.. focusing on the TypeScript implementation. For a short explanation, observe that the above definition is just a specialization of the other Optic definitions (for example replace `Getting r`

with `Lens`

and `Const r`

with some arbitrary Functor `f`

to obtain the Lens definition). We’re specializing the definition to `Const`

mainly to avoid updating a read only Optic.

So we need, somehow, to define the `Const`

Functor (and Applicative) as well as `Getting`

. And the representation has to be consistent with other Optics for the composition to still work and the compiler to infer the right types.

In Haskell, `Const`

is defined as a compile-time wrapper

```
newtype Const r a = Const { getConst :: r }
```

In other words, `Const`

holds a value of type `r`

but from the perspective of the type system it is both an `r`

and an `a`

. In TypeScript we could achieve a similar thing by using an intersection type:

```
type Const<R, A> = R & A;
```

Of course, the real value is `R`

. `A`

is just a *phantom type*.

Now for `Getting`

, the trick (or the hack) is to give `Getting`

a shape similar to other optics, but with additional constraints on the mapped types

```
interface Getting<R, S, A> {
readonly $type?: "Getting";
$applyOptic: <FA extends Const<R, A>, FS extends Const<R, S>>(
F: Applictive<A, S, FA, FS>,
f: Fn<A, FA>,
s: S
) => FS;
}
```

I beleive the `... extends Const<R, X>`

clauses are not effective with TypeScript bivariance on function parameters. But the `$type?: Getting`

ensures that we don’t actually set or update Getters (which can be created using the `to`

function). This requires of course that we add `Getting`

to the type of all other optics which is in fact true (they are all Getters).

```
interface Iso<S, T, A, B> {
readonly $type?: "Getting" & "Iso" & "Lens" & "Traversal";
// ...
}
// ... idem for all other optics
```

We need also to add an overload to `compose`

, because composing a Getter with another Optic should always result on a Getter. Since `Getting`

is now the most basic type (instead of `Traversal`

) we need to add the overload at the bottom after all the others

```
// ... all other overloads
function compose<S, T, A, B, X, Y>(
parent: Traversal<S, T, A, B>,
child: Traversal<A, B, X, Y>
): Traversal<S, T, X, Y>;
function compose<S, T, A, B, X, Y>(
parent: Getter<S, A>,
child: Getter<A, X>
): Getter<S, X>;
```

TypeScript compiler will traverse the overloads from the top (most specific optics) to the bottom (most general optics) and will choose the most specific result for our composition.

For `Getter`

s I just moved the `R`

type parameter down to the optic function. My assumption is that now `Getter<S,A>`

is a `Getting<R,S,A>`

for all `R`

s (which should be inferred by the compiler from the context)

```
interface Getter<S, A> {
readonly $type?: "Getting";
$applyOptic: <R, FA extends Const<R, A>, FS extends Const<R, S>>(
F: Functor<A, S, FA, FS>,
f: Fn<A, FA>,
s: S
) => FS;
}
```

`to`

converts a normal function to a `Getter`

(so it can be composed with other optics).

```
function to<S, A>(sa: Fn<S, A>): Getter<S, A> {
return {
$applyOptic(F, f, s) {
return f(sa(s)) as any;
}
};
}
```

For now I’m using sort of hack `as any`

to typecast the result, but it should be safe (because we know the result of applying `f`

is a `Const<R,A>`

which could be safely converted to `Const<R,S>`

since `A`

and `S`

are just phantom types).

### Accessor functions

We still need to implement the `Functor`

and `Applicative`

interfaces for `Const`

. But first we need to define the `Monoid`

interface (needed by `Const`

to be an Applicative)

```
interface Monoid<A> {
empty: () => A;
concat: (xs: A[]) => A;
}
```

The following function implements the Const Functor and Applicative

```
function ConstM(M) {
return {
map(f, k) {
return k;
},
pure: _ => M.empty(),
combine(_, ks) {
return M.concat(ks);
}
};
}
```

The definition of `map`

is trivial, we’e just forwarding our constant value (the `R`

in `Const<R,A>`

). For the Applicative definition, we’re relying on a given Monoid `M`

to accumulate the `R`

s. For example if we consider the `List`

Monoid

```
const List = {
empty: () => [],
concat(xss) {
return [].concat(...xss);
}
};
```

Then I can create a Const Applicative that accumulates all the values into an array

```
const ConstList = Const(List);
```

And here is the corresponding accessor function (as always we’re specifying the type parameters at the call site)

```
function toList<S, A>(l: Getting<A[], S, A>, s: S): A[] {
return l.$applyOptic(
ConstList as Applicative<A, S, Const<A[], A>, Const<A[], S>>,
x => [x] as Const<A[], A>,
s
);
}
```

Using `toList`

, for example, on a Traversal will `combine`

all the values inside using the `ConstList`

Applicative, which under the hoods uses the `List`

Monoid to concatenate the traversed values.

The other functions `view`

, `preview`

and `has`

all have a similar implementation, we use a special instance of the Monoid to provide a different behavior (cf link below for the full implementation).

One caveat is that

`view`

doesn’t actually work the same way as in Haskell. Since it can only get one value, if it’s used on a Traversal or Prism it’ll throw an Error (in Haskell the Monoid intance is automatically choosed by the compiler).

Another last minute caveat is that optional

`$type`

in Optic interface doesn’t seem to play nice with`strictNullChecks`

enabled. But probably fixable.

Next typings to add

- The Proxy interface
- More awkward multiple composition (composing more than 2 optics)