Current section
Files
Jump to
Current section
Files
examples/object-via-process.lfe
;; Copyright (c) 2013-2020 Duncan McGreggor <oubiwann@gmail.com>
;;
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;; File : object-via-process.lfe
;; Author : Duncan McGreggor
;; Purpose : Demonstrating simple OOP with lightweight Erlang processes
;; The simple object system demonstrated below shows how to do the following:
;; * create objects
;; * call methods on those objects
;; * have methods which can call other methods
;; * update the state of an instance variable
;;
;; Note, however, that his example does not demonstrate inheritance.
;;
;; To use the code below in LFE, do the following:
;;
;; $ ./bin/lfe
;;
;; Load the file and create a fish-class instance:
;;
;; lfe> (slurp "examples/object-via-process.lfe")
;; #(ok object-via-process)
;; lfe> (set mommy-fish (init-fish "Carp"))
;; #Pid<0.33.0>
;;
;; Execute some of the basic methods:
;;
;; lfe> (send mommy-fish 'species)
;; "Carp"
;; lfe> (send mommy-fish 'move 17)
;; "The Carp swam 17 feet!"
;; lfe> (send mommy-fish 'id)
;; "47eebe91a648f042fc3fb278df663de5"
;;
;; Now let's look at modifying state data (e.g., children counts):
;;
;; lfe> (send mommy-fish 'children)
;; ()
;; lfe> (send mommy-fish 'children-count)
;; 0
;; lfe> (set baby-fish-1 (send mommy-fish 'reproduce))
;; #Pid<0.34.0>
;; lfe> (send baby-fish-1 'id)
;; "fdcf35983bb496650e558a82e34c9935"
;; lfe> (send mommy-fish 'children-count)
;; 1
;; lfe> (set baby-fish-2 (send mommy-fish 'reproduce))
;; #Pid<0.35.0>
;; lfe> (send baby-fish-2 'id)
;; "3e64e5c20fb742dd88dac1032749c2fd"
;; lfe> (send mommy-fish 'children-count)
;; 2
;; lfe> (send mommy-fish 'info)
;; (#(id "f05064ffcf92d7b3e72968fd481abbd0")
;; #(species "Carp")
;; #(children
;; ("d53a426c732c938f996a1c2520bb621f" "15fede691ab3f96e9e3df248d37b7b55")))
(defmodule object-via-process
(export all))
(defun init-fish (species)
"This is the constructor that will be used most often, only requiring that
one pass a 'species' string.
When the children are not defined, simply use an empty list."
(init-fish species ()))
(defun init-fish (species children)
"This constructor is useful for two reasons:
1) as a way of abstracting out the id generation from the
larger constructor, and
2) spawning the 'object loop' code (fish-class/3)."
(spawn (lambda ()
(fish-class species children (gen-id)))))
(defun fish-class (species children id)
"This function is intended to be spawned as a separate process which is
used to track the state of a fish. In particular, fish-class/2 spawns
this function (which acts as a loop, pattern matching for messages)."
(let ((move-verb "swam"))
(receive
(`#(,caller move ,distance)
(! caller (lists:flatten
(io_lib:format "The ~s ~s ~p feet!"
`(,species ,move-verb ,distance))))
(fish-class species children id))
(`#(,caller species ())
(! caller species)
(fish-class species children id))
(`#(,caller children ())
(! caller children)
(fish-class species children id))
(`#(,caller children-count ())
(! caller (length children))
(fish-class species children id))
(`#(,caller id ())
(! caller (lists:flatten id))
(fish-class species children id))
(`#(,caller info ())
(! caller `(#(id ,id)
#(species ,species)
#(children ,children)))
(fish-class species children id))
(`#(,caller reproduce ())
(let* ((child (init-fish species))
(child-id (send child 'id))
(children-ids (lists:append children `(,child-id))))
(! caller child)
(fish-class species children-ids id))))))
(defun send (object method-name)
"This is a generic function, used to call into the given object (class
instance)."
(send object method-name '()))
(defun send (object method-name arg)
"This is a generic function, used to call into the given object (class
instance)."
(! object `#(,(self) ,method-name ,arg))
(receive
(data data)))
(defun gen-id ()
(let (((binary (id (size 128))) (crypto:strong_rand_bytes 16)))
(io_lib:format "~32.16.0b" (list id))))