मान लीजिए मेरे पास एक अपरिवर्तनीय आवरण है:

template<class T>
struct immut {
  T const& get() const {return *state;}
  immut modify( std::function<T(T)> f ) const { return immut{f(*state)}; }
  immut(T in):state(std::make_shared<T>(std::move(in))){}
private:
  std::shared_ptr<T const> state;
};

अगर मेरे पास immut<Bob> b है, तो मैं एक Bob(Bob) ऑपरेशन को किसी ऐसी चीज में बदल सकता हूं जो मेरे b को बदल सके।

template<class T>
std::function<immut<T>(immut<T>)> on_immut( std::function<void(T&)> f ){
  return [=](auto&&in){ return in.modify( [&](auto t){ f(t); return t; } ); };
}

तो अगर Bob int x,y; है, तो मैं भोले-भाले कोड [](auto& b){ b.x++; } को immut<Bob> अपडेटर में बदल सकता हूं।


अब, क्या होगा यदि Bob में बदले में immut<Alice> सदस्य हों, जिसके बदले में immut<Charlie> सदस्य हों।

मान लीजिए मेरे पास एक Charlie अपडेटर, void(Charlie&) है। और मुझे पता है कि Charlie मैं कहां अपडेट करना चाहता हूं। परिवर्तनशील भूमि में ऐसा दिखेगा:

void update( Bob& b ){
  modify_charlie(b.a.c[77]);
}

और मैं इसे इसमें विभाजित कर सकता हूं:

template<class S, class M>
using get=std::function<M&(S&)>;
template<class X>
using update=std::function<void(X&)>;
template<class X>
using produce=std::function<X&()>;

void update( produce<Bob> b, get<Bob, Alice> a, get<Alice, Charlie> c, update<Charlie> u ){
  u(c(a(b())));
}

या यहां तक ​​​​कि बीजीय जाओ और (छद्म कोड) है:

get<A,C> operator|(get<A,B>,get<B,C>);
update<A> operator|(get<A,B>,update<B>);
produce<B> operator|(produce<A>,get<A,B>);
void operator|(produce<A>, update<A>);

हमें आवश्यकतानुसार संचालन को एक साथ श्रृंखलाबद्ध करने देना।

void update( produce<Bob> b, get<Bob, Alice> a, get<Alice, Charlie> c, update<Charlie> u ){std::
  u(c(a(b())));
}

हो जाता है

b|a|c|u;

जहां कदमों को एक साथ सिला जा सकता है, जहां कहीं भी उनकी आवश्यकता हो।


इम्मट्स के बराबर क्या है, और क्या इसका कोई नाम है? आदर्श रूप से मैं चाहता हूं कि कदम अलग-अलग हों, फिर भी वे परिवर्तनशील मामले में कंपोजेबल हों, जिसमें भोले "लीफ कोड" म्यूटेबल स्टेट स्ट्रक्चर्स पर हों।

6
Yakk - Adam Nevraumont 10 पद 2018, 08:26

1 उत्तर

सबसे बढ़िया उत्तर

इम्मट्स के बराबर क्या है, और क्या इसका कोई नाम है?

आईडीके क्या उप-राज्य हेरफेर के लिए एक सार्वभौमिक नाम है। लेकिन हास्केल में, एक है। Lens जो बिल्कुल वही प्रदान करता है जो आप चाहते हैं।

C++ में, आप एक lens को गेट्टर और सेटर फ़ंक्शंस की एक जोड़ी के रूप में मान सकते हैं, जो दोनों केवल इसके प्रत्यक्ष उप-भाग पर ध्यान केंद्रित करते हैं। फिर एक संरचना के गहरे उप-भाग पर ध्यान केंद्रित करने के लिए दो लेंसों को एक साथ संयोजित करने के लिए एक संगीतकार होता है।

// lens for `A` field in `D`
template<class D, class A>
using get = std::function<A const &(D const &)>;

template<class D, class A>
using set = std::function<D(D const &, A)>;

template<class D, class A>
using lens = std::pair<get<D, A>, set<D, A>>;

// compose (D, A) lens with an inner (A, B) lens,
// return a (D, B) lens
template<class D, class A, class B>
lens<D, B>
lens_composer(lens<D, A> da, lens<A, B> ab) {
    auto abgetter = ab.first;
    auto absetter = ab.second;
    auto dagetter = da.first;
    auto dasetter = da.second;

    get<D, B> getter = [abgetter, dagetter]
        (D const &d) -> B const&
    {
        return abgetter(dagetter(d));
    };

    set<D, B> setter = [dagetter, absetter, dasetter]
        (D const &d, B newb) -> D
    {
        A const &a = dagetter(d);
        A newa = absetter(a, newb);
        return dasetter(d, newa);
    };

    return {getter, setter};
};

आप इस तरह एक बुनियादी लेंस लिख सकते हैं:

struct Bob {
    immut<Alice> alice;
    immut<Anna>  anna;
};
auto bob_alice_lens
= lens<Bob, Alice> {
    [] (Bob const& b) -> Alice const & {
        return b.alice.get();
    },
    [] (Bob const& b, Alice newAlice) -> Bob {
        return { immut{newAlice}, b.anna };
    }
};

ध्यान दें कि इस प्रक्रिया को मैक्रो द्वारा स्वचालित किया जा सकता है।

फिर यदि Bob में immut<Alice>, Alice में immut<Charlie> शामिल हैं, तो आप 2 लेंस (Bob से Alice और Alice लिख सकते हैं। Charlie), Bob से Charlie लेंस की रचना किसके द्वारा की जा सकती है:

auto bob_charlie_lens = 
lens_composer(bob_alice_lens, alice_charlie_lens);

लाइव डेमो

नोट:

नीचे टाइप-इरेज़र ओवरहेड के बिना लीनियर मेमोरी ग्रोथ wrt डेप्थ के साथ एक अधिक संपूर्ण उदाहरण है (std::function), और पूर्ण संकलन समय प्रकार की जांच के साथ। यह मूल लेंस उत्पन्न करने के लिए मैक्रो का भी उपयोग करता है।

#include <type_traits>
#include <utility>

template<class T>
using GetFromType = typename T::FromType;

template<class T>
using GetToType = typename T::ToType;

// `get` and `set` are fundamental operations for Lens,
// this Mixin will add utilities based on `get` and `set`
template<class Derived>
struct LensMixin {
    Derived &self() { return static_cast<Derived&>(*this); }

    // f has type: A& -> void
    template<class D, class Mutation>
    auto modify(D const &d, Mutation f)
    {
        auto a = self().get(d);
        f(a);
        return self().set(d, a);
    }
};

template<
    class Getter, class Setter,
    class D, class A
    >
struct SimpleLens : LensMixin<SimpleLens<Getter, Setter, D, A>> {
    static_assert(std::is_same<
            std::invoke_result_t<Getter, const D&>, const A&>{},
            "Getter should return const A& for (const D&)");

    static_assert(std::is_same<
            std::invoke_result_t<Setter, const D&, A>, D>{},
            "Setter should return D for (const D&, A)");
    using FromType = D;
    using ToType = A;

    SimpleLens(Getter getter, Setter setter)
        : getter(getter)
        , setter(setter)
    {}

    A const &get(D const &d) { return getter(d); }
    D set(D const &d, A newa) { return setter(d, newa); }
private:
    Getter getter;
    Setter setter;
};

template<
    class LensDA, class LensAB
    >
struct ComposedLens : LensMixin<ComposedLens<LensDA, LensAB>> {
    static_assert(std::is_same<
            GetToType<LensDA>, GetFromType<LensAB>
        >{}, "Cannot compose two Lens with wrong intermediate type");

    using FromType = GetFromType<LensDA>;
    using ToType = GetToType<LensAB>;

private:
    using intermediateType = GetToType<LensDA>;
    using D = FromType;
    using B = ToType;
    LensDA da;
    LensAB ab;
public:

    ComposedLens(LensDA da, LensAB ab) : da(da), ab(ab) {}

    B const &get(D const &d) { return ab.get(da.get(d)); }
    D set(D const &d, B newb) {
        const auto &a = da.get(d);
        auto newa = ab.set(a, newb);
        return da.set(d, newa);
    }
};

namespace detail {
    template<class LensDA, class LensAB>
    auto MakeComposedLens(LensDA da, LensAB ab) {
        return ComposedLens<LensDA, LensAB> { da, ab };
    }

    template<class D, class A, class Getter, class Setter>
    auto MakeSimpleLens(Getter getter, Setter setter)
    {
        return SimpleLens<Getter, Setter, D, A> {
            getter, setter
        };
    }
}

template<class LensDA, class LensAB>
auto lens_composer(LensDA da, LensAB ab) {
    return detail::MakeComposedLens (da, ab);
}


#include <memory>

template<class T>
struct immut {
  T const& get() const {return *state;}
  immut(T in):state(std::make_shared<T>(std::move(in))){}
private:
  std::shared_ptr<T const> state;
};

#define MAKE_SIMPLE_LENS(D, A, Aname)   \
    detail::MakeSimpleLens<D, A>(       \
        +[] (D const &d) -> A const & { \
            return d . Aname . get();   \
        },                              \
        +[] (D const &d, A newa) -> D { \
            D newd = d;                 \
            newd . Aname = newa;        \
            return newd;                \
        })




struct Charlie {
    int id = 0;
};

struct Alice{
    immut<Charlie> charlie;
};
struct Anna {};
struct Bob {
    immut<Alice> alice;
    immut<Anna>  anna;
};

auto alice_charlie_lens = MAKE_SIMPLE_LENS(Alice, Charlie, charlie);
auto bob_alice_lens     = MAKE_SIMPLE_LENS(Bob, Alice, alice);
auto bob_charlie_lens   = lens_composer(bob_alice_lens, alice_charlie_lens);

static_assert(std::is_same<GetFromType<decltype(bob_charlie_lens)>, Bob>{});
static_assert(std::is_same<GetToType<decltype(bob_charlie_lens)>, Charlie>{});

#include <iostream>

int main() {
    immut<Charlie> charlie{Charlie{77}};
    immut<Alice> alice{Alice{charlie}};
    immut<Anna>  anna{Anna{}};

    Bob bob{alice, anna};
    std::cout << "bob     -> anna: " << static_cast<void const*>(&bob.anna.get()) << "\n";
    std::cout << "bob     -> charlie: " << bob_charlie_lens.get(bob).id << "\n";

    // Bob newbob = bob_charlie_lens.set(bob, Charlie{148});
    Bob newbob = bob_charlie_lens.modify(bob, [] (auto &charlie) {
            charlie.id += (148 - 77);
            });
    std::cout << "new bob -> anna: " << static_cast<void const*>(&bob.anna.get()) << "\n";
    std::cout << "old bob -> charlie: " << bob_charlie_lens.get(bob).id << "\n";
    std::cout << "new bob -> charlie: " << bob_charlie_lens.get(newbob).id << "\n";
}
6
llllllllll 12 पद 2018, 12:10