An experimental optics library for OCaml. See accessor library for a more comprehensive implementation.
% dune utop
# open Optics;;
# view _1 (42, 3.14);;
- : int = 42
# (42, (3.14, "foobar")) |> view (_2 // _1);;
- : float = 3.14
# (42, (3.14, "foobar")).%[_2 // _1];;
- : float = 3.14
# set _Some "foobar" (Some 42);;
- : string option = Option.Some "foobar"
# (42, (None, "foobar")).%[_2 // _1 // _Some] <- 0;;
- : int * (int option * string) = (42, (Option.None, "foobar"))
# (Some [1; 2; 3], 3.14) |> _1 // _Some // sets' List.map %~ succ;;
- : int list option * float = (Option.Some [2; 3; 4], 3.14)
# _Some // to_' List.length;;
- : (_[< `Affine_fold ], '_weak1 list option -> '_weak1 list option,
int -> int)
t
= <fun>
# view _Some;;
Error: This expression has type
([< prism ] as 'a, 'b option -> 'c option, 'b -> 'c) t =
unit -> ('a, 'b option -> 'c option, 'b -> 'c) Optics._t
but an expression was expected of type
([> getter ] as 'd, 'e -> 'f, 'g -> 'h) t =
unit -> ('d, 'e -> 'f, 'g -> 'h) Optics._t
Type
('a, 'b option -> 'c option, 'b -> 'c) Optics._t =
('a, 'b option, 'c option, 'b, 'c) Optics.__t
is not compatible with type
('d, 'e -> 'f, 'g -> 'h) Optics._t = ('d, 'e, 'f, 'g, 'h) Optics.__t
Type 'a = [< `Affine_fold | `Affine_traversal | `Prism | `Setter ]
is not compatible with type 'd = [> `Affine_fold | `Getter ]
The first variant type does not allow tag(s) `Getter
# #require "jsonaf";;
# let john =
{|
{
"firstName": "John",
"lastName": "Smith",
"isAlive": true,
"age": 27
}
|}
|> Jsonaf.parse
|> Base.Or_error.ok_exn
;;
val john : Jsonaf_kernel.t =
`Object
[("firstName", `String "John"); ("lastName", `String "Smith");
("isAlive", `True); ("age", `Number "27")]
# john.%?[Json.(_Object // key "age" // _Int)];;
- : int option = Option.Some 27
# john.%?[Json.(_Object // key "isAlive" // _Bool)];;
- : bool option = Option.Some true
# john.%?[Json.(_Object // key "first-name" // _String)];;
- : Json.t option = Option.None
# john.%?[Json.(_Object // key "firstName" // _String)];;
- : string option = Option.Some "John"
classDiagram
class setter {
+ over
+ set
}
class affine_fold {
+ preview
+ previews
+ is
+ isn't
}
setter <|-- affine_traversal
affine_fold <|-- affine_traversal
class affine_traversal {
+ matching
}
affine_fold <|-- getter
class getter {
+ get
+ view
+ views
}
affine_traversal <|-- prism
getter <|-- lens
affine_traversal <|-- lens
prism <|-- iso
lens <|-- iso
Due to the value restriction, optics created with function application has a weakly polymorphic type.
# sets List.map;;
- : (_[< setter ], '_weak2 list -> '_weak3 list, '_weak2 -> '_weak3) Optics._t
<abstr>
So, for example, if we want a polymorphic List.map
setter, we need to wrap it in a thunk:
# let _List_map () = sets List.map;;
# _List_map;;
- : unit -> ([< setter ], 'a list -> 'b list, 'a -> 'b) Optics._t = <fun>
This library exports optics wrapped in a thunk (unit → _ Optics._t
) as _ Optics.t
and
defines optics operations over them.
There are some functions which directly create a _ Optics.t
value, whose name is suffixed by '
.
If you want to create an _ Optics.t
value inline, these funciton would be a handy choice.
# [1;2;3] |> set _List_map 1;;
- : int list = [1; 1; 1]
# [1;2;3] |> set (sets' List.map) 1;;
- : int list = [1; 1; 1]
NB: Optics composition (//)
has no raw-optics-returning variants.
So, if you want to bind a variable by optics composed with (//)
,
you need to eta-expand it.
# let _1_2 () = () |> _1 // _2;;
# _1_2;;
- : unit -> ([< lens ], ('a * 'b) * 'c -> ('a * 'd) * 'c, 'b -> 'd) Optics._t =