leque / optics.ml

an experimental optic library for OCaml

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

optics.ml

An experimental optics library for OCaml. See accessor library for a more comprehensive implementation.

Usage

% 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"

Optics Hierarchy

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

Optics and weak polymorphism

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 =

About

an experimental optic library for OCaml

License:MIT License


Languages

Language:OCaml 100.0%