関数型プログラミングを理解する-OOP

私はさまざまなパラダイムを試して、さまざまな興味深い(私にとって)アイデアを試してみたいと思っています(そのうちのいくつかは投稿に変わります:1つ2つ)。私は最近、関数型言語でオブジェクト指向コードを記述できるかどうかをテストすることを決定しました。



考え



オブジェクト指向プログラミングの作成者であるAlan Kayからのインスピレーションを探していました



OOPは私にメッセージを送ることを意味します。ローカルストレージ、状態+プロセスの保護と非表示。また、非常に遅いバインディング。

元の:



私にとってのOOPとは、メッセージング、ローカルでの保持と状態プロセスの保護と非表示、およびすべてのものの極端な遅延バインディングのみを意味します。

メッセージングと内部状態を実装できれば幸せだと思いました。



実際、これがアイデア全体の主要な問題です-状態。



状態



関数型プログラミングでは、状態をまったく持つべきではありません。次に、FPの値を変更するにはどうすればよいですか?通常、再帰を使用(疑似コード):



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