Arc Forumnew | comments | leaders | submitlogin
Kicking up more fuss about modules
8 points by cchooper 6116 days ago | 13 comments
I posted earlier (http://arclanguage.org/item?id=3202) about whether Arc needs modules. I learnt 2 things:

1. Always unit test your comments :)

2. I'm not very good at expressing myself in forums.

So here's another crack at saying what I wanted to say...

In 'Why Arc Isn't Especially Object Oriented' (http://paulgraham.com/noop.html) Paul Graham wrote:

"If a language is itself an object-oriented program, it can be extended by users. Well, maybe. Or maybe you can do even better by offering the sub-concepts of object-oriented programming a la carte. Overloading, for example, is not intrinsically tied to classes. We'll see."

I think a similar thing applies to modules. They solve a variety of different problems (hiding state, integrating code etc.) which might be best solved by individual, powerful abstractions. Of course, you can still combine these all together and call it a module system, but you could also combine them with other things and create something totally different, and that's the power of a la carte abstractions.

For example, as I and a few others pointed out, you can use with to hide local definitions from the users of your 'module'.

  (with (local-def-1 [+ _ 1]
         local-def-2 car:cdr)
        (def public-def (x)
          (local-def-2:local-def-1 x)))
But this has a problem: forward reference.

  (with (foo [bar _]
         bar [+ 1 _])
        (foo 5))
  => Error: "reference to undefined identifier: _bar"
Without forward reference, you can't have mutually recursive private functions. But that can be fixed by creating a macro that allows forward reference:

  (forward-with (foo [bar _]
                 bar [+ 1 _])
                (foo 5))
  => 6
But look what just happened... I invented a new abstraction that could have all kinds of uses. As you can see, encapsulation is not intrinsically tied to modules any more than overloading is intrinsically tied to classes.

By addressing one of the problems that modules are supposed to solve, instead of addressing the problem of creating modules, I've produced something of general use. That's the power of a la carte thinking. If we keep trying this, we may find out that we don't need modules at all. To quote PG again:

"I personally have never needed object-oriented abstractions. Common Lisp has an enormously powerful object system and I've never used it once. I've done a lot of things (e.g. making hash tables full of closures) that would have required object-oriented techniques to do in wimpier languages, but I have never had to use CLOS.

Maybe I'm just stupid, or have worked on some limited subset of applications. There is a danger in designing a language based on one's own experience of programming. But it seems more dangerous to put stuff in that you've never needed because it's thought to be a good idea."

Replace 'object-oriented' with 'modules' and you'll see what I'm getting at.



4 points by drcode 6116 days ago | link

i think you want "withs"

  (withs (bar [+ 1 _]
          foo [bar _])
         (foo 5))
..in hindsight, you're arguing it needs mutual recursion, which withs doesn't have either...

-----

3 points by cchooper 6116 days ago | link

Ooh... I hadn't seen withs before. That'll be useful.

But you're right, it still doesn't fix it. The closest thing is labels in Common Lisp, but that can only be used to create functions. Perhaps if even CL can't do it then it's not that useful after all.

-----

4 points by drcode 6116 days ago | link

of course you can do this:

  (with (foo nil
         bar nil)
        (= foo [bar _])
        (= bar [+ 1 _])
        (foo 5))
That is the Right Way to solve this in arc, I think... Handles mutual recursion without problem. Easily wrappable in a macro, if desired.

-----

3 points by cchooper 6116 days ago | link

You just beat me to it:

  (mac fwiths (defs . body)
    `(with ,(intersperse-nils (keep-odd-pos defs))
           ,@(make-setters defs)
           ,@body))

  (def keep-odd-pos (lst)
    (if lst (cons (car lst) (keep-odd-pos (cddr lst)))))

  (def intersperse-nils (lst)
    (if lst (cons (car lst) (cons nil (intersperse-nils (cdr   lst))))))

  (def make-setters (lst)
    (if lst (cons (list '= (car lst) (cadr lst)) (make-setters (cddr lst)))))

-----

3 points by shiro 6116 days ago | link

The closest thing you want is letrec in Scheme, which binds both functions and variables.

-----

2 points by vrk 6116 days ago | link

This is exactly what Scheme's letrec does. Try it in mzscheme, which you will have installed if you're playing with arc1:

  (letrec ((foo (lambda (x) (bar x))) 
           (bar (lambda (x) (+ 1 x)))) 
    (foo 5))
(As an aside, it's beginning to be obvious how many redundant parentheses Scheme and most other Lisps have.)

-----

1 point by cchooper 6116 days ago | link

letrec won't work for non-function definitions, for example:

  (letrec ((x 5)
           (y x))
    y)
For this, you need let-star (not sure how to write that in markdown), but let-star doesn't give you forward reference, so nothing is quite right.

-----

5 points by shiro 6116 days ago | link

Oh, if you want that, check out letrec*.

http://www.r6rs.org/final/html/r6rs/r6rs-Z-H-14.html#node_id...

-----

1 point by cchooper 6116 days ago | link

That's it, perfectly!

-----

4 points by pg 6116 days ago | link

You can just write let* so long as there is a space after the asterisk.

-----

1 point by cchooper 6116 days ago | link

Ah, the problem was I had a comma after it. Thanks.

-----

1 point by andreyf 5805 days ago | link

let*, huh?

-----

1 point by almkglor 6114 days ago | link

Somebody push this on the arc-wiki ^^ I'm at the office, the internet connection at home is broken so I was severely Arc-deprived last weekend ^^

  (mac withrec (parms . body)
    " Assigns a set of local variables for a given `body'.
      Assignment is simultaneous
      Functions assigned to local variables may refer to
      other local functions:
        (withrec
           (f1 (fn (x) (aif (cdr x) (f2 it)))
            f2 (fn (x) (aif (car x) (f1 it))))
           (...))
      See also [[with]] [[withs]] [[let]] [[fn]] [[do]] "
    `(let ,(map car (pair parms)) nil
        (= ,@parms)
        ,@body))

-----