Module Accessor
Types
type ('inner, 'outer, 'kind) t
=
{
f : w. ('kind, 'w) Accessor__.Dictionary.t -> ('inner, 'w) Accessor__.Mapping.t -> ('outer, 'w) Accessor__.Mapping.t;
}
Here is a summary of the type parameters in
(i -> a -> b, it -> at -> bt, c) Accessor.t
:i
is the output index typea
is the type of value that is readb
is the type of value that is writtenit
is the input index typeat
is the type of value that is read frombt
is the type of value resulting from a writec
is the kind of accessor
The representation is exposed, but not intended to be used directly.
module Simple : sig ... end
Accessors are commonly not indexed and don't need to support polymorphic updates. In such cases, it may be easier to read and write types in terms of
Simple.t
. Here is an example where the improvement by usingSimple.t
is significant:
module O : sig ... end
To use
Accessor
in your own code, it is recommended to add the following to your import.ml:
type at_least_one
=[
|
`at_least_one
]
type at_most_one
=[
|
`at_most_one
]
type coerce
=[
|
`coerce
]
type construct
=[
|
`construct
]
type get
=[
|
`get
]
type map
=[
|
`map
]
type constructor
= construct
type equality
=[
|
get
|
map
|
at_most_one
|
at_least_one
|
construct
|
coerce
]
type field
=[
|
get
|
map
|
at_most_one
|
at_least_one
]
type getter
=[
|
get
|
at_least_one
|
at_most_one
]
type isomorphism
=[
|
get
|
map
|
at_most_one
|
at_least_one
|
construct
]
type many
=[
|
get
|
map
]
type many_getter
= get
type mapper
= map
type nonempty
=[
|
get
|
map
|
at_least_one
]
type nonempty_getter
=[
|
get
|
at_least_one
]
type optional
=[
|
get
|
map
|
at_most_one
]
type optional_getter
=[
|
get
|
at_most_one
]
type variant
=[
|
get
|
map
|
at_most_one
|
construct
]
val (@>) : ('middle, 'outer, 'kind) t -> ('inner, 'middle, 'kind) t -> ('inner, 'outer, 'kind) t
a @> b
is the composition of the two accessorsa
andb
. From left to right, a chain of composed accessors goes from outermost to innermost values. The resulting accessor kind is determined by the least powerful argument. Here are a few examples:- An
isomorphism
composed with afield
is afield
. - A
field
composed with avariant
is anoptional
. - A
getter
composed with avariant
is anoptional_getter
.
It's normally more intuitive to think of the operations you need than to think of exactly which kind of accessor you are creating. For example, if you are trying to extract a value from a data structure using a
field
, you would probably useget
. However, if you compose thefield
with anoptional
,get
no longer makes sense; you must use something likeget_option
, instead.The non-operator name is
Accessor.compose
.- An
val (.@()) : 'at -> (Base.unit -> 'a -> 'b, Base.unit -> 'at -> 'bt, [> getter ]) t -> 'a
x.@(t)
extracts a single value fromx
as identified byt
. The non-operator name isAccessor.get
.
val (.@?()) : 'at -> (Base.unit -> 'a -> _, Base.unit -> 'at -> _, [> optional_getter ]) t -> 'a Base.option
x.@?(t)
extracts at most one value fromx
as identified byt
. The non-operator name isAccessor.get_option
.
val (.@*()) : 'at -> (Base.unit -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'a Base.list
x.@*(t)
extracts any number of values fromx
as identified byt
. The non-operator name isAccessor.to_list
.
val id : ('a, 'a, _) t
id
can be used as any kind of accessor. It is also the only way to summon anequality
.
Using accessors
Indices
module Index : sig ... end
An
Index.t
is a heterogeneous stack of values intended to serve as "breadcrumbs" that show how you got to some value currently being accessed inside a composite data structure. For example, if on the way in you traversed aMap.t
, one component of the index might be the key of the data being accessed.
module Subtyping : sig ... end
The
Subtyping
module contains all the types used for accessor subtyping. You shouldn't have to use it, but it's here for the documentation.
Functions
Getting and Folding
val get : (Base.unit -> 'a -> _, Base.unit -> 'at -> _, [> getter ]) t -> 'at -> 'a
get t at
reads a value fromat
.
val geti : ('i -> 'a -> _, Base.unit -> 'at -> _, [> getter ]) t -> 'at -> 'i Index.t * 'a
geti t at
reads a value and its index fromat
.
val get_option : (Base.unit -> 'a -> _, Base.unit -> 'at -> _, [> optional_getter ]) t -> 'at -> 'a Base.option
get_option t at
reads a value fromat
, if present.
val get_optioni : ('i -> 'a -> _, Base.unit -> 'at -> _, [> optional_getter ]) t -> 'at -> ('i Index.t * 'a) Base.option
get_optioni t at
reads a value and its index fromat
, if present.
val match_ : (Base.unit -> 'a -> _, Base.unit -> 'at -> 'bt, [> variant ]) t -> 'at -> ('a, 'bt) Base.Either.t
match_ t at
is likeget_option
, but in the failure case it may be able to give you the input data structure with a different type.
val matchi : ('i -> 'a -> _, Base.unit -> 'at -> 'bt, [> variant ]) t -> 'at -> ('i Index.t * 'a, 'bt) Base.Either.t
An indexed version of
match_
.
val to_list : (Base.unit -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> 'a Base.list
Extract all the values targetted by an accessor in some composite data structure into a list.
val to_listi : ('i -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> ('i Index.t * 'a) Base.list
An indexed version of
to_list
.
val to_array : (Base.unit -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> 'a Base.array
Extract all the values targetted by an accessor in some composite data structure into an array.
val to_arrayi : ('i -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> ('i Index.t * 'a) Base.array
An indexed version of
to_array
.
val fold : (Base.unit -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> init:'acc -> f:('acc -> 'a -> 'acc) -> 'acc
Fold across all the values targetted by an accessor with an accumulator.
val foldi : ('i -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> init:'acc -> f:('i Index.t -> 'acc -> 'a -> 'acc) -> 'acc
Indexed version of fold.
val iter : (Base.unit -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> f:('a -> Base.unit) -> Base.unit
Iterate over all the values targetted by an accessor, applying the function argument to each one.
val iteri : ('i -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> f:('i Index.t -> 'a -> Base.unit) -> Base.unit
An indexed version of
iter
.
val length : (Base.unit -> _ -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> Base.int
length t at
returns the number of targets inat
.
val is_empty : (Base.unit -> _ -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> Base.bool
is_empty t at
istrue
iff there are no targets inat
.
val sum : (module Base.Container.Summable with type t = 'sum) -> (Base.unit -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> f:('a -> 'sum) -> 'sum
sum (module Summable) t at ~f
returns the sum off a
for all targetsa
inat
.
val sumi : (module Base.Container.Summable with type t = 'sum) -> ('i -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> f:('i Index.t -> 'a -> 'sum) -> 'sum
sumi
is the indexed version ofsum
.
val count : (Base.unit -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> f:('a -> Base.bool) -> Base.int
count t at ~f
returns the number of targets inat
for whichf
evaluates to true.
val counti : ('i -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> f:('i Index.t -> 'a -> Base.bool) -> Base.int
counti
is the indexed version ofcount
.
val exists : (Base.unit -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> f:('a -> Base.bool) -> Base.bool
exists t at ~f
returnstrue
iff there is a target inat
for whichf
returnstrue
. This is a short-circuiting operation.
val existsi : ('i -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> f:('i Index.t -> 'a -> Base.bool) -> Base.bool
existsi
is the indexed version ofexists
.
val for_all : (Base.unit -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> f:('a -> Base.bool) -> Base.bool
for_all t at ~f
returnstrue
ifff
returnstrue
for all targets inat
. This is a short-circuiting operation.
val for_alli : ('i -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> f:('i Index.t -> 'a -> Base.bool) -> Base.bool
for_alli
is the indexed version offor_all
.
val find_map : (Base.unit -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> f:('a -> 'b Base.option) -> 'b Base.option
find_map
returns the first evaluation off
that returnsSome
.
val find_mapi : ('i -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> f:('i Index.t -> 'a -> 'b Base.option) -> 'b Base.option
find_mapi
is the indexed version offind_map
.
val find : (Base.unit -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> f:('a -> Base.bool) -> 'a Base.option
find t at ~f
returns the first target inat
for which the evaluation off
returnstrue
.
val findi : ('i -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> f:('i Index.t -> 'a -> Base.bool) -> ('i Index.t * 'a) Base.option
findi
is the indexed version offind
.
val min_elt : (Base.unit -> 'a -> _, Base.unit -> 'at -> _, [> nonempty_getter ]) t -> 'at -> compare:('a -> 'a -> Base.int) -> 'a
min_elt t at ~compare
usescompare
to compare each target inat
and returns the first target with the smallest value.
val min_elt_option : (Base.unit -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> compare:('a -> 'a -> Base.int) -> 'a Base.option
min_elt_option t at ~compare
usescompare
to compare each target inat
and returns the first target with the smallest value, if any.
val max_elt : (Base.unit -> 'a -> _, Base.unit -> 'at -> _, [> nonempty_getter ]) t -> 'at -> compare:('a -> 'a -> Base.int) -> 'a
max_elt t at ~compare
usescompare
to compare each target inat
and returns the first target with the largest value.
val max_elt_option : (Base.unit -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> compare:('a -> 'a -> Base.int) -> 'a Base.option
max_elt_option t at ~compare
usescompare
to compare each target inat
and returns the first target with the largest value, if any.
val hd : (Base.unit -> 'a -> _, Base.unit -> 'at -> _, [> nonempty_getter ]) t -> 'at -> 'a
hd t at
returns the first targetted element ofat
.
val hdi : ('i -> 'a -> _, Base.unit -> 'at -> _, [> nonempty_getter ]) t -> 'at -> 'i Index.t * 'a
An indexed version of
hd
.
val hd_option : (Base.unit -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> 'a Base.option
hd_option t at
returns the first targetted element ofat
, if any.
val hd_optioni : ('i -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> ('i Index.t * 'a) Base.option
An indexed version of
hd_option
.
val map_reduce : (Base.unit -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> empty:'r -> combine:('r -> 'r -> 'r) -> f:('a -> 'r) -> 'r
map_reduce t at ~empty ~combine ~f
appliesf
to each targetted value inat
and combines the results usingcombine
. The result isempty
if there were no values.empty
andcombine
are expected to satisfy the following properties:combine empty a = a
combine a empty = a
combine (combine a b) c = combine a (combine b c)
val map_reducei : ('i -> 'a -> _, Base.unit -> 'at -> _, [> many_getter ]) t -> 'at -> empty:'r -> combine:('r -> 'r -> 'r) -> f:('i Index.t -> 'a -> 'r) -> 'r
An indexed version of
map_reduce
.
val map_reduce_nonempty : (Base.unit -> 'a -> _, Base.unit -> 'at -> _, [> nonempty_getter ]) t -> 'at -> combine:('r -> 'r -> 'r) -> f:('a -> 'r) -> 'r
map_reduce_nonempty t at ~combine ~f
appliesf
to each targetted value inat
and combines the results usingcombine
.combine
is expected to satisfy the property:combine (combine a b) c = combine a (combine b c)
.
val map_reduce_nonemptyi : ('i -> 'a -> _, Base.unit -> 'at -> _, [> nonempty_getter ]) t -> 'at -> combine:('r -> 'r -> 'r) -> f:('i Index.t -> 'a -> 'r) -> 'r
An indexed version of
map_reduce_nonempty
.
Modifying
val map : (Base.unit -> 'a -> 'b, Base.unit -> 'at -> 'bt, [> mapper ]) t -> 'at -> f:('a -> 'b) -> 'bt
map t at ~f
appliesf
to each targetted value insideat
, replacing it with the result.
val mapi : ('i -> 'a -> 'b, Base.unit -> 'at -> 'bt, [> mapper ]) t -> 'at -> f:('i Index.t -> 'a -> 'b) -> 'bt
mapi
is the indexed version ofmap
.
val folding_map : (Base.unit -> 'a -> 'b, Base.unit -> 'at -> 'bt, [> many ]) t -> 'at -> init:'acc -> f:('acc -> 'a -> 'acc * 'b) -> 'bt
folding_map
is a version ofmap
that threads an accumulator through calls tof
.
val folding_mapi : ('i -> 'a -> 'b, Base.unit -> 'at -> 'bt, [> many ]) t -> 'at -> init:'acc -> f:('i Index.t -> 'acc -> 'a -> 'acc * 'b) -> 'bt
folding_mapi
is the indexed version offolding_map
.
val fold_map : (Base.unit -> 'a -> 'b, Base.unit -> 'at -> 'bt, [> many ]) t -> 'at -> init:'acc -> f:('acc -> 'a -> 'acc * 'b) -> 'acc * 'bt
fold_map
is a combination offold
andmap
that threads an accumulator through calls tof
.
Monadic and Applicative functions
Signatures
module Functor : sig ... end
module Applicative : sig ... end
module Applicative_without_return : sig ... end
module Monad : sig ... end
The monad signatures differ from the applicative ones in that some of the functions have an optional
how
argument. They always default to`Sequential
, which is the behavior that interleaves side effects with monadic effects. If you override this argument to`Parallel
then all the side effects are performed up front, and then the results are combined.
module Monad_without_return : sig ... end
Functors
module Of_functor : functor (F : sig ... end) -> Functor.S with type 'a t := 'a F.t
Of_functor
,Of_functor2
, andOf_functor3
generate map-like functions that work under some "functor", which is like a monad or applicative, except that it only supportsmap
.
module Of_functor2 : functor (F : sig ... end) -> Functor.S2 with type ('a, 'd) t := ('a, 'd) F.t
module Of_functor3 : functor (F : sig ... end) -> Functor.S3 with type ('a, 'd, 'e) t := ('a, 'd, 'e) F.t
module Of_applicative : functor (A : sig ... end) -> Applicative.S with type 'a t := 'a A.t
Of_applicative
andOf_applicative2
can be used to generate map-like functions that can use applicative effects. See alsoOf_monad
, which gives more control over the relationship between side effects and monadic effects.
module Of_applicative2 : functor (A : sig ... end) -> Applicative.S2 with type ('a, 'e) t := ('a, 'e) A.t
See
Of_applicative
.
module Of_monad : functor (M : sig ... end) -> Monad.S with type 'a t := 'a M.t
Of_monad
is similar toOf_applicative
. There are two differences.
module Of_monad2 : functor (M : sig ... end) -> Monad.S2 with type ('a, 'e) t := ('a, 'e) M.t
See
Of_monad
.
module Of_applicative_without_return : functor (A : sig ... end) -> Applicative_without_return.S with type 'a t := 'a A.t
Like
Of_applicative
, but withoutreturn
.
module Of_applicative_without_return2 : functor (A : sig ... end) -> Applicative_without_return.S2 with type ('a, 'e) t := ('a, 'e) A.t
Like
Of_applicative2
, but withoutreturn
.
module Of_applicative_without_return3 : functor (A : sig ... end) -> Applicative_without_return.S3 with type ('a, 'd, 'e) t := ('a, 'd, 'e) A.t
module Of_monad_without_return : functor (A : sig ... end) -> Monad_without_return.S with type 'a t := 'a A.t
Like
Of_monad
, but withoutreturn
.
module Of_monad_without_return2 : functor (A : sig ... end) -> Monad_without_return.S2 with type ('a, 'e) t := ('a, 'e) A.t
Like
Of_monad2
, but withoutreturn
.
module Of_monad_without_return3 : functor (A : sig ... end) -> Monad_without_return.S3 with type ('a, 'd, 'e) t := ('a, 'd, 'e) A.t
Recursive update
val transform : (Base.unit -> 'a -> 'b, Base.unit -> 'a -> 'b, [> mapper ]) t -> 'a -> f:('b -> 'b) -> 'b
transform t a ~f
appliesf
everywhere it can inside ofa
once. It operates from the bottom up in one pass.t
is used to find the children at each level, where the children are expected to have the same type as their parent.
val rewrite : (Base.unit -> 'a -> 'b, Base.unit -> 'a -> 'b, [> mapper ]) t -> 'a -> f:('b -> 'a Base.option) -> 'b
rewrite t a ~f
applies the rewrite rulef
everywhere it can inside ofa
until it cannot be applied anywhere else. It operates from the bottom up, retrying subtrees each time a rule is applied successfully.t
is used to find the children at each level, where the children are expected to have the same type as their parent.
Type equality
module Identical : sig ... end
An
Identical.t
is similar to aType_equal.t
, but it relates two pairs of types with each other instead of merely two types. It is a more natural way of using an equality accessor thanType_equal.t
would be, since you only need to match on one constructor.
val identical : (Base.unit -> 'a -> 'b, Base.unit -> 'at -> 'bt, [> equality ]) t -> ('a, 'b, 'at, 'bt) Identical.t
An equality is more powerful even than an isomorphism. It can be used to prove that the types are equal using the
identical
function.
Construction
val construct : (_ -> _ -> 'b, _ -> _ -> 'bt, [> constructor ]) t -> 'b -> 'bt
construct
goes the opposite way to most access patterns. It allows you to construct a composite data structure without reading from one.
Custom mappings
module Equality : sig ... end
An
equality
can transform any mapping. There is no need for you to provide any functionality of your own.
module Isomorphism : sig ... end
module Field : sig ... end
module Variant : sig ... end
module Constructor : sig ... end
module Getter : sig ... end
module Optional : sig ... end
module Optional_getter : sig ... end
module Nonempty : sig ... end
module Nonempty_getter : sig ... end
module Many : sig ... end
module Many_getter : sig ... end
module Mapper : sig ... end
Creating accessors
Avoiding the value restriction
Deriving accessors
"Well behaved" accessors
Creation functions
Field accessors
val field : get:('at -> 'a) -> set:('at -> 'b -> 'bt) -> ('i -> 'a -> 'b, 'i -> 'at -> 'bt, [< field ]) t
field ~get ~set
creates a field accessor. A field accesses exactly one value within a composite data structure. For the field to be well behaved,get
andset
should satisfy the following properties:get (set at a) = a
set at (get at) = at
set (set at a) b = set at b
val field' : ('at -> 'a * ('b -> 'bt)) -> ('i -> 'a -> 'b, 'i -> 'at -> 'bt, [< field ]) t
field'
is the same asfield
, just with a slightly different interface.field
is usually more convenient to use, butfield'
can be useful to allowget
andset
to share the computation of finding the location to modify.
val of_field : ([> `Set_and_create ], 'r, 'a) Base.Field.t_with_perm -> ('i -> 'a -> 'a, 'i -> 'r -> 'r, [< field ]) t
A
Field.t
is sufficient to define a field accessor, but the resulting accessor might not be as polymorphic as it could have been if defined by hand or using@@deriving accessor
.
val fieldi : get:('at -> 'i * 'a) -> set:('at -> 'b -> 'bt) -> (('i * 'it) -> 'a -> 'b, 'it -> 'at -> 'bt, [< field ]) t
fieldi
is the indexed version offield
.
val fieldi' : ('at -> 'i * 'a * ('b -> 'bt)) -> (('i * 'it) -> 'a -> 'b, 'it -> 'at -> 'bt, [< field ]) t
fieldi'
is the indexed version offield'
.
val of_fieldi : ([> `Set_and_create ], 'r, 'a) Base.Field.t_with_perm -> ((Base.string * 'it) -> 'a -> 'a, 'it -> 'r -> 'r, [< field ]) t
A
Field.t
is sufficient to define an indexed field accessor, where the index is the name of the field as a string. The resulting accessor might not be as polymorphic as it could have been if defined by hand or using@@deriving accessor
.
Variant accessors
val variant : match_:('at -> ('a, 'bt) Base.Either.t) -> construct:('b -> 'bt) -> ('i -> 'a -> 'b, 'i -> 'at -> 'bt, [< variant ]) t
variant ~match_ ~construct
creates a variant accessor. A variant accesses at most one value within a composite data structure, and if it does access a value then that value is representative of the entire data structure. A well behaved variant should satisfy the following properties:match_ (construct a) = First a
- if
match_ at = First a
thenconstruct a = at
- if
match_ at = Second bt
thenat = bt
val varianti : match_:('at -> ('i * 'a, 'bt) Base.Either.t) -> construct:('b -> 'bt) -> (('i * 'it) -> 'a -> 'b, 'it -> 'at -> 'bt, [< variant ]) t
varianti
is the indexed version ofvariant
.
Optional accessors
val optional : match_:('at -> ('a, 'bt) Base.Either.t) -> set:('at -> 'b -> 'bt) -> ('i -> 'a -> 'b, 'i -> 'at -> 'bt, [< optional ]) t
optional ~match_ ~set
creates an optional accessor. An optional accesses at most one value within a composite data structure. A well behaved optional should satisfy the following properties:match_ (set at a) = Either.First.map (match_ at) ~f:(const a)
- if
match_ at = First a
thenset at a = at
- if
match_ at = Second bt
thenat = bt
andset at b = at
set (set at a) b = set at b
val optional' : ('at -> ('a * ('b -> 'bt), 'bt) Base.Either.t) -> ('i -> 'a -> 'b, 'i -> 'at -> 'bt, [< optional ]) t
optional'
is the same asoptional
, just with a slightly different interface.optional
is usually more convenient to use, butoptional'
can be useful to allowmatch_
andset
to share the computation of finding the location to modify.
val optionali : match_:('at -> ('i * 'a, 'bt) Base.Either.t) -> set:('at -> 'b -> 'bt) -> (('i * 'it) -> 'a -> 'b, 'it -> 'at -> 'bt, [< optional ]) t
optionali
is the indexed version ofoptional
.
val optionali' : ('at -> ('i * 'a * ('b -> 'bt), 'bt) Base.Either.t) -> (('i * 'it) -> 'a -> 'b, 'it -> 'at -> 'bt, [< optional ]) t
optionali'
is the indexed version ofoptional'
.
val filter_index : ('i Index.t -> Base.bool) -> ('i -> 'a -> 'a, 'i -> 'a -> 'a, [< optional ]) t
filter_index predicate
accesses the entire value if its index satisfiespredicate
, otherwise it accesses nothing. Compose it with a many accessor to access a subset of values.
val filter_map_index : ('i Index.t -> 'j Index.t Base.option) -> ('j -> 'a -> 'a, 'i -> 'a -> 'a, [< optional ]) t
filter_map_index f
is likefilter_index
, but it can also modify the indices.
Isomorphism accessors
val isomorphism : get:('at -> 'a) -> construct:('b -> 'bt) -> ('i -> 'a -> 'b, 'i -> 'at -> 'bt, [< isomorphism ]) t
isomorphism ~get ~construct
creates an isomorphism accessor. An isomorphism accesses exactly one value which exactly represents the entire data structure. A well behaved isomorphism should satisfy the following properties:get (construct b) = b
construct (get at) = at
val isomorphismi : get:('at -> 'i * 'a) -> construct:('b -> 'bt) -> (('i * 'it) -> 'a -> 'b, 'it -> 'at -> 'bt, [< isomorphism ]) t
isomorphismi
is the indexed version ofisomorphism
.
val map_index : ('i Index.t -> 'j Index.t) -> ('j -> 'a -> 'b, 'i -> 'a -> 'b, [< isomorphism ]) t
map_index f
appliesf
to the the indices that pass through it in a chain of composed accessors.
Mapper accessors
val mapper : ('at -> f:('a -> 'b) -> 'bt) -> ('i -> 'a -> 'b, 'i -> 'at -> 'bt, [< mapper ]) t
mapper map
creates a mapper accessor. A mapper can modify values inside a composite data structure, but cannot read anything out. A well behaved mapper should satisfy the following properties:map at ~f:Fn.id = at
map at ~f:(Fn.compose f g) = map (map at ~f:g) ~f
Many accessors
val many : ('at -> ('bt, 'a, 'b) Many.t) -> ('i -> 'a -> 'b, 'i -> 'at -> 'bt, [< many ]) t
many traverse
creates amany
accessor. Amany
accesses any number of values within a composite data structure.To define a
many
accessor, you must useAccessor.Many.t
, which is an applicative. You should traverse the data structure as necessary, and each time you reach a value that should be accessed, applyAccessor.Many.access
to it.Here is an example of using
many
to define an accessor that reaches all the elements of a list:Accessor.many (fun at -> Accessor.Many.all (List.map at ~f:Accessor.Many.access))
A well behaved many should satisfy the same properties as a well behaved mapper, but generalized for an applicative setting. The properties themselves are uselessly complicated when written out, but here they are anyway.
A
andB
are assumed to have theof_many
function generated byMany.Of_applicative
, andCompose
is assumed to be some functor behaving likeApplicative.Compose
that also usesMany.Of_applicative
to generate anof_many
function.A.of_many (traverse at) ~access:A.return = A.return at
Compose(A)(B).of_many (traverse at) ~access:(fun a -> A.map (g a) ~f) = A.map (A.of_many (traverse at) ~access:g) ~f:(fun at -> B.of_many (traverse at) ~access:f)
Nonempty accessors
val nonempty : ('at -> ('bt, 'a, 'b) Nonempty.t) -> ('i -> 'a -> 'b, 'i -> 'at -> 'bt, [< nonempty ]) t
nonempty traverse
creates a nonempty accessor. A nonempty accesses a nonzero number of values within a composite data structure.To define a
nonempty
accessor, you must useAccessor.Nonempty.t
, which is an applicative lackingreturn
. You should traverse the data structure as necessary, and each time you reach a value that should be accessed, applyAccessor.Nonempty.access
to it.Here is an example of using
nonempty
to define an accessor that reaches both components of a tuple:Accessor.nonempty (fun (a, b) -> let open Accessor.Nonempty.Let_syntax in let%map_open a = access a and b = access b in a, b)
A well behaved nonempty should satisfy the second property of a well behaved mapper, but generalized for an applicative setting. The property itself is uselessly complicated when written out, but here it is anyway.
A
andB
are assumed to have theof_nonempty
function generated byNonempty.Of_applicative_without_return
, andCompose
is assumed to be some functor behaving likeApplicative_without_return.Compose
that also usesNonempty.Of_applicative_without_return
to generate anof_nonempty
function.Compose(A)(B).of_nonempty (traverse at) ~access:(fun a -> A.map (g a) ~f) = A.map (A.of_nonempty (traverse at) ~access:g) ~f:(fun at -> B.of_nonempty (traverse at) ~access:f)
val nonemptyi : ('at -> ('bt, 'i * 'a, 'b) Nonempty.t) -> (('i * 'it) -> 'a -> 'b, 'it -> 'at -> 'bt, [< nonempty ]) t
nonemptyi
is the indexed version ofnonempty
.
Getter accessors
Optional getter accessors
val optional_getter : ('at -> 'a Base.option) -> ('i -> 'a -> _, 'i -> 'at -> _, [< optional_getter ]) t
optional_getter get
creates an optional getter accessor. An optional getter reads at most one value from a composite data structure. There are no properties necessary for an optional getter to be well behaved.
val optional_getteri : ('at -> ('i * 'a) Base.option) -> (('i * 'it) -> 'a -> _, 'it -> 'at -> _, [< optional_getter ]) t
optional_getteri
is the indexed version ofoptional_getter
.
Many getter accessors
val many_getter : ('at -> 'a Many_getter.t) -> ('i -> 'a -> 'b, 'i -> 'at -> 'bt, [< many_getter ]) t
many_getter map_reduce
creates a many_getter accessor. A many getter reads any number of values from a composite data structure. There are no properties necessary for a many getter to be well behaved.To define a many_getter, you must use the
Many_getter
interface. LikeMany
, it has anaccess
function to designate which values to access. UnlikeMany
, instead of an applicative interface, it hasempty
andappend
(or( @ )
) functions.Here is an example of defining a getter that reads all the elements of a list:
Accessor.many_getter (fun at -> Accessor.Many_getter.of_list (List.map at ~f:Accessor.Many_getter.access))
val many_getteri : ('at -> ('i * 'a) Many_getter.t) -> (('i * 'it) -> 'a -> 'b, 'it -> 'at -> 'bt, [< many_getter ]) t
many_getteri
is the indexed version ofmany_getter
.
Nonempty getter accessors
val nonempty_getter : ('at -> 'a Nonempty_getter.t) -> ('i -> 'a -> 'b, 'i -> 'at -> 'bt, [< nonempty_getter ]) t
nonempty_getter map_reduce
creates a nonempty getter accessor. A nonempty getter reads at least one value from a composite data structure. There are no properties necessary for a nonempty getter to be well behaved.To define a nonempty_getter, you must use the
Nonempty_getter
interface. LikeNonempty
, it has anaccess
function to designate which values to access. UnlikeNonempty
, instead of an applicative style interface, it has anappend
(or( @ )
) function.Here is an example of defining a getter that reads both of the components of a tuple:
Accessor.nonempty_getter (fun (a, b) -> Accessor.Nonempty_getter.(access a @ access b))
val nonempty_getteri : ('at -> ('i * 'a) Nonempty_getter.t) -> (('i * 'it) -> 'a -> 'b, 'it -> 'at -> 'bt, [< nonempty_getter ]) t
nonempty_getteri
is the indexed version ofnonempty_getter
.
Constructor accessors
val constructor : ('b -> 'bt) -> (_ -> _ -> 'b, _ -> _ -> 'bt, [< constructor ]) t
constructor construct
creates a constructor accessor. A constructor creates a composite data structure from an argument. There are no properties necessary for a constructor to be well behaved.
val of_variant : ('b -> 'bt) Base.Variant.t -> (_ -> _ -> 'b, _ -> _ -> 'bt, [< constructor ]) t
A
Variant.t
is not sufficient to define a variant accessor, but is at least sufficient to define a constructor accessor.
Transforming accessors
val invert : (Base.unit -> 'a -> 'b, Base.unit -> 'at -> 'bt, [> isomorphism ]) t -> ('i -> 'bt -> 'at, 'i -> 'b -> 'a, [< isomorphism ]) t
Turn an isomorphism around.
invert (isomorphism ~get:f ~construct:g)
isisomorphism ~get:g ~construct:f
.
val getter_to_constructor : (Base.unit -> 'a -> _, Base.unit -> 'at -> _, [> getter ]) t -> (_ -> _ -> 'at, _ -> _ -> 'a, [< constructor ]) t
Turn a getter into a constructor.
getter_to_constructor (getter f)
isconstructor f
.
val constructor_to_getter : (_ -> _ -> 'b, _ -> _ -> 'bt, [> constructor ]) t -> ('i -> 'bt -> _, 'i -> 'b -> _, [< getter ]) t
Turn a constructor into a getter.
constructor_to_getter (constructor f)
isgetter f
.
val many_to_list_field : (Base.unit, 'a, 'at, [> many ]) Simple.t -> (_, 'a Base.list, 'at, [< field ]) Simple.t
Given a
many
accessor, generate afield
accessor that accesses all the elements that would be accessed by themany
accessor in the form of a list. When replacing, if the list is too short then later elements in the data structure are left alone, and if the list is too long then extraneous elements are not used.The resulting accessor is only well-behaved if you preserve the length of the list across getting and setting.