MackeyRMS/elm-accessors/3.0.2

NOTE: the latest version is 3.1.0

Accessors

Accessors are a way of operating on nested data in Elm that doesn't require gobs of boilerplate.

Optic: is the opaque underlying interface that enables the rest of the library to work.

type alias Optic pr ls s t a b x y =
Optic pr ls s t a b x y

Any Optic is both "lens" and "prism".

Type Aliases: are shorthands from the Optics nomenclature that make writing your own accessors more convenient and hopefully easier to understand.

type alias Iso pr ls s a x y =
Iso pr ls s a x y

The isomorphism is both "lens" and "prism".

type alias Lens ls s a x y =
Lens ls s a x y

Lens that cannot change type of the object.

type alias Prism pr s a x y =
Prism pr s a x y

Prism that cannot change the type of the object.

type alias Traversal s a x y =
Traversal s a x y

Traversal that cannot change the type of the object.

Constructors

Accessors are built using these functions:

traversal : String -> (s -> List a) -> ((a -> b) -> s -> t) -> Traversal_ s t a b x y

This exposes a description field that's necessary for use with the name function for getting unique names out of compositions of accessors. This is useful when you want type safe keys for a Dictionary but you still want to use elm/core implementation.

each : Optic pr ls a b x y -> Traversal (List a) (List b) x y
each =
    Base.traversal "[]"
        identity
        List.map
lens : String -> (s -> a) -> (s -> b -> t) -> Lens_ ls s t a b x y

This exposes a description field that's necessary for use with the name function for getting unique names out of compositions of accessors. This is useful when you want type safe keys for a Dictionary but you still want to use elm/core implementation.

foo : Optic attr view over -> Optic { rec | foo : attr } view over
foo =
    makeOneToOne
        ".foo"
        .foo
        (\change rec -> { rec | foo = change rec.foo })
prism : String -> (b -> t) -> (s -> Result t a) -> Prism_ pr s t a b x y

A prism constructor.

Parameters are: reconstructor and a splitter.

Reconstructor takes a final value and constructs a final object.

The splitter turns initial object either to final object directly (if initial object is of wrong variant), or spits out a.

iso : String -> (s -> a) -> (b -> t) -> Iso_ pr ls s t a b x y

An isomorphism constructor.

Action functions

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

get : A_Lens_ pr s t a b -> s -> a

The get function takes:

  • An accessor,
  • A datastructure with type super and returns the value accessed by that combinator.
get (foo << bar) myRecord
all : An_Optic_ pr ls s t a b -> s -> List a

Used with a Prism, think of !! boolean coercion in Javascript except type safe.

Just "Stuff"
    |> all just
--> ["Stuff"]


Nothing
    |> all just
--> []
try : An_Optic_ pr ls s t a b -> s -> Maybe a

Used with a Prism, think of !! boolean coercion in Javascript except type safe.

["Stuff", "things"]
    |> try (at 2)
--> Nothing


["Stuff", "things"]
    |> try (at 0)
--> Just "Stuff"
has : An_Optic_ pr ls s t a b -> s -> Bool

Used with a Prism, think of !! boolean coercion in Javascript except type safe.

Just 1234
    |> has just
--> True


Nothing
    |> has just
--> False


[ "Wooo", "Things" ]
    |> has (at 7)
--> False


[ "Wooo", "Things" ]
    |> has (at 0)
--> True
is : An_Optic_ pr ls s t a b -> s -> Bool

alias for has

map : An_Optic_ pr ls s t a b -> (a -> b) -> s -> t

The map 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.
map (foo << qux) ((+) 1) myRecord
over : An_Optic_ pr ls s t a b -> (a -> b) -> s -> t

alias for map

over (foo << qux) ((+) 1) myRecord
set : An_Optic_ pr ls s t a b -> b -> s -> t

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
new : A_Prism_ ls s t a b -> b -> t

Use prism to reconstruct.

name : An_Optic_ pr ls s t a b -> String

This function gives the name of the function as a string...

to : An_Iso_ s t a b -> s -> a

Alias of get for isomorphisms

from : An_Iso_ s t a b -> b -> t

Get the inverse of an isomorphism

swap : An_Iso_ s t a b -> Optic pr ls b a t s x y

swap the parent and target types of an iso for composition chains.

Lifters for composing w/ indexed optics

ixd : An_Optic_ pr ls s t a b -> Traversal_ ( ix, s ) t a b x y

Lift an optic over an indexed traversal

Type aliases for custom and action functions

type alias Iso_ pr ls s t a b x y =
Iso_ pr ls s t a b x y

The isomorphism is both "lens" and "prism".

type alias Lens_ ls s t a b x y =
Lens_ ls s t a b x y

The lens is "not a prism".

type alias Prism_ pr s t a b x y =
Prism_ pr s t a b x y

The prism is "not a lens".

type alias Traversal_ s t a b x y =
Traversal_ s t a b x y

The traversal is neither "lens" or "prism".

type alias An_Optic pr ls s a =
An_Optic pr ls s a

This MUST be a Prism or Iso

type alias An_Iso s a =
An_Iso s a

This MUST be a Prism or Iso

type alias A_Lens pr s a =
A_Lens pr s a

This MUST be a non-type changing Lens or Iso

type alias A_Prism ls s a =
A_Prism ls s a

This MUST be a non-type changing Prism or Iso

type alias An_Optic_ pr ls s t a b =
An_Optic_ pr ls s t a b

Any Optic

type alias An_Iso_ s t a b =
An_Iso_ s t a b

This MUST be a Prism or Iso

type alias A_Lens_ pr s t a b =
A_Lens_ pr s t a b

This MUST be a Lens or Iso

type alias A_Prism_ ls s t a b =
A_Prism_ ls s t a b

This MUST be a Prism or Iso

Common Optics to mitigate import noise. Not everything is re-exported.

just : Prism_ pr (Maybe a) (Maybe b) a b x y

This accessor combinator lets you access values inside Maybe.

import Accessors exposing (..)
import Lens as L


maybeRecord : { foo : Maybe { bar : Maybe {stuff : Maybe Int} }, qux : Maybe { bar : Maybe Int } }
maybeRecord = { foo = Just { bar = Just { stuff = Just 2 } }
              , qux = Nothing
              }


try (L.foo << just << L.bar << just << L.stuff) maybeRecord
--> Just (Just 2 )


try (L.qux << just << L.bar) maybeRecord
--> Nothing


map (L.foo << just << L.bar << just << L.stuff << just) ((+) 1) maybeRecord
--> {foo = Just {bar = Just { stuff = Just 3 }}, qux = Nothing}


map (L.qux << just << L.bar << just) ((+) 1) maybeRecord
--> {foo = Just {bar = Just {stuff = Just 2}}, qux = Nothing}
ok : Prism_ pr (Result ignored a) (Result ignored b) a b x y

This accessor lets you access values inside the Ok variant of a Result. alias for Result.Accessors.onOk

import Accessors exposing (..)
import Lens as L


maybeRecord : { foo : Result String { bar : Int }, qux : Result String { bar : Int } }
maybeRecord = { foo = Ok { bar = 2 }
              , qux = Err "Not an Int"
              }


try (L.foo << ok << L.bar) maybeRecord
--> Just 2


try (L.qux << ok << L.bar) maybeRecord
--> Nothing


map (L.foo << ok << L.bar) ((+) 1) maybeRecord
--> { foo = Ok { bar = 3 }, qux = Err "Not an Int" }


map (L.qux << ok << L.bar) ((+) 1) maybeRecord
--> { foo = Ok { bar = 2 }, qux = Err "Not an Int" }
err : Prism_ pr (Result a ignored) (Result b ignored) a b x y

This accessor lets you access values inside the Err variant of a Result. alias for Result.Accessors.onErr

import Accessors exposing (..)
import Lens as L


maybeRecord : { foo : Result String { bar : Int }, qux : Result String { bar : Int } }
maybeRecord = { foo = Ok { bar = 2 }
              , qux = Err "Not an Int"
              }


try (L.foo << err) maybeRecord
--> Nothing


try (L.qux << err) maybeRecord
--> Just "Not an Int"


map (L.foo << err) String.toUpper maybeRecord
--> { foo = Ok { bar = 2 }, qux = Err "Not an Int" }


map (L.qux << err) String.toUpper maybeRecord
--> { foo = Ok { bar = 2 }, qux = Err "NOT AN INT" }
values : Traversal_ (Dict key a) (Dict key b) a b x y

values: This accessor lets you traverse a Dict including the index of each element alias for Dict.Accessors.each

import Accessors exposing (..)
import Lens as L
import Dict exposing (Dict)


dictRecord : {foo : Dict String {bar : Int}}
dictRecord = { foo = [ ("a", { bar = 2 })
                     , ("b", { bar = 3 })
                     , ("c", { bar = 4 })
                     ] |> Dict.fromList
             }


all (L.foo << values) dictRecord
--> [{bar = 2}, {bar = 3}, {bar = 4}]


map (L.foo << values << L.bar) ((*) 10) dictRecord
--> {foo = [("a", {bar = 20}), ("b", {bar = 30}), ("c", {bar = 40})] |> Dict.fromList}


all (L.foo << values << L.bar) dictRecord
--> [2, 3, 4]


map (L.foo << values << L.bar) ((+) 1) dictRecord
--> {foo = [("a", {bar = 3}), ("b", {bar = 4}), ("c", {bar = 5})] |> Dict.fromList}
keyed : Traversal_ (Dict key a) (Dict key b) ( key, a ) b x y

keyed: This accessor lets you traverse a Dict including the index of each element alias for Dict.Accessors.eachIdx

import Accessors exposing (..)
import Lens as L
import Dict exposing (Dict)


dictRecord : {foo : Dict String {bar : Int}}
dictRecord = { foo = [ ("a", { bar = 2 })
                     , ("b", { bar = 3 })
                     , ("c", { bar = 4 })
                     ] |> Dict.fromList
             }


multiplyIfA : (String, { bar : Int }) -> { bar : Int }
multiplyIfA ( key, ({ bar } as rec) ) =
    if key == "a" then
        { bar = bar * 10 }
    else
        rec




all (L.foo << keyed) dictRecord
--> [("a", {bar = 2}), ("b", {bar = 3}), ("c", {bar = 4})]


map (L.foo << keyed) multiplyIfA dictRecord
--> {foo = [("a", {bar = 20}), ("b", {bar = 3}), ("c", {bar = 4})] |> Dict.fromList}


all (L.foo << keyed << ixd L.bar) dictRecord
--> [2, 3, 4]


map (L.foo << keyed << ixd L.bar) ((+) 1) dictRecord
--> {foo = [("a", {bar = 3}), ("b", {bar = 4}), ("c", {bar = 5})] |> Dict.fromList}
key : String -> Lens ls (Dict String a) (Maybe a) x y

key: NON-structure preserving accessor over Dict's alias for Dict.Accessors.at

In terms of accessors, think of Dicts as records where each field is a Maybe.

import Dict exposing (Dict)
import Accessors exposing (..)
import Lens as L


dict : Dict String {bar : Int}
dict = Dict.fromList [("foo", {bar = 2})]


get (key "foo") dict
--> Just {bar = 2}


get (key "baz") dict
--> Nothing


try (key "foo" << just << L.bar) dict
--> Just 2


set (key "foo") Nothing dict
--> Dict.remove "foo" dict


set (key "baz" << just << L.bar) 3 dict
--> dict
keyI : Int -> Lens ls (Dict Int a) (Maybe a) x y

key: NON-structure preserving accessor over Dict's alias for Dict.Accessors.id

In terms of accessors, think of Dicts as records where each field is a Maybe.

import Dict exposing (Dict)
import Accessors exposing (..)
import Lens as L


dict : Dict Int {bar : Int}
dict = Dict.fromList [(1, {bar = 2})]


get (keyI 1) dict
--> Just {bar = 2}


get (keyI 0) dict
--> Nothing


try (keyI 1 << just << L.bar) dict
--> Just 2


set (keyI 1) Nothing dict
--> Dict.remove 1 dict


set (keyI 0 << just << L.bar) 3 dict
--> dict
(comparable -> String)
-> comparable
-> Lens ls (Dict comparable a) (Maybe a) x y

key_: NON-structure preserving accessor over Dict's alias for Dict.Accessors.at_

In terms of accessors, think of Dicts as records where each field is a Maybe.

import Dict exposing (Dict)
import Accessors exposing (..)
import Lens as L


dict : Dict Char {bar : Int}
dict = Dict.fromList [('C', {bar = 2})]


keyC : Char -> Lens ls (Dict Char { bar : Int })  (Maybe { bar : Int }) x y
keyC =
    key_ String.fromChar


get (keyC 'C') dict
--> Just {bar = 2}


get (keyC 'Z') dict
--> Nothing


try (keyC 'C' << just << L.bar) dict
--> Just 2


set (keyC 'C') Nothing dict
--> Dict.remove 'C' dict


set (keyC 'Z' << just << L.bar) 3 dict
--> dict
each : Traversal_ (List a) (List b) a b x y

This accessor combinator lets you access values inside List. alias for List.Accessors.each

eachIdx : Traversal_ (List a) (List b) ( Int, a ) b x y

This accessor lets you traverse a list including the index of each element alias for List.Accessors.eachIdx

at : Int -> Traversal (List a) a x y

at: Structure Preserving accessor over List members. alias for List.Accessors.at

every : Traversal_ (Array a) (Array b) a b x y

This accessor combinator lets you access values inside Array. alias for Array.Accessors.each

import Accessors exposing (..)
import Array exposing (Array)
import Lens as L


arrayRecord : {foo : Array {bar : Int}}
arrayRecord =
    { foo =
        Array.fromList [{ bar = 2 }, { bar = 3 }, {bar = 4}]
    }


all (L.foo << every << L.bar) arrayRecord
--> [2, 3, 4]


map (L.foo << every << L.bar) ((+) 1) arrayRecord
--> {foo = Array.fromList [{bar = 3}, {bar = 4}, {bar = 5}]}
everyIdx : Traversal_ (Array b) (Array c) ( Int, b ) c x y

This accessor lets you traverse an Array including the index of each element alias for Array.Accessors.eachIdx

import Accessors exposing (..)
import Lens as L
import Array exposing (Array)


arrayRecord : { foo : Array { bar : Int } }
arrayRecord = { foo = [ {bar = 2}
                      , {bar = 3}
                      , {bar = 4}
                      ] |> Array.fromList
              }


multiplyIfGTOne : (Int, { bar : Int }) -> { bar : Int }
multiplyIfGTOne ( idx, ({ bar } as rec) ) =
    if idx > 0 then
        { bar = bar * 10 }
    else
        rec




all (L.foo << everyIdx) arrayRecord
--> [(0, {bar = 2}), (1, {bar = 3}), (2, {bar = 4})]


map (L.foo << everyIdx) multiplyIfGTOne arrayRecord
--> {foo = [{bar = 2}, {bar = 30}, {bar = 40}] |> Array.fromList}


all (L.foo << everyIdx << ixd L.bar) arrayRecord
--> [2, 3, 4]


map (L.foo << everyIdx << ixd L.bar) ((+) 1) arrayRecord
--> {foo = [{bar = 3}, {bar = 4}, {bar = 5}] |> Array.fromList}
ix : Int -> Traversal (Array a) a x y

alias for Array.Accessors.at

import Accessors exposing (..)
import Array exposing (Array)
import Lens as L


arr : Array { bar : String }
arr = Array.fromList [{ bar = "Stuff" }, { bar =  "Things" }, { bar = "Woot" }]


try (ix 1) arr
--> Just { bar = "Things" }


try (ix 9000) arr
--> Nothing


try (ix 0 << L.bar) arr
--> Just "Stuff"


set (ix 0 << L.bar) "Whatever" arr
--> Array.fromList [{ bar = "Whatever" }, { bar =  "Things" }, { bar = "Woot" }]


set (ix 9000 << L.bar) "Whatever" arr
--> arr
fst : Lens_ ls ( a, two ) ( b, two ) a b x y

Lens over the first component of a Tuple alias for Tuple.Accessors.fst

import Accessors exposing (..)


charging : (String, Int)
charging = ("It's over", 1)


get fst charging
--> "It's over"


set fst "It's over" charging
--> ("It's over", 1)


map fst (\s -> String.toUpper s ++ "!!!") charging
--> ("IT'S OVER!!!", 1)
snd : Lens_ ls ( one, a ) ( one, b ) a b x y

alias for Tuple.Accessors.snd

import Accessors exposing (..)


meh : (String, Int)
meh = ("It's over", 1)


get snd meh
--> 1


set snd 1125 meh
--> ("It's over", 1125)


meh
    |> set snd 1125
    |> map fst (\s -> String.toUpper s ++ "!!!")
    |> map snd ((*) 8)
--> ("IT'S OVER!!!", 9000)

Color Iso's :raised_hands: wooo!!! For seemless composition of colors.

hexA : Prism pr String TransparentColor x y

hex: This accessor lets you convert between tesk9/palette TransparentColor && SolidColor

import Color




new (hexA << swap transparent) Color.red
-->  "#CC0000"
try (hexA << swap transparent) "#C00F" -- with alpha channel
--> Just Color.red
transparent : Iso pr ls Color TransparentColor x y

solid: This accessor lets you convert between tesk9/palette TransparentColor && SolidColor

import Color




from transparent <| to transparent Color.red
--> Color.red
solid : Iso pr ls TransparentColor SolidColor x y

solid: This accessor lets you convert between tesk9/palette TransparentColor && SolidColor

import Color




from (transparent << solid) <| to (transparent << solid) Color.red
--> Color.red
oklch : Iso pr ls Color Oklch x y

oklch: This accessor lets you convert between oklch & avh4/elm-color

import Color
import Color.Round as Round




Color.blue |> to oklch |> from oklch |> Round.rgb
--> Round.rgb Color.blue
oklab : Iso pr ls Oklch Oklab x y

oklab: This accessor lets you convert between oklch & avh4/elm-color

import Color
import Color.Round as Round




 Color.blue |> to (oklch << oklab) |> from (oklch << oklab) |> Round.rgb
--> Round.rgb Color.blue
elmui : Iso pr ls Color Color x y

elmui: This accessor lets you convert between oklch & avh4/elm-color

import Color




from elmui <| to elmui Color.red
--> Color.red
hsluv : Iso pr ls Color HSLuv x y

hsluv: This accessor lets you convert between oklch & avh4/elm-color

import Color
import Color.Round as Round




Color.blue |> to hsluv |> from hsluv |> Round.rgb
--> Round.rgb Color.blue