Named parameters

Started by cormullion, March 01, 2008, 08:52:42 AM

Previous topic - Next topic

cormullion

One thing I liked about AppleScript was the way you could supply named parameters for functions. I've been wondering whether it's possible to do something similar in newLISP.



The problem to be solved is when you write a function that has a fair few arguments and you'd prefer to not have to supply them in a specific order, but you'd be happy to label them in the function call.



I think the recent use of the colon in newLISP might prevent its use for this purpose?

Cyril

#1
Quote from: "cormullion"The problem to be solved is when you write a function that has a fair few arguments and you'd prefer to not have to supply them in a specific order, but you'd be happy to label them in the function call.



I think the recent use of the colon in newLISP might prevent its use for this purpose?


My attempt (just out of the text editor, not tested heavily):


(define-macro (defkey header)
  (let (_makedef (fn (_x) (if (symbol? _x)
                            (list _x nil)
                            (list (_x 0) (eval (_x 1))))))
    (letex ((Name (header 0))
            (Default (map _makedef (1 header)))
            (Body (cons 'begin (args))))
      (define-macro (Name)
        (letn ((_default 'Default)
               (_arglist (args))
               (_offset-nil (find ': _arglist))
               (_offset-len (if _offset-nil _offset-nil (length _arglist)))
               (_pos-default (0 _offset-len _default))
               (_pos-arglist (0 _offset-len _arglist))
               (_key-arglist (_offset-len _arglist)))
          (map set (map first _default) (map last _default))
          (map set (map first _pos-default) (map eval _pos-arglist))
          (if (!= (% (length _key-arglist) 3) 0) (throw 'error))
          (dolist (_triple (explode _key-arglist 3))
            (local (_tag _key _val)
              (map set '(_tag _key _val) _triple)
              (if (!= _tag ':) (throw error))
              (set _key (eval _val))))
          Body)))))


And then:


(defkey (f a (b 20) c)
  (list a b c))

(f 10 :c (* 3 10))


Seems working for a first glance...
With newLISP you can grow your lists from the right side!

Jeff

#2
I've tried with colons before, but there was a problem due to newLISP regarding punctuation as a special case in argument lists.
Jeff

=====

Old programmers don\'t die. They just parse on...



http://artfulcode.net\">Artful code

Dmi

#3
Vote for named parameters!

I've playing with that once time before.

But there is a problem, and you can see it in your example just after typing 'f':
> f
(lambda-macro ()
 (letn ((_default '((a nil) (b 20) (c nil))) (_arglist (args)) (_offset-nil (find
     ': _arglist))
   (_offset-len
    (if _offset-nil
     _offset-nil
     (length _arglist)))
   (_pos-default (0 _offset-len _default))
   (_pos-arglist (0 _offset-len _arglist))
   (_key-arglist (_offset-len _arglist)))
  (map set (map first _default) (map last _default))
  (map set (map first _pos-default) (map eval _pos-arglist))
  (if (!= (% (length _key-arglist) 3) 0)
   (throw 'error))
  (dolist (_triple (explode _key-arglist 3))
   (local (_tag _key _val)
    (map set '(_tag _key _val) _triple)
    (if (!= _tag ':)
     (throw error))
    (set _key (eval _val))))
  (begin
   (list a b c))))

I found that this is very much code overhead, if that function will be called frequently.

So I've decide that there must be a sort of code preprocessor here, which will transform each combination of arguments used in a code to a short form of usage.

For ex: we have (f-internal a b c)

And (f :c 1) transforms to (f-internal nil nil 1) by preprocessor.



....but, if so, thinking in common, probably, here is a place for some engine that will allow to produce newlisp code from human-like language constructs.

But I have no idea here...
WBR, Dmi

Cyril

#4
Quote from: "Dmi"I found that this is very much code overhead, if that function will be called frequently.

So I've decide that there must be a sort of code preprocessor here, which will transform each combination of arguments used in a code to a short form of usage.


There is exactly one of the points of common newlisp critique  from CL party: we have no macros. What is called "macro" in newlisp is called f-expr in most other lisps. I myself have found newlisp good enough for me, but there is a sad true here: no "before execution" time macro transformer.
With newLISP you can grow your lists from the right side!

Lutz

#5
CL is a preprocessed and compiled language. Creating something like named parameters in an interpreted language creates a lot of overhead during runtime.



Any macro compiling or preprocessing would take away from the dynamic character of the language. But using the new reader function (renamed 'read-expr' in 9.3.3) you could write a preprocessor and create your own language (in between the limits of newLISP syntax and parser).

Cyril

#6
Quote from: "Lutz"you could write a preprocessor and create your own language


<I've forgot the name>'s law: complex enough program written in any language contains a lisp interpreter inside.



<I've forgot the name too>'s corollary: including programs written in lisp.
With newLISP you can grow your lists from the right side!

m i c h a e l

#7
Based on the work followed by the OCaml developers, we may find that keyword arguments go hand-in-hand with default arguments.



I've been playing with passing associations using the form '(parameter value) and then parsing (args) with assoc. Still working on it.



Here is something else with less overhead:


> ;; first, a demo function:
(define (f a b c) (cons (or a 1) (dup (list (or b 2) (or c 3)))))
(lambda (a b c) (cons (or a 1) (dup (list (or b 2) (or c 3)))))
> ;; notice how the function was defined in respect to the arguments
>
> ;; using the function in the usual way:
> (f)
(1 (2 3) (2 3))
> (f 9)
(9 (2 3) (2 3))
> (f 9 8 7)
(9 (8 7) (8 7))
> ;; what if we wanted to supply the third argument and default the first two?
> ;; our function definition allows us to do this:
> (f nil nil 9)
(1 (2 9) (2 9))
> ;; and to make it shorter and more attractive, we could do this:
> (set '_ nil)
nil
> (f _ _ 8)
(1 (2 8) (2 8))
>


Default arguments will not work with this method, as newLISP uses the nil argument instead of the default.



Just another tool in the toolbox ;-)



m i c h a e l

cormullion

#8
Quote from: "Lutz"Creating something like named parameters in an interpreted language creates a lot of overhead during runtime.


Oh well, no worries. I was thinking mainly of the 'ease of use' angle - although that never comes without a corresponding price to be paid. I just like the idea of not having to remember the arguments in the correct order every time.



The nearest I've got to what I want was


(define (func aslist)
  (letn ((l  (lookup 'len aslist))
         (w  (lookup 'width aslist))
         (h  (lookup 'height aslist)))
  (println {len is } l )
  (println {width  } w)
  (println {height } h)
  ))

(func '((width 20) (len 10) (height 30)))

len is 10
width  20
height 30


as long as I remember that the names are different.

Lutz

#9
this one avoids the extra pair of parenthesis and quote:


(define-macro (foo)
(local (len width height)
(bind (args) true)
(println "len:" len " width:" width " height:" height)
))


> (foo (width 20) (height 30) (len 10))

len:10 width:20 height:30


ps: added the true flag, which was introduced later in 9.4.5. This insures that parameters are evaluated:


> (foo (width (+ 15 5)) (height 30) (len 10))
len:10 width:20 height:30

Jeff

#10
The problem with that is that the arguments bound within the function are bound externally to the function.  You could use the same technique I used in destructuring-bind:


(define-macro (destructuring-bind)
  (letex ((unifier (args 0))
          (target (args 1))
          (body (rest (rest (args)))))
    (let unifier
      (bind (unify 'unifier 'target))
      (dolist (expr 'body)
        (eval expr)))))


Using letex and let together to create a valid let param-list:


(define (foo)
  (letex ((arg-list (args)))
    (let arg-list
      (println x)
      (println y))))

(foo '(x 1) '(y 2))


The real solution is to use a factory function that would expand a function body to the above.
Jeff

=====

Old programmers don\'t die. They just parse on...



http://artfulcode.net\">Artful code

newBert

#11
Quote from: "Lutz"this one avoids the extra pair of parenthesis and quote:


(define-macro (foo)
(local (len width height)
(bind (args))
(println "len:" len " width:" width " height:" height)
))


> (foo (width 20) (height 30) (len 10))

len:10 width:20 height:30


ps: or use (apply set (flat (args))) instead of (bind (args)), if assignments have to be evaluated.


Wow, I say what I've said in another http://www.alh.net/newlisp/phpbb/viewtopic.php?p=11996#11996">topic :

With newLISP ...
Quote... in any case we can get through!

:)
<r><I>>Bertrand<e></e></I> − <COLOR color=\"#808080\">><B>newLISP<e></e></B> v.10.7.6 64-bit <B>>on Linux<e></e></B> (<I>>Linux Mint 20.1<e></e></I>)<e></e></COLOR></r>

Dmi

#12
Quote from: "Cyril"
There is exactly one of the points of common newlisp critique  from CL party: we have no macros. What is called "macro" in newlisp is called f-expr in most other lisps. I myself have found newlisp good enough for me, but there is a sad true here: no "before execution" time macro transformer.

That's right, but not exactly, bacause of nature of newlisp.

In fact we can already have a preprocessor that can parse already loaded context, using (symbols) etc. and transform any sequences of symbols and values into newlisp-compatible function calls. And no (reader) needed here despite it's great improvement either.



The bad news is that there's no stable and useful concept & technics for such transformations yet.
WBR, Dmi

cormullion

#13
Quote from: "Jeff"Using letex and let together to create a valid let param-list:


(define (foo)
  (letex ((arg-list (args)))
    (let arg-list
      (println x)
      (println y))))

(foo '(x 1) '(y 2))



I like this solution - it's idiomatic but short and easy to remember and doesn't require pre-definitions. Will try it out. Thanks!

Jeff

#14
It shouldn't be too difficult to write a factory-function macro that would let you define a function and have it expand to that.
Jeff

=====

Old programmers don\'t die. They just parse on...



http://artfulcode.net\">Artful code