Arc Forumnew | comments | leaders | submit | kennytilton's commentslogin
4 points by kennytilton 6447 days ago | link | parent | on: Special Variables

I thought about suggesting special variables when pg asked for suggestions, but I plan on sticking with CL so I thought it inappropriate to... open my big yap? :)

-----


I have heard that even universities are held hostage by students who view college as merely job training, demanding courses in the languages business cannot see past.

Seems to me the unis should fight that fight, but there are a lot of unis in the US, they have to think about marketing, too.

Perhaps as the IT job market continues to shrink the masses will move on to something else and let the computer science departments go back to teaching algorithms.

-----

3 points by eds 6448 days ago | link

I haven't met many students that are set on learning Java and only Java (or insert other popular language of choice), mostly its just that they don't know anything better is out there. I have, however, met professors who hold very strong, and wildly incorrect views on Lisp (many of which probably haven't been accurate for 20 years or more). But how is a college freshman supposed to tell a guy with a PhD he is completely incorrect?

I've heard claims that they'll let me program in whatever language I want in upper division, but I have to wonder if that is true or not.

-----

3 points by kennytilton 6447 days ago | link

I am just telling you what I heard. From professors. At unis in England, come to think of it. Oh, and Brown, a top US school. Sad, I know. :(

-----

1 point by jcl 6445 days ago | link

IIRC, Brown has two introductory computer science tracks -- one that starts with Scheme and the other with Java, with the Scheme course recommended for majors. They've got one of the PLT Scheme implementors on faculty, too, so some of the advanced courses are Scheme-based, as well.

-----


I should add that dsb and thus defun support optional and keyword args at the same time only because it is possible; I cannot imagine it ever being sensible. :)

-----

2 points by are 6447 days ago | link

Very nice work on supporting both opt and key args.

Although if you had something like this:

(defun fn (a &o (b 'b) (c 'c) &k (d 'd))

with a usage like this:

(fn 1 'd 'e)

... how would you know whether:

1) 'd is the value of the first opt arg and 'e is the value of the second (the key arg unsupplied)

or

2) 'd is the key for the key arg, and 'e is its supplied value (the 2 opt args unsupplied)

?

Maybe I'm missing something here, but it seems to me that unless you have special syntax for keywords, you will get into trouble.

And if you have to introduce special syntax for keys anyway, it is just as well to make every single argument keyable on its symbol (even vanilla ones), and just worry about combining &o and &rest (which should then be doable).

-----

3 points by kennytilton 6447 days ago | link

"with a usage like this: (fn 1 'd 'e) how would you know..."

The interpretation is that d and e are the two optional args, so any caller wanting to supply a keyword arg has to supply the optionals. Recall that I said it was possible, not sensible. :) But in tool design I think we should let users hang themselves rather than guess (perhaps wrongly) that no one would ever come up with a good use for such a thing.

A second, lesser motivation is that CL works that way.

-----

2 points by kennytilton 6447 days ago | link

The gang on comp.lang.lisp reminded me of (in effect):

  (def read-from-string (s &o eof-error-p eof-value 
                           &k start end preserve-whitespace)
     ...)
Which does make me think the guess about keywords being added as an afterthought might be spot on.

-----

2 points by kennytilton 6447 days ago | link

c.l.lisp just offered a much better observation: the optional args above are standard for the various "read" functions, and the start and end keywords are standard for string functions. Read-from-string then is inheriting consistently from both families.

-----

2 points by eds 6447 days ago | link

Even CL's special keyword syntax doesn't save you from optional and keyword confusion:

  [1]> (defun test (a &optional (b 'b) (c 'c) &key (d 'd))
         (list a b c d))
  TEST
  [2]> (test 1 :d :e)
  (1 :D :E D)
Optional parameters always bind first in CL, and I believe dsb is written to mimic that behavior.

Having all parameters be keyword arguments as well might be interesting, but it wouldn't avoid optional/keyword confusion.

-----

1 point by are 6446 days ago | link

> Having all parameters be keyword arguments as well might be interesting, but it wouldn't avoid optional/keyword confusion.

Why not?

Let's say you have a function with 3 standard args followed by 2 opt args. So you have 5 args, all keyable on the symbol you give them in the def.

Let's further say that in a call to this function, I key the middle standard arg (#2 in the def) plus the first opt arg (#4 in the def) and also, I omit the second opt arg (#5 in the def). So, I'm supplying two standard args apart from the two args I'm keying. Then the function call parser would know, after identifying the 2 keyed args and matching them to positions #2 and #4 in the def, that the first non-key arg supplied corresponds to position #1 in the def, the second non-key arg supplied corresponds to position #3 in the def, and that an arg for position #5 in the def is missing, leading to the usage of the default value for this opt arg.

This would even work when you want to raise an error for a non-supplied, non-opt arg.

Wouldn't this work quite intuitively (keying an arg when calling a function "lifts it out" of the normal "vanillas then optionals" argument sequence, shortening that sequence, put keeping a well-defined order for it)? (You would need special syntax for keys in this proposal. My suggestion is a colon appended to the arg symbol, rather than prepended, like in CL.)

Can someone give a counterexample if they think this somehow wouldn't work?

&rest args are left as an exercise for the reader :-)

-----

2 points by almkglor 6447 days ago | link

It could be, if you had an old function that was using optional arguments, and then eventually had to add even more arguments, which you finally decide to make keyworded; without breaking existing code, you can support both optional and keyword args.

What I would like to see is optional, keyword, and rest arguments. Imagine something like this:

  (def foo (k v)
    'type (k int)
    (+ k v))

-----

3 points by kennytilton 6447 days ago | link

The syntax to add rest args, if it followed the CL example, would be:

  (dsb (r1 &o o1 &r rest &k k1) data ....)
If data was (1 2 'k1 3) then most params would be bound as expected and then rest would be bound to (k1 3).

But now the data (1 2 'k1 3 4) causes an error "Key list is not even", ie, once you say &k you undertake certain obligations as the caller. Even if you even up the list:

   (1 2 'k1 2 3 5)
...you get an error "3 is an invalid keyword", because 3 appears in a keyword position. This can be avoided by announcing your intention to have undeclared keywords:

   (a b &rest rest &key k1 &allow-other-keys)
That of course is CL, and it kinda makes my day that if I were crazy enough to extend dsb in Arc I would end up with:

   (a b &r rest &k k1 &aok)

-----

1 point by kennytilton 6446 days ago | link

"...if I were crazy enough to extend dsb..."

Was there ever any doubt? :)

Still requiring extensions from earlier posts:

  (def dsb-params-parse (params)
    (withs (reqs nil key? nil opt? nil keys nil opts nil
          rest (mem '&r params)
          rest-var (cadr rest)
          aok? (find '&aok cddr.rest)
           resting? nil
           no-mas nil)
    (each p params
      (if no-mas (assert nil "No params &aok, OK?" ',params)
        (is p '&o) (do (assert ~opt? "Duplicate &o:" ',params)
                       (assert ~key? "&k cannot precede &o:" ',params)
                     (= opt? t))
        (is p '&k) (do (assert ~key? "Duplicate &k:" ',params)
                       (= key? t))
        (is p '&r) (= resting? t)
        (is p '&aok) (= no-mas t)
        key? (push-end p keys)
        (and opt? (no resting?)) (push-end p opts)
        (no resting?) (do (assert (~acons p) "Reqd parameters need not be defaulted:" p)
                           (push-end p reqs))))
    (prt 're-obj!!!!! reqs opts rest-var keys aok?)
    (obj reqs reqs opts opts rst rest-var keys keys aok? aok?)))
And man was I happy to have the above as a breakout from the macro itself:

  (mac dsb (params data . body)
  (w/uniq (tree kvs valid-keys aok?)
    `(withs (,tree ,data
              ,@(let plist (dsb-params-parse params)
                  (prn `(reqs ,plist!reqs))
                  (prn `(rst ,plist!rst))
                  (prn `(keys ,plist!keys))
                  (prn `(&aok ,plist!aok?))
                  (with (n -1)
                    (+ (mappend [list _ `(nth ,(++ n) ,tree)] plist!reqs)
                      (mappend [list (carif _) `(if (< ,(++ n) (len ,tree))
                                                    (nth ,n ,tree)
                                                  ,(cadrif _))] plist!opts)
                      `(,plist!rst (nthcdr ,(++ n) ,tree))
                      `(,valid-keys ',plist!keys)
                      `(,aok? ',plist!aok?)
                      `(,kvs (do (prt 'foing ,valid-keys)
                                 (when (and ,plist!rst ,valid-keys)
                                   (assert (even (len ,plist!rst)) "Keyword list not even" ,plist!rst)
                                   (let ,kvs (pair ,plist!rst)
                                     (prt 'vetting ,valid-keys 'againt ,kvs)
                                     (unless ,aok?
                                       (assert (all [find (car _) ,valid-keys] ,kvs)
                                         "Invalid key in" (map car ,kvs)))
                                     (prt 'kvs!!!!! ,kvs)
                                     ,kvs))))
                      (mappend [list (carif _)
                                 `(do (prt 'kvs!!! ',(carif _) ,kvs)
                                      (aif (assoc ',(carif _) ,kvs)
                                        cadr.it
                                        ,(cadrif _)))] plist!keys)))))
       ,@body)))
What is missing (largely) is graceful handling of invalid use.

-----

1 point by almkglor 6446 days ago | link

> What is missing (largely) is graceful handling of invalid use.

(err:tostring:write ...) works fine for me for reporting errors and aborting

-----

2 points by kennytilton 6447 days ago | link

Ok, and then going forward only the new code has the burden of supplying optionals... hmmm, refactoring at 7am with the demo to the CEO scheduled for 9am?...

Lock and load! Add the keywords!! :)

-----


"Would anyone really care if they just went away?"

You mean a cons cell only has a CAR slot, there is no CDR? :)

Or that you would ban me from storing anything other than nil or another cons in the cdr?

ie, dotted lists are not really "there", so they cannot go away.

-----

2 points by nzc 6448 days ago | link

Neh, what I meant was, you can have a thing that quacks like a list and never see the need for a dotted pair.

That can be hidden below the abstraction. See, case in point, lists/arrays in Ruby, which are more or less lists without the car and cdr part. Although, you do get a 'nil' if you access beyond the end of the array. ;-)

-----

9 points by kennytilton 6448 days ago | link

Ruby lists can quack? Bah, let me know when they can mate, that's when you know you have the same species: ie, can two Ruby more-or-less lists have shared structure, formed say by ruby-consing a different element onto the same list?

And ducks can nest. How about Ruby lists? I have always thought the language should have been called Trep, for Tree Processing language. Can I build a Ruby tree and then quickly flatten by juggling some Ruby ducktails?

As for "That can be hidden below the abstraction", OK, now you have a duck: moving smoothly on the surface and paddling like crazy beneath.

The whole comparison just seems (sorry on more levels than one) daffy.

-----

3 points by absz 6448 days ago | link

Indeed, you are correct. Ruby[1] doesn't have "native" lists, it has native arrays. Observe:

  irb(main):001:0> [].class
  => Array
There is no Ruby cons, nor Ruby car, nor Ruby cdr. There is y.unshift x or [x] + y for cons (though these cannot constructed "dotted Arrays"), arr[0] or arr.first for car, and arr[1..-1] for cdr, but they denote no structure-sharing. However, flattening would merely be arr.flatten—of course it doesn't work by cdr-juggling, but that's because there are no cdrs.

I'm not really sure what the grandparent post means: "lists without the car and cdr part"? That sounds to me like a very good definition of an... array![2]

Also, that set of duck puns/references was fantastic. Well done.

[1]: Which is a very nice language.

[2]: Well, without the O(1) access time, but I digress.

-----

5 points by kennytilton 6447 days ago | link

"that set of duck puns/references was fantastic."

I can take no credit -- they just popped out of my keyboard. There must be some poorly understood connection between algorithms, data structures, and waterfowl.

-----

3 points by absz 6447 days ago | link

I smell a Ph.D. thesis afoot... awing?

Actually, they (the infamous "they") did a study, and apparently, ducks are the funniest animal. Take that with as large a grain of salt as you think is appropriate :)

-----

1 point by vincenz 6432 days ago | link

This is directly related to the existence of "daffy duck"

-----


(a) No apologies needed, I kinda anticipated this question. :) (b) Perhaps the best use case was just added, the application of DSB to DEFUN. I should extend the examples, tho. (c) In simple cases where one bit of application generates a result in the form of a list of values, native Arc destructuring such as:

  (let (x y . z) data-genned-elsewhere ...)
...works fine, and indeed is the most common case. But as the application becomes more elaborate we might start having one bit of code emitting data of a more interesting nature, and then at the point of consumption we want to be able to parse that data tersely and in a self-documenting way.

So I might have an application where RGB triples are being passed around and eventually handed to OpenGL and app code just emits (list r g b) and the code that talks to OpenGL looks like:

   (dsb (r g b) color-data
      ...tell ogl about r g and b ...)
Fine, but now I decide in one place to emit an alpha value as well. No problem, all the code just emitting RGB stays unchanged, but in one place I can emit RGBA and then modify the consuming location thus:

   (dsb (r g b &o a) color-data
     ...etc...)
If I now want to get into shininess or material or other ogl options, the optional thing gets nuts because I have to remember in which position shininess goes and then make sure I supply enough nils for other optionals to get the right alignment, and that breaks if I have a fun default for one of the optionals I am bogusly providing just to get alignment. keyword args get us out of that trap, and we do not even have to list them in the right order, they get worked out at call time (so, yes, do not abuse this feature).

  (dsb (r g b &k (a 255) shiny mat) color-data
     ... etc)
Now in CL we have DEFSTRUCT and can at some point punt on lists and get into emitting:

   (make-coloring :r 42 :g 42 :b 42 :shiny +gl-shiny-super+)
...and then the consumer reads the structure attributes so it can talk to OpenGL. The Arc DEFTEM is a solution here if we are restricting ourselves to keyword args. And as I said at the outset, a CL-style DEFUN might be the posterboy for DSB, because we can now decide a DEF needs a new parameter and add it as an optional or keyword arg without changing every call. And where an especially hairy DEF such as open-file will end up with a kazillion parameters, keyword args make calling the function a lot slicker.

-----

1 point by NickSmith 6448 days ago | link

Thanks Kenny, that hit the Mark :)

Looking at mac dsb I realise just how much I have to learn :(

-----

4 points by kennytilton 6448 days ago | link

heh-heh, don't feel bad, i was not kidding when I said it is one of the craziest macros I ever wrote, and I have written over five hundred. Might be the craziest, actually. Macros are normally hard because we are writing code to write code, bouncing back and forth between the mindset of the expanding code and that of the expanded code we are after as we go from backquote to unquote and back.

But in this case with the keyword args the expanded code then had to include code to continue the binding process, because the expander code cannot guess which keywords had been supplied by the caller. Hence the rather sick stop mid-expansion to produce the kvs binding with remaining runtime args paired into an assoc before continuing on to produce the other keyword bindings, each necessitating a runtime lookup of the keyword before deciding if any default should be used, which default might itself still be code!

Man, I now I am confused. Good thing I wrote the macro before I wrote this comment. :)

Needless to say, the macro was evolved: first just getting required params to work. Trivial, but still. Then optionals, with defaults, including computed defaults. I had a bug here, recall, forgetting nil supplied in an optional position means nil, not the default. Then keyword args.

A bit of a giveaway, btw, is my apology above for leaving behind some print statements in the expanding code. We debug macros just like we debug any other code.

Another tip is that I might have broken out that little state machine in the beginning as a separate function to parse the params like:

  (a b c &o (d 42) (e (+ c 1)) &k x) 
...into an assoc:

   '((reqs a b c)(opts (d 42)(e (+ c 1))(keys x))
ie, Divide and conquer: get that working separately, then tackle the rest. I did not do that because of insufficient lookahead (did not realize what I got myself into). :)

-----


And now I can redefine a cl-like DEFUN which similarly takes optional and keyword args.

  (mac defun (name params . body)
    (w/uniq (args)
      `(def ,name ,args
         (dsb ,params ,args ,@body))))
Add some more utilities:

  (def prt args
  (apply prs args)
  (prn))

  (mac tst (id form expected)
  (let res (uniq)
  `(let ,res ,form
     (if (iso ,res ,expected)
       (prt 'test ,id 'OK)
     (do
         (prt 'error 'attempting ,id)
         (prt 'expected ,expected)
       (prt 'got ,res)
       (prt 'code ',form))))))
And we can test:

  (defun tabc (a b c)
  (list a b c))

  (tst "vanilla" (tabc 'dog 'cat 3) '(dog cat 3))

  (defun tabc-od (a b c &o (d 42))
    (list a b c d))

  (tst "one optional, nil supplied"
   (tabc-od 'dog 'cat 3 nil)
   '(dog cat 3 nil))

  (tst "one optional, 4 supplied"
   (tabc-od 'dog 'cat 3 4)
   '(dog cat 3 4))

  (tst "one optional, unsupplied"
   (tabc-od 'dog 'cat 3)
   '(dog cat 3 42))
But do not try this at home without the new improved version of dsb below.

I was mishandling nil when supplied as an optional or keyword argument by defaulting the value. When a parameter is missing we use the default if any, but when it is supplied (even if nil, if you can follow that) then that is the value used.

  (mac dsb (params data . body)
  (w/uniq (tree kvs)
    `(withs (,tree ,data
              ,@(with (reqs nil key? nil opt? nil keys nil opts nil)
                  (each p params
                    (if
                        (is p '&o) (do (assert (no opt?) "Duplicate &o:" ',params)
                                       (assert (no key?) "&k cannot precede &o:" ',params)
                                     (= opt? t))
                      (is p '&k) (do (assert (no key?) "Duplicate &k:" ',params)
                                     (= key? t))
                   key? (push-end p keys)
                   opt? (push-end p opts)
                   (do (assert (~acons p) "Reqd parameters need not be defaulted:" p)
                       (push-end p reqs))))
                  (with (n -1)
                    (+ (mappend [list _ `(nth ,(++ n) ,tree)] reqs)
                      (mappend [list (carif _) `(if (< ,(++ n) (len ,tree))
                                                    (nth ,n ,tree)
                                                  ,(cadrif _))] opts)
                      `(,kvs (pair (nthcdr ,(++ n) ,tree)))
                      (mappend [list (carif _)
                                 `(aif (assoc ',(carif _) ,kvs)
                                    (cadr it)
                                    ,(cadrif _))] keys)))))
       ,@body)))

-----

2 points by almkglor 6447 days ago | link

Personally I'd rather do something like this, in imitation of my p-m: modifier macro:

  (dsb:def name params
     (body))
This would also allow (by the magic of hacking the global sig table) us to use (fn ...), (rfn ...), (afn ...), (xxxfn ...) with dsb.

-----


Oops, left some debug prints in there. :(

-----

3 points by kennytilton 6449 days ago | link | parent | on: Issues Regarding Lisp Adoption

No, tell us what you really think. :)

I think you are right about one thing, I freaked when I realized Python did not have quality IDEs.

otoh, clearly you have never used a quality IDE. Or you never get beyond about a hundred lines of code in your projects. Or ________? :)

-----

3 points by lojic 6448 days ago | link

I've used Visual Studio, JBuilder, Eclipse, etc., and for the languages I used them for (C++, C#, Java), I felt the IDEs were essential. When I switched to Ruby, the same quality of IDEs weren't available, but I found I was much more productive in Ruby with a simple editor than I had been with other languages plus an IDE, and I actually enjoyed the lighter weight environment quite a bit.

Would I be even more productive in Ruby with an IDE? Possibly. But it's possible that more powerful languages lessen the need for an IDE. Your love of ACL for Common Lisp would be a counter data point, but it's only one data point. Well, Avi Bryant would tout the Squeak IDE as a great advantage in using Smalltalk. In my case, it's somewhat moot given the lack of IDEs for Ruby. As I develop more in Lisp, I'll be better able to judge the effectiveness of the available IDEs.

Tens of thousands of lines of Ruby haven't been a problem for me with just vim, and now Emacs, and I don't think hundreds of thousands of lines would be a problem either if designed well.

-----

5 points by kennytilton 6448 days ago | link

"I found I was much more productive in Ruby with a simple editor than I had been with other languages plus an IDE"

Bingo, and that is why we do not see fancy IDEs for agile languages. The corollary being horrid languages have great IDEs because lawdy we need something!

One thing going on here is reflection. If my language has that I can toss off my own rough IDE-ish hacks in minutes and then they do exactly what I want, too, so the demand just never develops for off-the-shelf IDEs.

I work with tens of KLOC of Common Lisp at a time. I like sitting in a backtrace and using a keychord to jump to a functions definition, or sitting in the inspector and jumping to a class definition in source or with a diffeent keychord to the class in a graphical class browser. ACL integrates everything (except, strangely "who calls?", tho that is available via an unexported function), so I just sail all over my big code bases as fast as I can think, click, and keychord. Some folks, btw, feel the same about Lispworks and Slime, we need an IDE smackdown some day to comapre. :)

-----

12 points by kennytilton 6449 days ago | link | parent | on: Issues Regarding Lisp Adoption

"I'm curious what things hold back Lisp from being more popular."

Inertia. Things like Java and Python were close enough to C (syntax and imperative paradigm) that moving to them was just a minor course change. Lisp feels like a U-turn, though I have argued elsewhere it is not really. Meanwhile Java had Sun behind it, giving PHBs a vital warm fuzzy. Lisp looks like the north wall of the Eiger in winter to PHBs.

The IDE problem just confirms anyone's fears if they even get that far, though Franz and Lispworks tried to address that by making unlimited-length trials of their fine IDEs available. Agreed on Emacs+Slime not being a good answer for noobs.

"I focused on IDE's, but I'm also interested in the general question of what things can be done and not done to get more people into Lisp."

Not to worry, it is happening, just step back and look at where languages are going, not the people. Steele famously describes Java as dragging people halfway from C++ to Lisp. Not sure about the fraction. :) Python moved a little more, and Ruby moved a little more. If Arc catches on it will constitute completion of the land bridge from static typing imperative languages to dynamically typed functional languages, but it does not matter because Common Lisp is starting to catch on anyway. And if you look at the ideas behind Lisp and why languages like Python and Ruby are doing so well, Lisp has already prevailed. My 2.

-----

10 points by cchooper 6449 days ago | link

In the future, everyone will use Lisp, but they won't know it's Lisp, because it will be called Microsoft Windows Dynamic Service-Oriented Parenthesis Construction Language for .NET.

-----

6 points by kennytilton 6449 days ago | link

heh-heh, no, we had our chance:

http://www.international-lisp-conference.org/2005/speakers.h...

Mr Dussud (a respected Lisper in a former life) said MS would be happy to host a Lisp if we would just add strong static typing and lose our silly little OO package, CLOS. A couple of other things, too.

We almost ran him from the room. :)

Meanwhile, there is: http://dotlisp.sourceforge.net/dotlisp.htm

....though the name is not as good as yours.

-----

1 point by cchooper 6449 days ago | link

I've just looked at the slides. Did he seriously call Java a dynamic language?

-----

8 points by kennytilton 6449 days ago | link

"Did he seriously call Java a dynamic language?"

Wow, yeah. I tuned him out when I saw he wanted us to declare our variable types. I did not listen to all of the audio, but in there somewhere you will hear some nutjob asking if, given that Lisp has finally started taking over the landscape and he now wants us to run up the white flag and adopt static typing, he also thinks it is time for mainland China surrender to Taiwan. That was me.

I went up later and shook his hand for being brave enough to come into the lion's den. Strong guy. <ouch>

-----

6 points by lojic 6448 days ago | link

Yeah, he called Ruby an untyped scripting language. Strange that it gives the following TypeError then ;)

  irb(main):003:0> x + 7
  TypeError: can't convert Fixnum into String
          from (irb):3:in `+'
          from (irb):3

-----

3 points by DrStrangelove 6322 days ago | link

Profusion of dialects - lack of libraries. There is an inverse relationship between the success of languages and their power. C is cumbersome to write; therefore, everyone treasures the libraries that exist and takes pains to remain compatible. Java, even assuming that it made it 'halfway to Lisp', is clearly still cumbersome enough. Python is already too powerful, but it benefits from its slowness. In many problem domains (GUI, databases, numeric computing), it is way too slow, forcing you to use C-coded extensions, which again help to prevent fracturing of the user base. Where it's fast enough, you get a bazillion of half-assed libraries and frameworks. E.g. Python web programming is a mess and, compared to PHP and Java, not popular at all. (There are also limitations to the VM that matter here but I'm not going into those.)

Lisp is so powerful that it lets everyone modify it, and so everyone goes and creates their own dialect of it. It compiles to fast, native code, so no need to adopt and maintain libraries shared by a large user base.

There is a nice language called D. Unfortunately, it comes with two incompatible standard libraries. How do you like that as a new user? Well, in Lisp it's the same, only times 257. Throw in the oatmeal cum nail-clippings visual appeal (L.Wall) and you might well wonder why it is not less popular than it currently is.

-----

4 points by kennytilton 6451 days ago | link | parent | on: generating sql query for c# with arc

I am afraid I do not know SQL syntax at all and it looks rather horrifying, but here is a more functional approach:

  (= *sql-template*
  ; first, express semantics with the template structure
  '(Study (StudyId (int) studyseq) ;; this will always be the key field. 
     (StudyCode (string 25)) ;; and always encapsulate the type info in their own list
     (StartDate (datetime 8))
     (Description (string 25))))
The trick above is to be a little creative on the front-end and express some things with structure that will make the code do less work. I eliminated the pk designator and simply specify the first thing after the table name to be a key designator. If there can be multipel keys, the template would need a list in that position and then the destructuring would be (table keys . otherfields).

It does not come to play, but notice that I wrapped the type info in its own list in case we ever do need to get at it algorithmically.

In the code I can now use destructuring to parse the template (and if I had to dig into the field info I would dstructure those, but here I just use car)

  (let (tbl key . fields) *sql-template*
   (prn (string "insert into " tbl
           " ( " (let c nil
                   (each (f . rest) fields
                     (when c (= c (string c ", ")))
                     (= c (string c f)))
                   c)
          ", " (car key)
          " ) values ( "
          (let c nil
            (each (f . rest) fields
              (when c (= c (string c ", ")))
              (= c (string c ":" f)))
            c)
          ", " (last key) ".nextVal"
          " ) returning " (car key) " into " (string ":" (car key)))))
This version uses list splicing:

  (let (tbl key . fields) *sql-template*
  (prn (apply string `("insert into " ,tbl
                       " ( " ,@(let c nil
                                 (each (f . rest) fields
                                   (when c (push ", " c))
                                   (push f c))
                                 (rev c))
                       ", " ,(car key)
                       " ) values ( "
                       ,@(let c nil
                           (each (f . rest) fields
                             (when c (push ", " c))
                             (push ":" c)
                             (push f c))
                           (rev c))
                       ", " ,(last key) ".nextVal"
                       " ) returning " ,(car key) 
                       " into " ,(string ":" (car key))))))

-----

4 points by kennytilton 6451 days ago | link

By the way, this is almost a solution in Common Lisp using the format string mini-language:

  (destructuring-bind (tbl key . fields)
    '(Study (StudyId (int) studyseq) ;; this will always be the key field. 
     (StudyCode (string 25)) ;; and always encapsulate the type info in their own list
     (StartDate (datetime 8))
     (Description (string 25)))
  (format t "insert into ~a (~{ ~a~^,~}) values (~{ :~a~^,~}) returning ~a into ~a"
    tbl
    (append (mapcar 'car fields)(list (car key)))
    (append (mapcar 'car fields)(list (car (last key))))
    (car key) (car key)))
It does not do the nextVal bit, and I would have to put bars around things like |StudyCode| to preserve case. But the neat thing is the ~{~} list expander (like @ in backquoted lists) and the escape thing ~^ that arranges for the comma not to print after the last item.

So what does the C# version look like?

-----

2 points by lboard 6450 days ago | link

i think destructuing will help a lot for these kind of things. I learned from u, thanks.

-----

5 points by kennytilton 6449 days ago | link

Isn't destructuring cool? We take an anonymous list and quickly associate useful names with what otherwise would be car, cadr, and caddr, etc.

In common lisp when we use destructuring-bind we can have optional /and/ keyword arguments, so you can plan ahead and create an adhoc list like (1 2 :height 3 :width 4) and let :weight default to 42:

  (destructuring-bind (x y &key (height )(width 0)(weight 42))
      ..etc with x, y, height, width, and weight...)
Hmmm, I think I did that for my Arc implementation of defun...shuffle, shuffle, dig..ah:

   (mac defun (name params . body)
  (w/uniq (rtargs)
    `(def ,name ,rtargs
       (withs ,(with (reqs nil key? nil opt? nil keys nil opts nil without)
                (each p params
                  (if (is p '&o) (do (assert (no opt?) "Duplicate &o:" ',params)
                                     (assert (no key?) "&k cannot precede &o:" ',params)
                                   (= opt? t))
                    (is p '&k) (do (assert (no key?) "Duplicate &k:" ',params)
                                   (= key? t))
                    key? (push-end p keys)
                    opt? (push-end p opts)
                    (do (assert (~acons p) "Reqd parameters need not be defaulted:" p)
                        (push-end p reqs))))
                (with (n -1 kvs (uniq))
                  (+ (mappend [list _ `(nth ,(++ n) ,rtargs)] reqs)
                    (mappend [list (carif _) `(or (nth ,(++ n) ,rtargs)
                                                ,(cadrif _))] opts)
                    (list kvs `(pair (nthcdr ,(++ n) ,rtargs)))
                    (mappend [list (carif _)
                               `(or (alref ,kvs ',(carif _))
                                       ,(cadrif _))] keys)
                    )))
         ,@body))))
I'll carve out a dsb tomorrow.

-----

More