Programmation fonctionnelle, familiarisez-vous avec - POO

J'aime expérimenter différents paradigmes et jouer avec différentes idées intéressantes (pour moi) (certaines se transforment en messages: un , deux ). J'ai récemment décidé de tester si je pouvais écrire du code orienté objet dans un langage fonctionnel.



Idée



Je cherchais l'inspiration d' Alan Kay , le créateur de la programmation orientée objet.



OOP pour moi signifie simplement la messagerie; stockage local, protection et masquage des Ă©tats + processus; et aussi une reliure extrĂȘmement tardive.

Original:



Pour moi, la POO ne signifie que la messagerie, la rĂ©tention locale et la protection et la dissimulation du processus d'Ă©tat, et la liaison extrĂȘmement tardive de toutes choses.

Je me suis dit que je serais heureux si je pouvais implémenter la messagerie et l'état interne.



En fait, c'est le problĂšme principal de toute l'idĂ©e - l'État.



Ă©tat



Nous ne devrions pas du tout avoir d'état dans la programmation fonctionnelle. Comment alors changer les valeurs dans le FP? Habituellement, en utilisant la récursivité (pseudocode):



function list_sum(list, result)
  if empty?
    result
  else
    list_sum(tail(list), result + first(list))
list_sum([1, 2, 3, 4], 0)


, . , , , .



. :



function some_object(state)
  msg = receive_message()
  next_state = process_message(msg)
  some_object(next_state)


, . . ? ? :



/ , .

. some_object(state) " " . .





, (, Go). receive_message() , - ( ). .





Haskell, , , , , - . , Clojure, .. , ( ).



, , Clojure :



(def user (atom {:id 1, :name "John"}))
@user ; ==> {:id 1, :name "John" }
(reset! user {:id 1, :name "John Doe"})
@user ; ==> {:id 1, :name "John Doe"}


, .





- . (, JavaScript -, ; ). .



? " " . , process_message(message) — .



Clojure clojure.core.async, . . , :



(ns functional-oop.object
  (:require [clojure.core.async :as async]))

(defn- datastructure [message-handler channel]
  {:message-handler message-handler
   :channel channel})


:



(defn- object-loop [obj state]
  (let [message (async/<!! (:channel obj))
        next-state ((:message-handler obj) obj state message)]
    (if (nil? next-state)
      nil
      (recur obj next-state))))


async/<!! . :message-handler (self, this), .



, — :



(defn init [state message-handler]
  (let [channel (async/chan 10)
        obj (datastructure message-handler channel)]
    (async/thread (object-loop obj state))
    obj))

(defn send-msg [obj msg]
  (async/>!! (:channel obj) msg))


, . send-msg. async/>!!, , - .





, , , ? . , string builder.



String builder — , :



builder = new StringBuilder
builder.add "Hello"
builder.add " world"
builder.build # ===> "Hello world"


:



(defn message-handler [self state msg]
  (case (:method msg)
    :add (update state :strings conj (:str msg))
    :add-twice (let [add-msg {:method :add, :str (:str msg)}]
                 (object/send-msg self add-msg)
                 (object/send-msg self add-msg)
                 state)
    :reset (assoc state :strings [])
    :build (do
             ((:callback msg) (apply str (:strings state)))
             state)
    :free nil
    ;; ignore incorrect messages
    state))

(def string-builder
  (object/init {:strings []} message-handler))


( , )



, , , , . 5 .



"hello world":



(object/send-msg string-builder {:method :add, :str "Hello"})
(object/send-msg string-builder {:method :add, :str " world"})

(let [result-promise (promise)]
  (object/send-msg string-builder
                   {:method :build
                    :callback (fn [res] (deliver result-promise res))})
  @result-promise)

;; ===> "Hello world"


. ?



- - . ? (promises).



. , . , .



@result-promise . , ( ).



add-twice, , .. . , , .. . . ( ?) , .



, - :



1.   :add-twice   "ha"
2.   :build  ,    "haha"


. - , :build , :add-twice :add ( , ).



, , . - , ( — Ruby on Rails) .

, , — . race condition ( ). — !:)



. . ?





— () , (). , , (, Ruby). .



"" . , ():



(ns functional-oop.klass.method
  (:require [functional-oop.object :as object]))

(defn- call-message [method-name args]
  {:method method-name :args args})

(defn call-on-object [obj method-name & args]
  (object/send-msg obj (call-message method-name args)))

(defn for-message [method-map msg]
  (method-map (:method msg)))

(defn execute [method self state msg]
  (apply method self state (:args msg)))


. — , : .



for-message. , . execute , : , , , .



:



(ns functional-oop.klass
  (:require [functional-oop.object :as object]
            [functional-oop.klass.method :as method]))

(defn- message-handler [method-map]
  (fn [self state msg]
    ;; Ignore invalid messages (at least for now)
    (when-let [method (method/for-message method-map msg)]
      (method/execute method self state msg))))


, :



(defn new-klass [constructor method-map]
  (object/init {:method-map method-map
                :constructor constructor
                :instances []}
               (message-handler {:new instantiate})))


, . , , , . new-klass klass, :new. , .



, — , — , ( ) . , , , .



, instantiate? :



(defn- instantiate [klass state promise-obj & args]
  (let [{:keys [constructor method-map]} state
        instance (object/init (apply constructor args)
                              (message-handler method-map))]
    (update state :instances conj @(deliver promise-obj instance))))


, , . .



:



(defn new-instance
  "Calls :new method on a klass and blocks until the instance is ready. Returns the instance"
  [klass & constructor-args]
  (let [instance-promise (promise)]
    (apply method/call-on-object klass :new instance-promise constructor-args)
    @instance-promise))


, - string-builder.



(defn- constructor [& strings]
  {:strings (into [] strings)})

(def string-builder-klass
  (klass/new-klass
   constructor
   {:add (fn [self state string]
           (update state :strings conj string))
    :build (fn [self state promise-obj]
             (deliver promise-obj
                      (apply str (:strings state)))
             state)
    :free (constantly nil)}))

(def string-builder-1 (klass/new-instance string-builder-klass))
(method/call-on-object instance :add "abc")
(method/call-on-object instance :add "def")
(let [result (promise)]
  (method/call-on-object instance :build result)
  @result)
;; ==> "abcdef

(def string-builder-2 (klass/new-instance string-builder-klass "Hello" " world"))
(method/call-on-object instance :add "!")
(let [result (promise)]
  (method/call-on-object instance :build result)
  @result)
;; ==> "Hello world!"


!



?



- ( , , ). . , . - . DSL , , .. Clojure.



. — , , .



- ?



— (). : , . ( ). , . :



# add
Title: Buy lots of toilet paper

# add
Title: Make a TODO list

# list
TODO list:
- Buy lots of toilet paper
- Make a TODO list

# complete
Index: 1

# list
TODO list:
- Buy lots of toilet paper
+ Make a TODO list

# exit




, ( ). , Haskell. , , . Haskell , . , - RabbitMQ.



, , . , , . .



, , , - :)



.





, Erlang. , .




All Articles