এসডিজেডএক্স -১/পলিস্টেট: পলিস্টেট: কমপোজেবল সসীম স্টেট মেশিন

এসডিজেডএক্স -১/পলিস্টেট: পলিস্টেট: কমপোজেবল সসীম স্টেট মেশিন

পলিস্টেট: কমপোজেবল সসীম স্টেট মেশিন

আপনার প্রকল্পের মূলে নিম্নলিখিত কমান্ডটি চালিয়ে একটি নির্ভরতা হিসাবে পলিস্টেট ডাউনলোড করুন এবং যুক্ত করুন:

zig fetch --save git+

তারপরে একটি নির্ভরতা হিসাবে পলিস্টেট যুক্ত করুন এবং আপনার বিল্ড.জিগে এর মডিউলগুলি এবং আর্টিফ্যাক্টটি আমদানি করুন:

    const polystate = b.dependency("polystate", .
        .target = target,
        .optimize = optimize,
    );

আপনি সাধারণত যেমন চান তেমন আপনার মডিউলগুলিতে মডিউলগুলি যুক্ত করুন:

    exe_mod.addImport("polystate", typed_fsm.module("root"));

পলিস্টেট-উদাহরণ

রে-গেম

এই দস্তাবেজটি লেখা আমার প্রত্যাশার চেয়ে বেশি কঠিন ছিল। যদিও আমি এই লাইব্রেরিটি কিছুক্ষণ লিখছি এবং ব্যবহার করছি, অন্যদের কাছে এটি পরিষ্কারভাবে ব্যাখ্যা করা সহজ নয়। এই দস্তাবেজটি পড়ার পরে যদি আপনার কোনও প্রশ্ন থাকে তবে দয়া করে জিজ্ঞাসা করতে দ্বিধা করবেন না। আমি কোনও বিভ্রান্তি পরিষ্কার করে খুশি হব!

পলিস্টেটের মূল নকশা দর্শন

  1. প্রকার স্তরে রাষ্ট্রীয় মেশিনের স্থিতি রেকর্ড করুন।
  2. প্রকারের রচনাটির মাধ্যমে কমপোজেবল স্টেট মেশিনগুলি অর্জন করুন।

সসীম স্টেট মেশিন (এফএসএম) একটি শক্তিশালী প্রোগ্রামিং প্যাটার্ন। যখন কমপোজেবিলিটি এবং টাইপ সুরক্ষার সাথে একত্রিত হয়, তখন তারা আরও আদর্শ প্রোগ্রামিংয়ের দৃষ্টান্ত হয়ে ওঠে।

দ্য polystate গ্রন্থাগারটি এই উদ্দেশ্যে সুনির্দিষ্টভাবে ডিজাইন করা হয়েছে। এটি অর্জনের জন্য আপনাকে কয়েকটি সাধারণ প্রোগ্রামিং কনভেনশনগুলি অনুসরণ করতে হবে। এই সম্মেলনগুলি খুব সোজা, এবং তারা যে সুবিধাগুলি নিয়ে আসে তা সম্পূর্ণরূপে মূল্যবান।

পলিস্টেটের ব্যবহারিক প্রভাব

  1. কম্পোজিশনাল ঘোষণার মাধ্যমে প্রোগ্রামের সামগ্রিক আচরণ সংজ্ঞায়িত করুন। এর অর্থ আমরা প্রকার স্তরে প্রোগ্রামের সামগ্রিক আচরণ নির্দিষ্ট করার ক্ষমতা অর্জন করি। এটি অপরিহার্য প্রোগ্রামের কাঠামোর সঠিকতা উল্লেখযোগ্যভাবে উন্নত করে। এই প্রোগ্রামিং শৈলী আমাদের প্রকার এবং রচনাগুলির দৃষ্টিকোণ থেকে প্রোগ্রামের অবস্থাটিকে নতুন করে ডিজাইন করতে উত্সাহিত করে, যার ফলে কোডের কমপোজেবিলিটি বাড়ানো যায়।
  2. সাধারণ রাজ্যগুলি রচনা করে জটিল রাষ্ট্র মেশিনগুলি তৈরি করুন। প্রথমবারের জন্য, আমরা টাইপ রচনার মাধ্যমে শব্দার্থক-স্তরের কোড পুনরায় ব্যবহার অর্জন করতে পারি। অন্য কথায়, আমরা টাইপ স্তরে শব্দার্থক-স্তরের কোড পুনঃব্যবহার প্রকাশ করার একটি উপায় খুঁজে পেয়েছি। এই পদ্ধতির একসাথে তিনটি প্রভাব অর্জন করে: সংক্ষিপ্ততা, নির্ভুলতা এবং সুরক্ষা।
  3. স্বয়ংক্রিয়ভাবে রাষ্ট্রীয় চিত্র উত্পন্ন করুন। যেহেতু প্রোগ্রামটির সামগ্রিক আচরণ ঘোষণা দ্বারা নির্ধারিত হয়, polystate স্বয়ংক্রিয়ভাবে রাষ্ট্রীয় চিত্র তৈরি করতে পারে। ব্যবহারকারীরা এই চিত্রগুলির মাধ্যমে প্রোগ্রামটির সামগ্রিক আচরণটি স্বজ্ঞাতভাবে বুঝতে পারেন।

আমি বিশ্বাস করি এই সমস্তগুলি অপরিহার্য প্রোগ্রামিংয়ের জন্য একটি দুর্দান্ত পদক্ষেপের প্রতিনিধিত্ব করে!

নকশা দর্শন এবং ব্যবহারিক প্রভাবগুলির বিশদ ব্যাখ্যা

আসুন একটি সাধারণ রাষ্ট্র মেশিনের একটি কংক্রিট উদাহরণ দিয়ে শুরু করা যাক। আমি কোডটিতে মন্তব্যের মাধ্যমে এই লাইব্রেরির মূল নকশা দর্শনটি বিশদভাবে ব্যাখ্যা করব।

const std = @import("std");
const polystate = @import("polystate");

pub fn main() !void 
    var st: GST = .;
    /// Determine an initial state
    const wa = Example.Wit(Example.a);
    /// Start executing the state machine with the message handler of this initial state
    /// The reason for using handler_normal here is related to tail-call optimization, which I will explain in detail later.
    wa.handler_normal(&st);


pub const GST = struct 
    counter_a: i64 = 0,
    counter_b: i64 = 0,
;

/// `polystate` has two core state types: FST (FSM Type) and GST (Global State Type).
/// FST must be an enum type. In this example, the FST is `Example`, which defines all the states of our state machine. In other words, it defines the set of states we will track at the type level.
/// GST is the global data. In this example, the GST is defined above with two fields, `counter_a` and `counter_b`, representing the data needed for state `a` and state `b`, respectively.
/// When we compose states, what we really want is to compose state handler functions, which implies a requirement for global data.
/// Therefore, the first programming convention is: the handler function for any state has access to the GST (i.e., global data), but users should try to use only the data corresponding to the current state.
/// For example, in the handler function for state `a`, you should try to use only the data `counter_a`.
/// This can be easily achieved through some naming conventions, and it's easy to create corresponding generic functions through metaprogramming, but the specific implementation is outside the scope of `polystate`.
const Example = enum 
    /// Three concrete states are defined here
    exit,
    a,
    b,

    /// `Wit` is a core concept in `polystate`, short for Witness. The term comes from (Haskell)( where it's called a 'type witness' or 'runtime evidence'.
    /// The core concepts of a finite state machine include four parts: state, message, message handler function, and message generator function. I will detail these parts in the example below.
    /// The purpose of the `Wit` function is to specify the state information contained in a message.
    pub fn Wit(val: anytype) type 
        return polystate.Witness(@This(), GST, null, polystate.val_to_sdzx(@This(), val));
    

    /// This is the second programming convention: The FST needs a public declaration that contains the specific content of the state. By adding `ST` after the state name, we implicitly associate the state with its specific content.
    /// In this example, this corresponds to the public declarations below:
    /// exit ~ exitST
    /// a    ~ aST
    /// b    ~ bST
    /// Here, `exitST` describes the four parts for the `exit` state: state, message, message handler function, and message generator function.
    /// Since the `exit` state has no messages, it also has no message generator function.
    /// This is the third programming convention: The implementation of a state's specific content must contain a function: `pub fn handler(*GST) void` or `pub fn conthandler(*GST) ContR`.
    /// They represent the message handler function. The former means the state machine has full control of the control flow. The latter means a continuation function is returned, leaving the external caller to invoke the continuation function and take control of the flow.
    pub const exitST = union(enum) 
        pub fn handler(ist: *GST) void 
            std.debug.print("exit\n", .);
            std.debug.print("st: any\n", .ist.*);
        
    ;
    pub const aST = a_st;
    pub const bST = b_st;
;

/// This describes the four parts for state `a`: state, message, message handler function, and message generator function.
/// 1. State
/// The state here is `a`.
pub const a_st = union(enum) 
    /// 2. Message
    /// A tagged union is used here to describe the messages, and `Wit` is used to describe the state we are about to transition to.
    AddOneThenToB: Example.Wit(Example.b),
    Exit: Example.Wit(Example.exit),

    /// 3. Message Handler Function
    /// Handles all messages generated by `genMsg`.
    pub fn handler(ist: *GST) void 
        switch (genMsg(ist)) wit
    

    /// 4. Message Generator Function
    /// If the value of `counter_a` is greater than 3, return `.Exit`.
    /// Otherwise, return `.AddOneThenToB`.
    /// The messages generated and handled here are defined in part 2 above.
    fn genMsg(ist: *GST) @This() 
        if (ist.counter_a > 3) return .Exit;
        return .AddOneThenToB;
    
;

pub const b_st = union(enum) 
    AddOneThenToA: Example.Wit(Example.a),

    pub fn handler(ist: *GST) void 
        switch (genMsg()) wit
    

    fn genMsg() @This() 
        return .AddOneThenToA;
    
;

উপরেরটি একটি সাধারণ উদাহরণ যা দেখায় যে কীভাবে একটি সাধারণ রাষ্ট্রীয় মেশিন তৈরি করা যায় polystate। এই উদাহরণটি প্রদর্শন করে না polystateএর সবচেয়ে শক্তিশালী বৈশিষ্ট্য: কমপোজিবিলিটি

আমাকে একটি নতুন রাষ্ট্র যুক্ত করে উপরের উদাহরণটি সংশোধন করতে দিন, yes_or_noকমপোজেবিলিটি প্রদর্শন করতে। আমি উপরের মতো কিছু কোড বাদ দেব। এই উদাহরণের জন্য সম্পূর্ণ কোডটি এখানে পাওয়া যাবে।

const std = @import("std");
const polystate = @import("polystate");

pub fn main() !void 
   ...


pub const GST = struct 
  ...
  buf: (10) u8 = @splat(0),
;

///Example
const Example = enum 
    exit,
    a,
    b,
    /// A new state `yes_or_no` is defined here
    yes_or_no,



    pub fn Wit(val: anytype) type 
        ...
    

    pub const exitST = union(enum) 
      ...
    ;
    pub const aST = a_st;
    pub const bST = b_st;
    
    /// The specific implementation of the new state is a function that depends on two state parameters: `yes` and `no`.
    /// Its semantic is to provide an interactive choice for the user: if the user chooses 'yes', it transitions to the state corresponding to `yes`; if the user chooses 'no', it transitions to the state corresponding to `no`.
    /// The `sdzx` function here turns a regular enum type into a new, composable type.
    /// For example, I can use `polystate.sdzx(Example).C(.yes_or_no, &. .a, .b )` to represent the state `(yes_or_no, a, b)`.
    /// I usually write this type as `yes_or_no(a, b)`, which indicates that `yes_or_no` is a special state that requires two concrete state parameters.
    /// Semantically, `yes_or_no(exit, a)` means: user confirmation is required before exiting. If the user chooses 'yes', it will transition to the `exit` state; if the user chooses 'no', it will transition to the `a` state.
    /// Similarly, `yes_or_no(yes_or_no(exit, a), a)` means: user confirmation is required twice before exiting. The user must choose 'yes' both times to exit.
    /// This is what composability means. Please make sure you understand this.
    pub fn yes_or_noST(yes: polystate.sdzx(@This()), no: polystate.sdzx(@This())) type 
        return yes_or_no_st(@This(), yes, no, GST);
    
;

pub const a_st = union(enum) 
    AddOneThenToB: Example.Wit(Example.b),
    /// This shows how to build and use a composite message in code.
    /// For a composite message, it needs to be placed in a tuple. The first state is the function, and the rest are its state parameters.
    /// Here, `. Example.yes_or_no, Example.exit, Example.a ` represents the state: `yes_or_no(exit, a)`.
    Exit: Example.Wit(. Example.yes_or_no, Example.exit, Example.a ),
    /// Similarly, `. Example.yes_or_no, .Example.yes_or_no, Example.exit, Example.a, Example.a ` can be used to represent the state: `yes_or_no(yes_or_no(exit, a), a)`.
    ...
;

pub const b_st = union(enum) 
  ...
;

/// Specific implementation of the `yes_or_no` state.
/// First, it's a function that takes four parameters: `FST`, `GST1`, `yes`, and `no`. Note that its implementation is independent of `Example` itself.
/// This is a generic implementation, independent of any specific state machine. You can use this code in any state machine.
/// I will explain this code again from four aspects: state, message, message handler function, and message generator function.
pub fn yes_or_no_st(
    FST: type,
    GST1: type,
    yes: polystate.sdzx(FST),
    no: polystate.sdzx(FST),
) type {
    /// 1. State
    /// Its specific state is: `polystate.sdzx(FST).C(FST.yes_or_no, &. yes, no )`.
    /// It requires two parameters, `yes` and `no`, and also needs to ensure that `FST` definitely has a `yes_or_no` state.
    return union(enum) 
        /// 2. Message
        /// There are three messages here. Special attention should be paid to `Retry`, which represents the semantic of re-entering due to an input error.
        Yes: Wit(yes),
        No: Wit(no),
        /// Note the state being constructed here; it points to itself.
        Retry: Wit(polystate.sdzx(FST).C(FST.yes_or_no, &. yes, no )),

        fn Wit(val: polystate.sdzx(FST)) type 
            return polystate.Witness(FST, GST1, null, val);
        

        /// 3. Message Handler Function
        pub fn handler(gst: *GST1) void 
            switch (genMsg(gst))  wit.handler(gst),
                .No => 
        

        const stdIn = std.io.getStdIn().reader();
        
        /// 4. Message Generator Function
        /// Reads a string from `stdIn`. If the string is "y", it returns the message `.Yes`. If the string is "n", it returns the message `.No`.
        /// In other cases, it returns `.Retry`.
        fn genMsg(gst: *GST) @This() err
    ;
}

এই উদাহরণটি স্পষ্টভাবে প্রমাণ করে যে কীভাবে টাইপ রচনার মাধ্যমে একটি কমপোজেবল স্টেট মেশিন অর্জন করা যায়।

একটি এটিএম কল্পনা করুন। আমরা যখন থাকি checkPin রাজ্য, ব্যবহারকারীকে একটি বাহ্যিক উত্স থেকে একটি পিন প্রবেশ করতে হবে। যদি পিনটি সঠিক হয় তবে এটি একটি প্রেরণ করে Successed বার্তা এবং দ্বারা নির্দিষ্ট রাজ্যে রূপান্তর success প্যারামিটার যদি এটি ভুল হয় তবে এটি একটি প্রেরণ করে Failed বার্তা এবং দ্বারা নির্দিষ্ট রাজ্যে রূপান্তর failed প্যারামিটার

একটি সাধারণ প্রয়োজনীয়তা হ’ল: ব্যবহারকারী সর্বোচ্চ তিনবার পিন প্রবেশের চেষ্টা করতে পারেন। যদি তিনটি প্রচেষ্টা ব্যর্থ হয় তবে কার্ডটি বের করে দেওয়া উচিত এবং মেশিনটি প্রাথমিক স্ক্রিনে ফিরে আসা উচিত।

“সর্বোচ্চ তিনবার” এখানে একটি অত্যন্ত গুরুত্বপূর্ণ সুরক্ষার প্রয়োজনীয়তা যা সহজেই পরিবর্তন করা উচিত নয়।

রাজ্যগুলি রচনা করে, আমরা স্বাভাবিকভাবেই এই প্রভাবটি প্রয়োগ করতে পারি। আমরা ডিজাইন checkPin জেনেরিক রাষ্ট্র হিসাবে এবং তারপরে রাষ্ট্রীয় রূপান্তর ঘোষণায় আমরা এই ব্যবসায়িক যুক্তিটি রচনা করে স্পষ্টভাবে বর্ণনা করি checkPin

  pub fn checkPinST(success: polystate.sdzx(Atm), failed: polystate.sdzx(Atm)) type 
        return union(enum) 
            Successed: polystate.Witness(Atm, GST, null, success),
            Failed: polystate.Witness(Atm, GST, null, failed),

            ...
            ...
        
  

    pub const readyST = union(enum) 
        /// By nesting the declaration of `checkPin` three times, we ensure that the PIN check happens at most three times. This precisely describes the behavior we need.
        /// This demonstrates how to determine the program's overall behavior through compositional declarations.
        InsertCard: Wit(. Atm.checkPin, Atm.session, . Atm.checkPin, Atm.session, . Atm.checkPin, Atm.session, Atm.ready   ),
        Exit: Wit(. Atm.are_you_sure, Atm.exit, Atm.ready ),

        ...
    

আমরা রাষ্ট্রের চিত্রের মাধ্যমে সরাসরি এর সামগ্রিক যুক্তি দেখতে পারি এবং polystate এই সমস্ত স্বয়ংক্রিয়ভাবে উত্পন্ন করতে পারে।

এটিএম-গ্রাফ

আমি ব্যবহার করেছি raylib একটি জেনেরিক “নির্বাচন” শব্দার্থক প্রয়োগ করতে: মাউসের মাধ্যমে ইন্টারেক্টিভ নির্বাচন।

নির্বাচনের নির্দিষ্ট আচরণটি তিনটি জেনেরিক রাজ্যের সমন্বয়ে গঠিত (select, inside, hover) এবং তাদের সম্পর্কিত বার্তা।

এই রাজ্যগুলি এবং বার্তাগুলি প্রয়োগ করে: মাউস সহ একটি উপাদান নির্বাচন করা এবং যখন মাউসটি তার উপরে ঘুরে বেড়ায় তখন কীভাবে প্রতিক্রিয়া জানানো হয়।

pub fn selectST(
    FST: type,
    GST: type,
    enter_fn: ?fn (polystate.sdzx(FST), *GST) void,
    back: polystate.sdzx(FST),
    selected: polystate.sdzx(FST),
) type 
    const cst = polystate.sdzx_to_cst(FST, selected);
    const SDZX = polystate.sdzx(FST);

    return union(enum) 
        // zig fmt: off
        ToBack  : polystate.Witness(FST, GST, enter_fn, back),
        ToInside: polystate.Witness(FST, GST, enter_fn, SDZX.C(FST.inside, &. back, selected )),
        // zig fmt: on
       ...
    ;


pub fn insideST(
    FST: type,
    GST: type,
    enter_fn: ?fn (polystate.sdzx(FST), *GST) void,
    back: polystate.sdzx(FST),
    selected: polystate.sdzx(FST),
) type 
    const cst = polystate.sdzx_to_cst(FST, selected);
    const SDZX = polystate.sdzx(FST);

    return union(enum) 
        // zig fmt: off
        ToBack    : polystate.Witness(FST, GST, enter_fn, back),
        ToOutside : polystate.Witness(FST, GST, enter_fn, SDZX.C(FST.select, &. back, selected )),
        ToHover   : polystate.Witness(FST, GST, enter_fn, SDZX.C(FST.hover, &. back, selected )),
        ToSelected: polystate.Witness(FST, GST, enter_fn, selected),
        // zig fmt: on
       ...
    ;


pub fn hoverST(
    FST: type,
    GST: type,
    enter_fn: ?fn (polystate.sdzx(FST), *GST) void,
    back: polystate.sdzx(FST),
    selected: polystate.sdzx(FST),
) type 
    const cst = polystate.sdzx_to_cst(FST, selected);
    const SDZX = polystate.sdzx(FST);

    return union(enum) 
        // zig fmt: off
        ToBack    : polystate.Witness(FST, GST, enter_fn, back),
        ToOutside : polystate.Witness(FST, GST, enter_fn, SDZX.C(FST.select, &. back, selected )),
        ToInside  : polystate.Witness(FST, GST, enter_fn, SDZX.C(FST.inside, &. back, selected )),
        ToSelected: polystate.Witness(FST, GST, enter_fn, selected),
        // zig fmt: on

       ...
    ;

মধ্যে ray-game প্রকল্প, “নির্বাচন” শব্দার্থকটি কমপক্ষে আটবার পুনরায় ব্যবহার করা হয়েছিল, যা কোডকে ব্যাপকভাবে হ্রাস করেছে এবং সঠিকতা উন্নত করেছে।

এই প্রকল্পের একটি আকর্ষণীয় উদাহরণ হ’ল “দ্বি-পর্যায়ের নির্বাচন”: আপনাকে প্রথমে একটি বিল্ডিং নির্বাচন করতে হবে, তারপরে এটি রাখার জন্য একটি গ্রিডের অবস্থান নির্বাচন করুন। বিল্ডিংয়ের পছন্দটি অবস্থানের পছন্দকেও সীমাবদ্ধ করে। নির্বাচন করুন_টিডব্লিউআইসিটি

এই জাতীয় শব্দার্থবিজ্ঞান সংক্ষিপ্তভাবে প্রকাশ করা যেতে পারে:

pub const placeST = union(enum) 
    ToPlay: Wit(. Example.select, Example.play, . Example.select, Example.play, Example.place  ),
    ...
;

এই কোডটি চরম সংক্ষিপ্ততার সাথে আমাদের অভিপ্রায় বর্ণনা করে। তবে আপনি যদি রাষ্ট্রের চিত্রের দিকে তাকান তবে আপনি দেখতে পাবেন যে প্রকৃত রাষ্ট্রীয় রূপান্তরগুলি এর সাথে মিলে যায় তা বেশ জটিল।

গ্রাফ

সাধারণ ঘোষণার মাধ্যমে, আমরা নেস্টলি জটিল “নির্বাচন” শব্দার্থবিজ্ঞানের সাথে পুনরায় ব্যবহার করেছি। এটি একটি বিশাল বিজয়!

এই সমস্তটির জন্য সম্পূর্ণ কোডটি ঠিক এখানে, প্রায় 130 লাইনের কোডে।

Source link

মন্তব্য করুন

আপনার ই-মেইল এ্যাড্রেস প্রকাশিত হবে না। * চিহ্নিত বিষয়গুলো আবশ্যক।