Shen-Language / shen-cl

Shen for Common Lisp (Unmaintained)
BSD 3-Clause "New" or "Revised" License
122 stars 11 forks source link

Error with the Shen properties #14

Closed bluejay77 closed 6 years ago

bluejay77 commented 6 years ago

Dr Tarver made the "o-o.shen" OO capability. I have in my hands the Paul Graham book On Lisp, and its Ch 25 contains a discussion of object oriented LISP.

I have been tinkering with making a Paul Graham type version of the o-o.shen. This one would be called oo.shen.

The file oo.shen is what I have made so far. The file ooEx.shen are some examples.

There is something strange here. Below is a copy of the output.

Shen, copyright (C) 2010-2015 Mark Tarver
www.shenlanguage.org, Shen 21
running under Common Lisp, implementation: SBCL
port 2.2 ported by Mark Tarver

(0-) \\ If I begin by loading the file ooEx.shen, it works:
(load "ooEx.shen")
File begins.
[]

defclass definitions
[]
The function COMMON-LISP-USER::object is undefined.

(1-) \\ But if I first load oo.shen and then ooEx.shen, the below happens:
(load "oo.shen")
defclass-macro
process-slots
instance
oo.type#o-o
attribute-type
class?
oo.create-class
instance
assign
cl-slots
cl-parents
cl-name
make-instance
iget
iput
[]

run time: 1.2080000415444374 secs
loaded

(2-) (load "ooEx.shen")
value not found

(3-) \\ Mysterious "value not found" error
(QUIT)

Process inferior-shen finished

Attachments: oo.shen ooEx.shen

File oo.shen

(package oo [defclass defclass-macro assign instance attribute class
              make-instance iget iput object
          cl-slots cl-parents cl-name process-slots
          attribute-type class? ]

(defmacro defclass-macro
  [defclass Class SuperClasses | Slots] 
  -> (create-class Class SuperClasses (process-slots Class Slots)))

\\ AJY 2018-04-24 (shen.typecheck ...) removed, changed in Shen 20 -->.
\\ Must be possible to have no (new) slots

(define process-slots
  Class [Attribute Type Value] -> \\ one slot?
      (do (put Class Attribute Type) \\ stored as properties
          [[Attribute Value]])
  Class [Attribute Type] -> 
      (do (put Class Attribute Type) [[Attribute]]) 
  Class [Attribute Type Value , | Slots] -> \\ a list of slots?
      (do (put Class Attribute Type)
          [[Attribute Value] | (process-slots Class Slots)])
  Class [Attribute Type , | Slots] -> \\ list of slots?
      (do (put Class Attribute Type)
          [[Attribute] | (process-slots Class Slots)])
  Class [] -> [[]] \\ no slots: modify nothing
  Class _  -> \\ otherwise: there is a syntax error
      (error "syntax error in class definition of ~A~%" Class))

(declare instance [[class Class] --> [instance Class]])

(datatype o-o

  if (class? Class)
  ______________________
  Class : (class Class);

  if (= (attribute-type Class Attribute) Type)
  __________________________________
  Attribute : (attribute Class Type);

  Instance : (instance Class);
  Value : A;
  Attribute : (attribute Class A);
  _____________________________________________________ 
  (assign Instance Attribute Value) : (instance Class);)

(define attribute-type
  Class Attribute -> (trap-error (get Class Attribute) (/. E [])))     

(define class?
  Class -> (cons? (trap-error (get Class slots) (/. E false))))  

(define create-class
  Class SuperClasses Slots ->
      (let SuperSlots (mapcan (function instance) SuperClasses)
           ClassSlots (append Slots SuperSlots)
           Create (put Class slots ClassSlots) \\ property slots: list
       Paren (put Class parents SuperClasses) \\ super i e parents
       Name (put Class class-name Class) \\ her name
           Class))

\\ instance creates an instance of the class,
\\ which is a list of lists

(define instance
  Class -> (get Class slots))

\\ assign assigns the value of a slot of an instance

(define assign
  [[Attribute | _] | Slots] Attribute Value ->
      [[Attribute Value] | Slots]
  [Slot | Slots] Attribute Value ->
      [Slot | (assign Slots Attribute Value)])

(define cl-slots
    Class -> (get Class slots))

(define cl-parents
    Class -> (get Class parents))

(define cl-name
    Class -> (get Class class-name))

(define make-instance
    Class InstanceName ->
    (do
        (put InstanceName instanceSlots (instance Class))
    InstanceName))

(define iget
    InstanceName SlotName ->
    (head (tail (head (assoc SlotName (get InstanceName instanceSlots))))))

(define iput
    InstanceName SlotName Val ->
    (let
        Lst (get InstanceName instanceSlots)
    NewLst (assign Lst SlotName Val)
    (put InstanceName instanceSlots NewLst)))

(do
    (put object slots [place-holder])
    (put object parents [object]) \\ super i e parents: Its own super
    (put object class-name object) \\ her name
    (put object place-holder list) \\ Program requires at least one slot
    [])

)

File ooEx.shen

(do (output "File begins.~%") [])

(do (output "~%defclass definitions~%") [])

(defclass int (object) val number 0) \\ integer class

(defclass fl (int)) \\ float class

(defclass cmx (fl)) \\ complex class

(do (output "~%make-instance definitions~%") [])

(set o (make-instance object o))

(set i (make-instance int i))

(set f (make-instance fl f))

(set c (make-instance cmx c))
rkoeninger commented 6 years ago

I'm able to reproduce the problem while shortening ooEx.shen to just:

(defclass int (object) val number 0)
(defclass fl (int))

The second line causes the error. If ooEx.shen is only:

(defclass int (object) val number 0)

Then there is no error. This also works:

(defclass int (object) val number 0)
(set i (make-instance int i))

So we can create an instance of the int class after it has been defined, we just can't extend it (I infer class fl is trying to extend int)...

... and that's only true when trying to load those statements from a file. If I (load "oo.shen") and then enter the 2 statements from the first snippet into the repl, there's no error. So the problem occurs when using set/value/put/get in some particular way while loading a file.

Shen, copyright (C) 2010-2015 Mark Tarver
www.shenlanguage.org, Shen 21
running under Common Lisp, implementation: SBCL
port 2.2 ported by Mark Tarver

(0-) (load "oo.shen")
defclass-macro
process-slots
instance
oo.type#o-o
attribute-type
class?
create-class
instance
assign
cl-slots
cl-parents
cl-name
make-instance
iget
iput
[]

run time: 0.31300000101327896 secs
loaded

(1-) (defclass int (object) val number 0)
int

(2-) (defclass fl (int))
fl

(3-) (set f (make-instance fl f))
f
rkoeninger commented 6 years ago

Tried it again - it works when I make ooEx.shen just:

(defclass int (object) val number 0)
(oo.create-class fl [int] [[]])

but this doesn't:

(defclass int (object) val number 0)
(defclass fl (int))

So if you inline the second use of the defclass macro in ooEx.shen, it works, and it works if you input the contents of ooEx.shen directly into the repl.

And removing the package oo from around the definitions in oo.shen doesn't change anything.

tizoc commented 6 years ago

The defclass-macro is side-effectful and each declaration depends on the effects of the previous one. But the Shen reader expands macros from right to left, this is what is causing the error when loading the file but not in the REPL.

The macro should instead generate the code that will create the class instead of calling it at expansion time, that way you ensure that the order is respected. It would look something like this but not exactly:

(defmacro defclass-macro
  [defclass Class SuperClasses | Slots]
  -> [create-class Class SuperClasses [process-slots Class Slots]])

The reason I say not exactly is that you have to make sure the Slots part gets generated as a literal list and not a function call ([val number 0] instead of (val number 0) in the case of the int definition).

tizoc commented 6 years ago

You can use this:

(define consify
  [X | Y] -> [cons (consify X) (consify Y)]
  X -> X)

(defmacro defclass-macro
  [defclass Class SuperClasses | Slots]
  -> [create-class Class (consify SuperClasses) [process-slots Class (consify Slots)]])

After making these changes it works for me:

(0-) (load "oo.shen")
oo.consify
defclass-macro
process-slots
instance
oo.type#o-o
attribute-type
class?
oo.create-class
instance
assign
cl-slots
cl-parents
cl-name
make-instance
iget
iput
[]

run time: 0.083874 secs
loaded

(1-) (load "ooEx.shen")
File begins.
[]

defclass definitions
[]
int
fl
cmx

make-instance definitions
[]
o
i
f
c

run time: 0.0023029999999999995 secs
loaded

(2-) 
tizoc commented 6 years ago

Closing this btw because it is not a bug, either in Shen/CL or the Shen Kernel.

tizoc commented 6 years ago

Btw, the right-to-left macro expansion order is probably just an accidental implementation detail, and not something that Mark made work like that by design. I don't know how hard it is to change, or if it is even worth it (I myself don't like the idea of macros having side effects much, and because of that I don't write code that does it, and because of that expansion order is never an issue).

But I also understand that the right-to-left expansion order is kinda confusing and unexpected.

In the case that an macro expansion order change is something that needs to be discussed, the place to do so is in the mailing list, because introducing that change in the ShenBSD kernel would introduce a huge incompatibility with ShenProfessional, unless Mark also thinks it is a good idea to enforce left-to-right expansion order and does it too.