Skip to content

Latest commit

 

History

History
33 lines (23 loc) · 2.57 KB

README.md

File metadata and controls

33 lines (23 loc) · 2.57 KB

jq-optics

A toy implementation of the jq language using optics.

Warning

This is a work in progress. There is no parser yet, let alone a CLI. The source code might be hard to understand if you're not already familiar with lens.

jq :: Env -> Filter -> IndexedJourney' Path Value Value

A traversal is a view of several disjoint targets in a data structure. Traversals obey laws that ensure the same target can't be visited twice, and are hard to compose side-by-side. Journeys are lawless traversals that are allowed to visit the same target multiple times, so that all the targets are updated sequentially. This allows representing jq filters as monomorphic journeys on JSON values.

In the CPS / van Laarhoven representation of optics, journeys are obtained by promoting Traversal's constraint from Applicative to Monad (and the indexed variant is obtained as usual):

type Journey s t a b = forall m. Monad m => (a -> m b) -> s -> m t
type IndexedJourney i s t a b = forall p m. (Indexable i p, Monad m) => p a (m b) -> s -> m t

More concretely, Journey s t a b is isomorphic to s -> Free (PStore a b) t, where Free is the free monad construction and

data PStore a b t = PStore a (b -> t)

So a journey takes an initial value and produces a series of "interaction points" that each produce a value (and possibly an index) and expect a replacement value, and in the end returns a final value that is the result of performing the replacements on the initial value. Journeys are composed side-by-side straightforwardly using (>=>). Every traversal is a journey, and every journey is a setter, but journeys have no subtyping relationship with folds. Most of the lens combinators for folds have to be reimplemented and they behave slightly differently.

Note

This project does not aim to be an exact clone of jq; in fact, the implementation departs from jq's in several ways:

  • Assignment is sequential, i.e. (a, b) = c is equivalent to (a = c) | (b = c). See [0, 2, 3] | (.[.[0]], .[.[0]]) = 1. On the other hand, this means that .[a, b] is not in general equivalent to .[a], .[b].
  • |= has cartesian product semantics instead of jq's behaviour, which is to take the first produced value, or delete the entry if no values are produced.
  • Assigning to a non-path-expression is a no-op instead of an error.