Arc Forumnew | comments | leaders | submitlogin
1 point by akkartik 4911 days ago | link | parent

Interesting. In the past I've always written my code to have use precede definition.

- One of my quibbles with C was often that it was too dumb to notice later definitions. One of the biggest advances of functional programming languages and more dynamic languages to me was that they didn't impose an ordering much of the time, and that made large codebases less brittle (think back to the last time you had to reorganize .h files, an utterly crap overhead activity if ever there was one, devoid of any usefulness).

- It seemed like the biggest advance of the ideas of literate programming (http://en.wikipedia.org/wiki/Literate_programming) and AOP (http://en.wikipedia.org/wiki/Aspect-oriented_programming) - that you could simply write your code fragments in any order, optimizing for readability, and the (pre)compiler would set them up right.

- I've said before (http://arclanguage.org/item?id=12293) that the scheme module system makes it impossibly hard to decompose ac.scm into multiple files. Certainly there's no way to decompose say ac-fn (with its support for destructuring and optional args) into its own module because PLT/Racket seems to have no notion of forward declarations (http://en.wikipedia.org/wiki/Forward_declaration).

But your point of course is that the compiler shouldn't impose an ordering. It's a convention for structuring codebases. You've given me something to chew on now. Thanks.



2 points by shader 4911 days ago | link

But your point of course is that the compiler shouldn't impose an ordering. It's a convention for structuring codebases.

Whenever I see something like a design pattern, I immediately start wondering what kinds of "complete abstraction" could be built out of it, to either make its construction easier, or less likely to be broken by someone new joining the project. As useful for testability as this revelation seems, it also seems like it could be easily broken by someone merely not observing the order conventions.

So, is there any way to get the advantages of testability associated with sequential definition that doesn't require prior knowledge of the pattern, or would it be better if the compiler forced ordering in the first place? What are the advantages in terms of succinctness or expressiveness given by out of order programming?

-----

2 points by aw 4907 days ago | link

For myself I'm still finding out the patterns and abstractions for writing code in order.

For example, the rule says that code shouldn't depend on code that comes after it, but then what about recursive definitions? Asking the question leads me to discover things like extend and defrule.

And there are subtler questions that I don't really understand yet. For example, if I write unit tests that work with earlier definitions, then those unit tests should still work after the later definitions are loaded. So later definitions should extend the program while letting the earlier definitions still work. But what does that mean exactly?

-----

2 points by akkartik 4907 days ago | link

the rule says that code shouldn't depend on code that comes after it, but then what about recursive definitions? Asking the question leads me to discover things like extend and defrule.

It's really interesting that you start with a simple axiom and end up with something largely overlapping with the principle of separation of concerns.

I say 'overlapping' because I sense you aren't quite using extend in the same way. You're using extend more rigidly (no negative connotation intended) than me, chopping your functions up into finer bits than I do.

Hmm, I find myself focusing on the parallel between overrideable definitions in dynamic languages like arc and ruby, and languages like haskell that permit defining functions in multiple clauses:

  fact 0 = 1
  fact n = n * fact(n - 1)

-----

2 points by aw 4907 days ago | link

You're using extend more rigidly

Yes, I should probably clarify that usually I'm not that rigid either. Most of my Arc programming is exploratory programming (figuring out what it is that I actually want) not engineering (implementing a solution to known goals).

Now the transition from exploratory programming to engineering is an interesting one. If I've created a program (using primarily exploratory programming techniques) and then I want my program to run on Arc-m, that's then largely an engineering effort with an easy to describe goal: I want my program to work the same on Arc-m as it does on Arc-n.

Paying the cost of "always" doing things in a particular rigid way doesn't make sense during exploratory programming. But if then I have a particular engineering task (convert this to run on Arc-m), turn introducing some rigidity (scaffolding if you will) is often useful: writing some unit tests, getting the dependencies laid out clearly.

-----

1 point by akkartik 4907 days ago | link

The benefit of interleaved tests is future-proofing, like you said. When making large, non-localized changes such as the move to a new arc, you know exactly the first test that failed. But what happens when you scale from one file to two? When multiple files of definitions depend on arc.ss?

The benefit of keeping tests in a distinct file: you can reason about the final semantics of definitions without thinking about the order in which they are orchestrated. That is a useful simplification except when making large localized changes.

I'm not certain of these ideas by any means. Just thinking out aloud. Would it be useful to analyze the tree of dependencies, and to execute tests in dependency order regardless of the order they're written in? Would that give us the best of both worlds?

-----

1 point by aw 4911 days ago | link

it also seems like it could be easily broken by someone merely not observing the order conventions

If you like unit tests, one option to make sure that you've put things in dependency order is instead of running your unit tests after loading all the code, put the unit tests for a definition immediately after the definition. That way those unit tests for the definition will run before the rest of the code has been loaded.

-----

1 point by akkartik 4907 days ago | link

it also seems like it could be easily broken by someone merely not observing the order conventions

You're programming in arc, ergo you're a smart programmer, ergo you'll notice when conventions are broken. That is the burden of conventions.

-----

1 point by akkartik 4911 days ago | link

You end up having to fight the compiler in some situations just to get code to compile. But that isn't an argument about succinctness or expressiveness, so now I'm starting to rethink it.

-----

1 point by akkartik 4907 days ago | link

Update: After reading the recent thread on hygiene (http://arclanguage.org/item?id=12700) I'm reminded that one of the themes of arc was to assume you're building for smart programmers, and to avoid constraints as far as possible. Hygiene is a constraint. Ordering definitions is a constraint. Better to replace them with conventions.

-----

1 point by shader 4907 days ago | link

Ah, but that still doesn't address the question of why ordering is so much better and whether or not some of those reasons can be reified into useful code. Yes, many things can be typed in repetitively by intelligent people who can remember design patterns. But that doesn't prevent those same intelligent people from wanting utilities and macros to simplify the process.

So far none of the macros and higher-order functions that constitute complete abstractions require you to code that way, they just allow you to if you wish. And help you by making the computer do a lot of the heavy lifting.

It's possible that this abstraction is just that - too abstract - but if so I'd still like to know why.

-----

1 point by akkartik 4907 days ago | link

Hmm, are there any kinds of constraints that help you write less code?

I'm finding this to be a useful line of inquiry; it's also starting to get too abstract. Are you still thinking about sequential definition, or about design patterns in general?

Why ordering is better: sequential definition helps localize errors. So where does this answer lead?

-----

2 points by aw 4911 days ago | link

Welcome!

Certainly there's no way to decompose say ac-fn into its own module

It is possible (though somewhat awkward) to create your own forwarding mechanism.

Module A:

  (define ac-fn #f)

  (define (set-ac-fn! fn)
    (set! ac-fn fn))

  (define (ac s env)
    ...
    ((eq? (xcar s) 'fn) (ac-fn (cadr s) (cddr s) env))
Module B:

  (set-ac-fn!
   (lambda (args body env)
     (if (ac-complex-args? args)
     ...

-----

1 point by akkartik 4895 days ago | link

Relevant: http://c2.com/cgi/wiki?HyperStaticGlobalEnvironment via http://arclanguage.org/item?id=12781

-----