Funktionale Programmierung, kennenlernen - OOP

Ich experimentiere gerne mit verschiedenen Paradigmen und spiele mit verschiedenen interessanten (fĂĽr mich) Ideen (einige davon werden zu Posts: eins , zwei ). Ich habe mich kĂĽrzlich entschlossen zu testen, ob ich objektorientierten Code in einer funktionalen Sprache schreiben kann.



Idee



Ich suchte nach Inspiration von Alan Kay , dem Schöpfer der objektorientierten Programmierung.



OOP bedeutet für mich nur Messaging; lokale Speicherung, Schutz und Verstecken von Zuständen + Prozessen; und auch extrem späte Bindung.

Original:



OOP bedeutet für mich nur Messaging, lokale Aufbewahrung und Schutz sowie das Verstecken von Staatsprozessen und die extreme Spätbindung aller Dinge.

Ich dachte, ich würde mich freuen, wenn ich Messaging und internen Status implementieren könnte.



Eigentlich ist dies das Hauptproblem der ganzen Idee - der Staat.



Bedingung



Wir sollten in der funktionalen Programmierung überhaupt keinen Zustand haben. Wie ändere ich dann die Werte im FP? Normalerweise mit Rekursion (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