Pemrograman fungsional, berkenalan - OOP

Saya suka bereksperimen dengan berbagai paradigma dan bermain dengan berbagai ide menarik (untuk saya) yang berbeda (beberapa di antaranya berubah menjadi tulisan: satu , dua ). Saya baru-baru ini memutuskan untuk menguji apakah saya bisa menulis kode berorientasi objek dalam bahasa fungsional.



Ide



Saya mencari inspirasi dari Alan Kay , pencipta pemrograman berorientasi objek.



Bagi saya OOP berarti mengirim pesan; penyimpanan lokal, perlindungan dan menyembunyikan proses negara +; dan juga sangat terlambat mengikat.

Asli:



OOP bagi saya hanya berarti pengiriman pesan, penyimpanan lokal dan perlindungan dan menyembunyikan proses negara, dan sangat mengikat semua hal.

Saya pikir saya akan senang jika saya bisa menerapkan perpesanan dan keadaan internal.



Sebenarnya, ini adalah masalah utama dari keseluruhan gagasan - negara.



kondisi



Kita seharusnya tidak memiliki status sama sekali dalam pemrograman fungsional. Lalu bagaimana cara mengubah nilai dalam FP? Biasanya, menggunakan rekursi (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