Arc Forumnew | comments | leaders | submitlogin
Fexprs as Lisp function application basis; $vau: the ultimate abstraction (wpi.edu)
1 point by evanrmurphy 4817 days ago | 16 comments


1 point by evanrmurphy 4817 days ago | link

Thanks to rocketnia for first getting me to notice the Kernel programming language [1] and then for sharing some interesting code about it [2]. (I'm checking out kernelish.arc [3] right now. :)

---

[1] http://arclanguage.org/item?id=11882

[2] http://arclanguage.org/item?id=13325

[3] https://gist.github.com/778492

-----

2 points by rocketnia 4817 days ago | link

I hope it's useful to you. The R-1RK is pretty well-reasoned in most regards, so it's some good inspiration. ^_^

...But I can't leave well enough alone. Here are a few key places I disagree with the philosophical choices:

"G3: Dangerous [things] should be impossible to program by accident" is pretty bogus, and even if I give it the benefit of the doubt, it's poorly phrased. IMO, things should be made purposefully difficult only when that makes the useful things easier overall. I think most uses of G3 in Kernel are actually efficiency concerns

"G2: The language primitives should be capable of implementing all the advanced features of R5RS" is a bit scary. I can't say I don't do the same sort of thing by making Penknife a similar experience to Arc, but I suspect heavy inspiration from a single souce is usually more of a bias than a productive motivation. It'd be best to justify each "advanced feature" on its own merit and to be ready to take inspiration from lots of sources. But hey, if someone has have enough time on their hands to do a point-by-point restoration of another good system, or if they don't have enough time to weigh the options, it's a reasonable pursuit. (Maybe. ^^; How much of that is me telling myself that? 'Cause somehow I feel I fall into both the "enough time" and "not enough time" cases, and it sounds a bit like I'm looking for excuses. :-p )

But most importantly, I think "G1a: [All] should be first-class objects" is better served by making every primitive utility's domain explicit and extensible. That is, reducing the number of official ways to contrast the first-class-ness of particular objects. To be fair, Kernel puts a strong emphasis on supporting cyclic data structures, and that's one big case which extensible utilities probably need to plan for in advance (so that extensions aren't limited to being naive recursive traversals). But on the other hand, Kernel's 'equal? doesn't allow a programmer-defined type (an encapsulation) to provide custom behavior, so it actually makes those types second-class. @_@

-----

1 point by evanrmurphy 4816 days ago | link

> "G3: Dangerous [things] should be impossible to program by accident" is pretty bogus, and even if I give it the benefit of the doubt, it's poorly phrased.

The paper actually says, "Dangerous things should be difficult to do by accident" [1]. I don't mean to nitpick, but in this context I think the difference between "difficult" and "impossible" is significant.

Update: I just realized that Shutt's dissertation and the R-1RK are distinct, but the quote is consistent across them.

---

[1] From page 88 of jshutt.pdf, downloadable at http://www.wpi.edu/Pubs/ETD/Available/etd-090110-124904/

-----

2 points by rocketnia 4816 days ago | link

That difference is significant too, but I'm specifically talking about the difference between trying to make something difficult and neglecting to make it easy. I pick a tool in the first place because it lets me do things more easily.

...Oh, I misquoted it. XD Yeah, thanks for catching that.

Another thing I missed was finishing the efficiency sentence. I was going to devote another paragraph to criticizing the efficiency-should-only-arise-naturally rule for being hypocritical--that an eye to efficiency influences and probably compromises other aspects of the design from the outset--but I determined I actually agree with that rule for Kernel. I think efficiency of fexprs is one of the most significant things Kernel stands to prove, so I don't mind an explicit and upfront goal of efficiency, but efficiency isn't in itself a goal motivating Kernel (I think), so it's good for the rule to be nuanced a bit. (Had I completed the sentence, it would have covered some part of that, but I'm not sure which. XD )

-----

1 point by rocketnia 4816 days ago | link

Oh, that dissertation is new(-ish)! Last time I looked into Kernel it was just the R-1RK. Gotta read that sometime. ^_^

-----

1 point by evanrmurphy 4817 days ago | link

  arc> (load "kernelish.arc")
  Error: "Function call on inappropriate object #(tagged fexpr #<procedure>) ()"
Might be an easy fix. Regardless, I've gleaned a lot from reading the code.

  (feval '(assign quote (fx (expr) env expr)))
I'm guessing this came from Kernel, but it's brilliant.

  (feval '(assign fn
            (fx (parms . body) env
              (wrap (eval (list* 'fx parms '!ignored body) env)))))
Hmm... I'm not sure I get wrap.

-----

2 points by rocketnia 4817 days ago | link

In Kernel, applicatives (procedures) have underlying operatives (fexprs), and 'wrap is the axiomatic way to make an applicative, kinda like Arc's use of 'annotate to make macros. I don't know whether or not I'm not particularly attached to 'wrap; I kind of included it on a whim in order to support a nifty definition of 'fn.

As far as the bug goes, I still haven't tested the code, but here are a few fixes I just pushed, which may or may not be relevant:

   (def feval (expr (o env fenv*))
     (case type.expr
       cons  (fapply (feval car.expr env) cdr.expr env)
  -    sym   do.env.expr
  +    sym   (fenv-get env expr)
             expr))
  
   ; The 'applicative type is totally Kernel's idea.
   (def fapply (op args (o env fenv*))
     (case type.op
       fexpr        (rep.op env args)
  -    applicative  ((rep rep.op) env (map [feval _ env] args))
  +    applicative  (fapply unwrap.op (map [feval _ env] args) env)
                    (apply op (map [feval _ env] args))))
  
  ...
  
  +
  +; We use a singleton list so that an applicative can wrap an
  +; applicative. Using 'annotate does nothing to a value that's already
  +; of the given type.
  +
   (fdef wrap (fexpr)
  -  (annotate 'applicative fexpr))
  +  (annotate 'applicative list.fexpr))
  
   (fdef unwrap (applicative)
  -  rep.applicative)
  +  rep.applicative.0)
  +

-----

2 points by rocketnia 4813 days ago | link

Well, I've finally taken the time to debug kernelish.arc. It was really buggy. XD It seems to be working now, but let me know if you run across some more trouble. ^_^

Here's the link again: https://gist.github.com/778492

Another thing:

I'm guessing [that definition of quote] came from Kernel, but it's brilliant.

Well, I didn't (consciously) take that from Kernel. It's just what fexprs do, right?

On the other hand, I sorta did take the definition of 'fn from Kernel's '$lambda. I peeked at the R-1RK in order to discover that it used 'list*, and the implementation was straightforward from there. The definition I have is probably exactly the same as the Kernel one.

-----

1 point by evanrmurphy 4812 days ago | link

It seems to be working in my first tests too. This is really neat, thanks for putting it together.

-----

1 point by evanrmurphy 4813 days ago | link

Cool thanks, I look forward to trying it. :)

-----

2 points by akkartik 4817 days ago | link

Dijkstra, in the introduction to "A discipline of programming":

  Needless to say, none of the examples in this book have been run on a computer.
:-p

-----

2 points by rocketnia 4816 days ago | link

Wow, what a quote. XD Is this a more accurate version? I can't find any search results for yours, but there aren't many for this one either. :-p

  None of the programs in this monograph, needless to say, has been tested on a machine.

-----

3 points by akkartik 4816 days ago | link

I was running off memory :) I'll check with my copy when I get home tonight. (Discipline is one of ~ten books I still own. http://www.reddit.com/r/programming/comments/6f0fz/do_you_bu...)

---

Update 15 hours later: yep, your version is right. A couple more gems in that Preface:

"..it hurts me to see the semantics of the repetitive construct

  while B do S
defined as that of the call of the recursive procedure

  whiledo(B, S)
Do you think the BS was chosen accidentally? :)

I don't like to crack an egg with a sledgehammer, no matter how effective the sledgehammer..

Contrast the Alan Kay quote that says you should find the most complex construct you possibly need, and build everything in terms of it. (again my google fu is weak)

For the absence of a bibliography I offer neither explanation nor apology.

---

My predominant impression after reading Discipline was that EWD was getting high off his programming. He was trying really hard, but failing to get me high as well.

-----

2 points by rocketnia 4816 days ago | link

I like your book management strategy, BTW. :)

Contrast the Alan Kay quote that says you should find the most complex construct you possibly need, and build everything in terms of it. (again my google fu is weak)

If Alan Kay's the person to credit for OOP, I guess that doesn't surprise me. ^_^ A simple basis of complicated things is just fine. For instance, not all our lambdas actually need to be closures, but that doesn't stop us from reasoning about them as closures for the sake of reducing the number of concepts we're juggling.

The problem comes around when people forget how arbitrary any particular complicated basis is. XD I'm looking at you, "Arc needs OOP" threads.

So maybe the Dijkstra and Kay quotes are compatible, in a sense. Kay can be encouraging people to find appropriate foundations from which to implement concepts, and Dijkstra can be encouraging people to perceive the concept itself rather than taking its implementation for granted.

I guess I can't really say that without knowing more of the context of what Dijkstra and Kay believed. Still, a quote more opposite to Dijkstra's might be this one:

Beyond the primitive operators, which by definition can't be written in the language, the whole of the Arc spec will be written in Arc. As Abelson and Sussman say, programs must be written for people to read, and only incidentally for machines to execute. So if a language is any good, source code ought to be a better way to convey ideas than English prose.

- Paul Graham

I think it's similar to foundational math and philosophy. An implementation (model) of a system in terms of another system can admit implementation-specific proofs of things that are false in other implementations. Just because one model or formalization has been found doesn't make it uninteresting to continue considering the motivating informal system on its own merit.

-----

2 points by evanrmurphy 4816 days ago | link

Found that Alan Kay quote in The Early History Of Smalltalk [1]:

"take the hardest and most profound thing you need to do, make it great, an [sic] then build every easier thing out of it."

---

[1] http://www.smalltalk.org/smalltalk/TheEarlyHistoryOfSmalltal...

-----

3 points by rocketnia 4815 days ago | link

Awesome. :)

My next questions was, why on earth call it a functional language? Why not just base everuything [sic] on FEXPRs and force evaluation on the receiving side when needed? I could never get a good answer, but the question was very helpful when it came time to invent Smalltalk, because this started a line of thought that said "take the hardest and most profound thing you need to do, make it great, an then build every easier thing out of it". That was the promise of LiSP and the lure of lambda--needed was a better "hardest and most profound" thing. Objects should be it.

This so closely parallels the way I'm thinking about Penknife that I suddenly find it spooky that Smalltalk uses square brackets. XD

-----