/ocaml-lens

Private extraction of astrada's lens library

Primary LanguageOCaml

Functional Lenses

Build Status

This package provides some basic types and functions for using lenses in OCaml. Functional lenses are based on F# implementation in FSharpX. See src/FSharpx.Extras/Lens.fs for the original implementation. Written by Alessandro Strada.

See also:

Examples

First load Lens in utop.

utop # #use "lens.ml";;

Given a couple of records

    type car = {
        make : string;
        model: string;
        mileage: int;
      };;

    type editor = {
        name: string;
        salary: int;
        car: car;
    };;

    type book = {
        name: string;
        author: string;
        editor: editor;
    };;

Create a new nested record

    let scifi_novel = {
       name =  "Metro 2033";
       author = "Dmitry Glukhovsky";
       editor =  {
         name = "Vitali Gubarev";
         salary =  1300;
         car =  {
           make = "Lada";
           model = "VAZ-2103";
           mileage = 310000
        }
      }
    };;

Now to construct a few lenses to access some things

    let car_lens = {
        get = (fun x -> x.car);
        set = (fun v x -> { x with car = v })
      };;

    let editor_lens = {
        get = (fun x -> x.editor);
        set = (fun v x -> { x with editor = v })
    };;

    let mileage_lens = {
        get = (fun x -> x.mileage);
        set = (fun v x -> { x with mileage = v })

    };;

Using these lenses we can modify the mileage without having to unpack the record

    let a = compose mileage_lens (compose car_lens editor_lens) in
    _set 10 scifi_novel a;;

Or using the Infix module we can do the same thing, only shorter.

    _set 10 scifi_novel (editor_lens |-- car_lens |-- mileage_lens);;

    (* or *)

    ((editor_lens |-- car_lens |-- mileage_lens) ^= 10) @@ scifi_novel;;

Ppx syntax extension

Lenses can be generated using the 'lens.ppx_deriving' plugin for ppx_deriving

#require "lens.ppx_deriving";;

type car = {
  make : string;
  model: string;
  mileage: int;
} [@@deriving lens];;

val car_make: (car, string) Lens.t
val car_model: (car, string) Lens.t
val car_mileage: (car, int) Lens.t

The prefix option can be used to prefix each lens name with lens.

#require "lens.ppx_deriving";;

type car = {
  make : string;
  model: string;
  mileage: int;
} [@@deriving lens { prefix = true }];;

val lens_car_make: (car, string) Lens.t
val lens_car_model: (car, string) Lens.t
val lens_car_mileage: (car, int) Lens.t

The submodule option groups all the lenses in a sub-module Lens.

#require "lens.ppx_deriving";;

type car = {
  make : string;
  model: string;
  mileage: int;
} [@@deriving lens { submodule = true }];;

module Lens :
  val make : (car, string) Lens.t
  val model : (car, string) Lens.t
  val mileage : (car, int) Lens.t
end

When the prefix and submodule options are combined, this is the module name which is prefixed.

#require "lens.ppx_deriving";;

type car = {
  make : string;
  model: string;
  mileage: int;
} [@@deriving lens { submodule = true; prefix = true }];;

module CarLens :
  val make : (car, string) Lens.t
  val model : (car, string) Lens.t
  val mileage : (car, int) Lens.t
end