More Source for Lutz's Language Design Contemplation

Started by m i c h a e l, July 29, 2006, 08:11:17 PM

Previous topic - Next topic

m i c h a e l

I find I use this pattern quite a bit:


(define (f a b)
(set
'a (or a 1)
'b (or b 3))
(+ a b b a))
   



This is how a newLISPer gives default values to parameters.



Using a pattern becomes easier if it is encoded into the language. Rather than having to explain the steps necessary to implement the idiom (usually to a newbieLISPer), you can just point them to the right function.



Here is an example using an imaginary "idiom-encapsulating function" called default:


(define (f a b)
(default
a 1
b 2)
(+ a b b a))


With the steps required to give default values to parameters packaged up into a function, we can explain to others (or remember for ourselves) how to easily use defaults in function definitions.



In the spirit of implicit indexing, we could further simplify the code to something like this:


(define (f (a 1) (b 3))
(+ a b b a))


As with all my observations, they are not meant to be taken as implementation requests. Just aesthetic stirrings within my soul.



FFtI (Feel Free to Ignore)



m i c h a e l

m i c h a e l

#1
And in true newLISP spirit, it is easy to find what you seek. Here is my first attempt at default:


(define-macro (default)
(map
(fn (each.pair)
(set (each.pair 0)
(or
(eval (each.pair 0))
(eval (each.pair 1)))))
(pair-up (args))))

(global 'default)


The pair-up function is user-defined and turns this: (1 2 3 4 5 6) into this: ((1 2)(3 4)(5 6)). I'm sure someone will see some way to improve this that could eliminate the need for pair-up entirely, so I'll hold off on embarrassing myself by posting the code for it right now.



I've tested it on the command line and used it as part of a function definition within a file, and so far it seems to work.



Have fun.



m i c h a e l

Lutz

#2
Both are good suggestions for handling defaults in function parameters. I liked this most:



(define (foo (a 1) (b 3))
   (println "a:" a " b:" b))

(foo) => a:1 b:3

(foo 8) => a:8 b:3

(foo 8 9) => a:8 b:9


and after a lot of experimentation I got it going without loosing any performance in function call overhead. In version 8.9.3 this default mechanism will be available.



Lutz



ps: the other form introducing a 'default' fiunction has more general usage, but is also longer.

m i c h a e l

#3
> after a lot of experimentation



So that is why you have been so mysteriously quiet ;-)



> I got it going without loosing any performance in function call overhead.



Fantastic! Good job, Lutz.



m i c h a e l

starseed

#4
Looks good!



And while you're at it ...



One thing about Rebol I really like is it's inbuilt help-system.

When you define a function you add some doc-strings, e.g.



(define (x+y x "first number" y "second number")

      "adds 2 numbers"

      (+ x y))



And with help, you get the information



(help x+y)

adds 2 numbers

x - first number

y - second number



Would it be interesting for you to add something like this?



(BTW, I know, the "adds 2 numbers" is already possible, but how much of a speed problem does it pose?)



Thanks, Ingo

starseed

#5
Is it a design decision, or an implementation detail, that it is not possible to overwrite predefined functions?



I like to do small adjustments to system functions, e.g. I might like to enhance import, so that I can import several functions at once. It has it's problems when exchanging code, but makes ones own life easier at times.



What I do now is, to just use my own context. It has the added benefit, that by getting the visual reminder, I know that I may use some non-standard versions of functions.

starseed

#6
Is it by design, that you drop out of a context, back into main, when you get an error?



I'd much rather stay in the context. At least when working at the repl, it makes more sense, or when using newlispEvalStr, then you don't even get the visual reminder, that you are suddenly back in the MAIN context.

starseed

#7
OK, I'll shut up for now, just one last thing, though ...



I find using (args 0) ... in macros very unreadable, how do you feel about adding gensym? On every call, it creates a symbol that's guaranteed to be different from every other symbol in the system.

Lutz

#8
Welcome to newLISP and the group starseed!


QuoteIs it a design decision, or an implementation detail, that it is not possible to overwrite predefined functions?


you can overwite protected/predefined symbols with 'constant':


(constant '+ add)  ; + now works ints and floats
(constant 'print (fn (x y z) ...)) ; redefine a builtin


QuoteIs it by design, that you drop out of a context, back into main, when you get an error?


This is by design to reach a defined known state, i.e. for a user defined error handler.


QuoteI find using (args 0) ... in macros very unreadable, how do you feel about adding gensym?


not sure about 'gensym' have to think about it. 'define-macro' is rarely used in newLISP, so the need for hygienic macros doesn't come up frequently and when it does, most people use (args). In other languages macros are frequently used to do variable expansion. In newLISP this is done not with 'define-macro' but with functions like 'expand' and 'letex'.



Lutz

starseed

#9
Thank you Lutz,



for all your ansers.



I'm mostly interested in _small_ and _multiplatform_ so no need to bloat newLisp. ;-)



I just wanted to express my ideas while they're fresh.





Ingo