[erlang-questions] Deep data structures and lenses

Guilherme Andrade g@REDACTED
Sat Sep 12 19:51:39 CEST 2015


Hello list,

Partially inspired by jlouis' erl-lenses[1] and an old discussion on
this mailing list[2], I've been sketching a library for easier
manipulation (both data fetching and updating) of deep data structures
(records inside records, keylists inside records, etc.)

I would like to give priority to:
    - Code readability;
    - Safety ('try to crash on unexpected data instead of producing
hard-to-track misbehaviour';)
    - Reasonable performance (without going the parse-transform way.)

Working code already public[3] but it's still a mess and requires some
cooking before it's ready for human consumption.

What follows is a few statements, also available on Github[4] that
demonstrate the kind of syntax I am hoping to achieve; input and
criticism are welcome, as I've been struggling with some design
decisions and general syntactic coherence.


[1]: https://github.com/jlouis/erl-lenses
[2]: http://erlang.org/pipermail/erlang-questions/2013-January/071936.html
[3]:
https://github.com/g-andrade/monocle/tree/f1e530f15de5e4aff94dd42bfb2d572afbfc8b96
[4]:
https://github.com/g-andrade/monocle/blob/f1e530f15de5e4aff94dd42bfb2d572afbfc8b96/test/monocle_tests.erl

---------------------------------------------------------------------

    -record(phone, {
            operator :: string(),
            number :: string(),
            balance :: float()
    }).

    -record(person, {
            name :: string(),
            age :: non_neg_integer(),
            sex :: m | f,
            phone :: #phone{},
            lucky_numbers :: [number()]
    }).


    tuple_test() ->
        Phone1 = #phone{operator = "Telecom",
                        number = "1231234",
                        balance = 23.51},
        Person1 = #person{name = "John Doe",
                          age = 33,
                          sex = m,
                          phone = Phone1,
                          lucky_numbers = [3, 75, 123, 3.2, 74]},

        Lens1 = mtuple:get(#person.age),
        EvalA = monocle:eval(Lens1, Person1),
        ?assert(EvalA =:= 33),
       
        Lens2 = mtuple:get(#person.phone, mtuple:get(#phone.number)),
        EvalB = monocle:eval(Lens2, Person1),
        ?assert(EvalB =:= Phone1#phone.number),

        Lens3 = mtuple:get(#person.phone, mtuple:get(#phone.balance)),
        EvalC = monocle:eval(Lens3, Person1),
        ?assert(EvalC =:= Phone1#phone.balance),

        Lens4 = mtuple:set(#person.phone,
                           mql:fold([mtuple:set(#phone.balance,
    mnum:sub(1)),
                                     mtuple:set(#phone.balance,
    mnum:mul(3)),
                                     mtuple:set(#phone.balance,
    mnum:'/'(3.0)),
                                     mtuple:set(#phone.balance,
    mql:fold([mnum:min(infinity),
                                                                          mnum:max(0),
                                                                          mnum:mul(-1),
                                                                          mnum:mul(-1)])),
                                     mtuple:set(#phone.balance,
    mnum:sub(1 bsl 3)),
                                     mtuple:set(#phone.balance,
    mnum:add(1 bsl 3))])),
        EvalD1 = monocle:eval(Lens4, Person1),
        EvalD2 = monocle:eval(Lens3, EvalD1),
        ?assert(EvalD2 =:= Phone1#phone.balance - 1),

        Lens5 = mtuple:get(#person.lucky_numbers, mlists:sum()),
        EvalE = monocle:eval(Lens5, Person1),
        ?assert(EvalE =:= lists:sum(Person1#person.lucky_numbers)),

        Lens6 = mql:map([mtuple:get(#person.name),
                         mtuple:get(#person.age),
                         mtuple:get(#person.phone,
    mtuple:get(#phone.number))]),
        EvalF = monocle:eval(Lens6, Person1),
        ?assert(EvalF =:= [Person1#person.name,
                           Person1#person.age,
                           Phone1#phone.number]),

        ok.


-- 
Guilherme

https://www.gandrade.net/
PGP: 0x602B2AD8 / B348 C976 CCE1 A02A 017E 4649 7A6E B621 602B 2AD8

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20150912/a066cc7b/attachment.htm>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: OpenPGP digital signature
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20150912/a066cc7b/attachment.bin>


More information about the erlang-questions mailing list