[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Comments on LET and constructors



 
 Thank you for the update.  I have some comments and questions on LET below,
 although even with LET, we can still only express a subset of possible 
 constructors, without a good reason why this subset is special.   
 
   For this reason, I would still rather do without the LET clause and would 
   still advocate either:
 
   - Having just simple SRFI-9 type constructors (my preference), or,
 
   - Making the user write the constructor explicitly.  This remains a
     more general solution.   
   
   
 Comments:
 =========
 
 - Calling it LET can be confusing, since it is unlike the usual LET.  
   How about FIELDS-WITH (see next).
 
 - The scoping is a bit weird in that references can precede bindings
   and bindings may not bind following references, e.g.
 
     (define-type foo ()
       (fields (x () x-value)         ; precedes binding
               (y () y-value))
       (let ((x-value 1)))
       (init! (r) (display x-value))  ; refers to toplevel x-value  
       (let ((y-value 2))))
     
   Also, in the above, presumably x-value refers to a toplevel
   binding and not the let-binding??
   
   How about making the scoping follow a more readable block structure
   such as (some variation of):
     
      (define-type foo ()
        (fields-with ((x-value 1)
                      (y-value 2))
          (x () x-value)
          (y () y-value)))
        (init! (r) (display x-value)) 
 
 - We can now indeed express the RATIONAL example: 
 
   (define-type rational (x y)   
     (let ((fields (if (= y 0)
                     (cons 1 0)    ; representation of infinity
                     (let ((common (gcd x y)))
                        (cons (/ x common)
                              (/ y common))))
     (fields (num   immutable (car fields))
             (denom immutable (cdr fields)))  
             
 - As you mention, LET-VALUES would be useful for efficiency 
   in examples such as RATIONAL above.
   
 - Even LET-VALUES (unless optimized away) would allocate more 
   intermediate values and presumably be slightly less efficient than an
   explicit constructor (in pseudocode):    
   
   (define-type rational
     (constructor (lambda (x y)
                    (if (= y 0)
                        (instantiate (parent)
                                     (num   1) 
                                     (denom 0))  ; representation of infinity
                        (let ((common (gcd x y)))
                          (instantiate (parent) 
                                       (num   (/ x common))
                                       (denom (/ y common)))))))	
     (fields num denom))        
 
 - We can do imperative things, but it is a hack:
   
    (define-type foo ()
      (let ((dummy (display "About to allocate a foo"))))
      (fields x y))
      
   This would be more natural to express in an explicit constructor.
   
 - We can express LETREC, but it is a hack, and inefficient
   without optimization:
 
    (define-type foo ()
      (let ((fields (letrec (even? (lambda (n) ......))
                            (odd?  (lambda (n) ......)))
                      (cons even? odd?))))
      (fields (method1 () (car fields))
              (method2 () (cdr fields))))
              
   This would be more natural to express in an explicit constructor. 
  
 - The same goes for the following pseudocode for a constructor with
   case-lambda.  It is expressible with LET, but a hack and inefficient:   
    
   (define-type rational
     (constructor (case-lambda 
                    ((n)   .....)
                    ((n d) .....))))            
 
 - What would be the "upgrade path" for adding possible extensions
   to make things like the previous two points less hackish?  Should future
   extensions have to piecemeal add LETREC, BEGIN, etc. clauses as extra syntax,
   eventually reproducing some sublanguage of standard Scheme? :-) 
  

 - Some constructors that are still not expressible:
   =================================================
 
   ;; Conditionally instantiating the parent with different arguments:
   ;; (actually this specific example can be expressed but would 
   ;;  require the test for (= y 0) to be duplicated):
                  
   (define-type rational 
     (parent finite-rational)
     (constructor (lambda (x y)
                    (if (= y 0)
                        (instantiate (parent 1 0))   
                        (instantiate (parent x y))))))    
                        
 
   ;; Local lexicals:
    
   (define-type generated-id
      (fields symbol)
      (constructor (let ((count 0))
                     (lambda ()
                       (set! count (+ count 1))
                       (string->symbol ................))))) 
                       
                       
   ;; Forwarding a variable number of arguments to parent 
   ;; (here called PARENT-APPLY):
   
   (define-type rational-with-comment (num . maybe-denom)
     (parent-apply rational num maybe-denom)
     (fields comment))
                     
                     
 Cheers
 Andre