bChiquet/elm-accessors/2.1.1

NOTE: the latest version is 3.0.0

Accessors

Relations are interfaces to document the relation between two data structures. For convenience, we'll call the containing structure super, and the contained structure sub. What a Relation claims is that a super is referencing a sub in some way.

Relations are the building blocks of accessors. An accessor is a function that expects a Relation and builds a new relation with it. Accessors are composable, which means you can build a chain of relations to manipulate nested structures without handling the packing and the unpacking.

Action functions

Action functions are functions that take an accessor and let you perform a specific action on data using that accessor.

get : (Relation sub sub sub -> Relation super sub wrap) -> super -> wrap

The get function takes:

  • An accessor,
  • A datastructure with type super and returns the value accessed by that combinator.
get (foo << bar) myRecord 
set :
(Relation sub sub sub -> Relation super sub wrap)
-> sub
-> super
-> super

The set function takes:

  • An accessor,
  • A value of the type sub,
  • A datastructure with type super and it returns the data structure, with the accessible field changed to be the set value.
set (foo << bar) "Hi!" myRecord
(Relation sub sub sub -> Relation super sub wrap)
-> (sub -> sub)
-> super
-> super

The over function takes:

  • An accessor,
  • A function (sub -> sub),
  • A datastructure with type super and it returns the data structure, with the accessible field changed by applying the function to the existing value.
over (foo << qux) ((+) 1) myRecord

Build accessors

Accessors are built using these functions:

(super -> sub)
-> ((sub -> sub) -> super -> super)
-> Relation sub reachable wrap
-> Relation super reachable wrap

This function lets you build an accessor for containers that have a 1:1 relation with what they contain, such as a record and one of its fields:

foo : Relation field sub wrap -> Relation {rec | foo : field} sub wrap
foo =
  makeOneToOne
    .foo
    (\change rec -> {rec | foo = change rec.foo })
((sub -> subWrap) -> super -> superWrap)
-> ((sub -> sub) -> super -> super)
-> Relation sub reachable subWrap
-> Relation super reachable superWrap

This function lets you build an accessor for containers that have a 1:N relation with what they contain, such as List (0-N cardinality) or Maybe (0-1). E.g.:

onEach : Relation elem sub wrap -> Relation (List elem) sub (List wrap)
onEach =
  makeOneToN
    List.map
    List.map

n.b. implementing those is usually considerably simpler than the type suggests.

Relation

type Relation super sub wrap

A Relation super sub wrap is a type describing how to interact with a sub data when given a super data.

The wrap exists because some types can't ensure that get will return a sub. For instance, Maybe sub may not actually contain a sub. Therefore, get returns a wrap which, in that example, will be Maybe sub

Implementation: A relation is a banal record storing a get function and an over function.