Arc Forumnew | comments | leaders | submit | Inaimathi's commentslogin
1 point by Inaimathi 5074 days ago | link | parent | on: Omega, or combining lisp and haskell

About the corrolaries. Sorry, I meant to acknowledge the possibility of multiple, non-unifiable languages at the "top tier", but not focus on it. On second reading, I seem to have left the notion out altogether. Which was unintentional; Seibel explicitly makes your point about local maxima, and I didn't intend for it to seem like unification was the only possibility. I just happened to be interested in thinking about it because it's not the the first time I heard the idea of "Lisp + Haskell would rule the world".

>I may not want to have to think about the world as containing keyword arguments, either. They make the function a more complicated concept, so that it takes more edge-case consideration (and code) to get everything working intuitively.

This is a quibble, and the one point I potentially disagree with. If you're speaking as the language implementer, fair enough, I could see how it would be harder to provide optional/keyword/default/rest/body args, but how does having this option hurt you as a language user? I'm wondering because my experience with this particular feature has been that it's very useful in some cases, but doesn't get in the way at all otherwise (which is why I'd consider it a net win if it could somehow be made to co-exist with auto-currying).

-----

3 points by rocketnia 5074 days ago | link

how does having this option hurt you as a language user?

I agree it's a minor quibble, but I am talking about an inconvenience from the standpoint of a language user. ^_^

A better example is call/cc. If the language I'm using has reentrant continuations and I want to write a loop utility that calls a callback several times, I can't just maintain the state of the loop as a local variable, 'cause if a continuation call restores a previous iteration of the loop, the variable holding the loop's state will actually stay as it was before the jump. The resulting iteration order will be a bit weird for my taste. For instance, it could appear to be 12345-jump-367 when I want 12345-jump-34567.

Arc has reentrant continuations, and we don't have to use them, but it's still somewhat necessary to worry about that issue when making libraries that might be used by people who do use continuations.

(It's only somewhat necessary because even standard Arc loop constructs like 'each already make that mistake, so the problem's bigger than one library.)

As for keyword arguments in particular, I actually brought up a case pretty recently (http://arclanguage.org/item?id=13085) where the definition of a library utility was more cumbersome in an Arc with keyword arguments than in one without them. It's a utility for making a function that when called, calculates another function and calls it with the same arguments:

  ; common definitions
  (def call (func . args)
    (apply func args))
  (mac late body
    `(fn-late:fn () ,@body))
  
  ; fn-late in a language without keyword arguments
  (def fn-late (body)
    (fn args
      (apply call.body args)))
  
  ; fn-late in a language with keyword arguments
  (def fn-late (body)
    (fn-that-handles-keys-explicitly keyword-args rest-args
      (apply-with-keys call.body keyword-args rest-args))
  
  ; same fn-late as above, but less ridiculously verbose
  (def fn-late (body)
    (kfn keys rest
      (kapply call.body keys rest)))
Again, people who don't use the extra feature can get by with the simpler version, but they should take the extra effort in a general-use library.

IMO, complexity in a few examples like these is just fine if the feature is good enough to make lots of other cases more pleasant. Where it really gets tricky is when multiple language features put together breed tough design corner cases:

  threads + mutation -> locks
  call/cc + mutation -> dynamic-wind
  call/cc + dynamic-wind -> What continuation exists *during* a wind?
  call/cc + locks -> Does a captured continuation keep its locks?
  auto-curry + keyword args -> Which intermediate fn gets each key?
There's probably some diabolical combination of individually nice language features such that a language would be nicer with one left out.

-----

4 points by akkartik 5067 days ago | link

Who cares? :)

A clean model for locks is an asset to any language. Using them well is up to the programmer.

The need to treat libraries as black boxes is holding back programming language design. It forces the set of inputs a library can accept to grow monotonically, causing a sort of tragedy of the commons.

I've had your comment on my browser for a week now. As other tabs have come and gone, one other story has stuck around as well, reread and mulled over: http://www.miller-mccune.com/culture-society/a-psychological... Now I find myself thinking the two have a slight overlap: paranoia :)

-----

1 point by rocketnia 5066 days ago | link

A clean model for locks is an asset to any language.

Do you mean any platform? 'Cause Penknife's an example of a language where

1) I don't expect to find an uncontroversial model for locks,

2) I don't want people to have to write thread-safe code just so that it's gemlike ('cause I know I won't get anything written myself that way XD ),

3) I intend for Penknife programs to be able to call out to other running platforms, including the underlying platform, when they need to, and

4) if Penknife is successful, I fully expect to see a Penknife fork that adopts threads as part of its own runtime. I don't expect it to be as timeless.

The need to treat libraries as black boxes is holding back programming language design. It forces the set of inputs a library can accept to grow monotonically, causing a sort of tragedy of the commons.

It took me a while to figure out what you meant here. Are you talking about closed frameworks that are gradually opened up in successive versions as the real-world needs are observed and the design solidifies?

I agree about those being frustratingly limiting, but the kind of omissions I'm talking about aren't the same thing as being closed. A language designed without feature X doesn't necessarily hide a secret world of X just waiting to be unleashed. ^_^

-----

4 points by akkartik 5066 days ago | link

Attempt 2:

Libs arose in static languages. They save effort recompiling common code. But you only save compilation time if libs are more stable than non-libs. If libs are islands in your sea of code.

In the desktop era libs were units of commerce. To sell a library you promised 'code reuse'.

But code reuse is still a mirage. Upgrading a lib remains akin to transplanting an organ into a new body. Always the fear of rejection.

As libs mature upgrades get smoother, but at the cost of degrees of freedom. Constraints are only added, never removed.

Let's try backing off from the mirage. We have dynamic languages now, no compilation. We aren't trying to sell code. Let's not get hung up on backwards compatibility, on breaking the code of others. It's often easy to fix. Just make sure code screams when it breaks. Unit tests, etc.

Let's try keeping code from others in the same directory as our own. Rather than segregate it into a separate ghetto, let us hack on it like it's our own.

Any codebase will form islands over time. That doesn't make solidity a virtue. Let us not prematurely generalize interfaces.

-----

1 point by rocketnia 5066 days ago | link

That's a bit easier to understand. ^_^

---

But code reuse is still a mirage. Upgrading a lib remains akin to transplanting an organ into a new body. Always the fear of rejection.

For the same reason as that, I don't especially believe in upgrading libraries. If that's code reuse, I agree it's a mirage, but my idea of code reuse is just to use the same code as someone else has used.

---

Constraints are only added, never removed.

Are you sure? A library that prides itself on perfect backward compatibility will only remove constraints and add features, never the reverse. Or are you talking about the fact that sometimes new features are limited in seemingly arbitrary ways because that's necessary for them to be able to cohabitate with features that came before?

---

Let's not get hung up on backwards compatibility, on breaking the code of others. It's often easy to fix. Just make sure code screams when it breaks. Unit tests, etc.

I agree. Fostering a community is probably easier when maintaining backwards compatibility, but I prefer the idea of encouraging people to use whatever version they like.

Let's try keeping code from others in the same directory as our own. Rather than segregate it into a separate ghetto, let us hack on it like it's our own.

...Then again, as waterhouse points out at http://arclanguage.org/item?id=13260, fostering a community is probably easier when there's a single standard basis everyone's using.

Another advantage to segregation is that occasionally the organ transplant does work, and then you've spent minimal effort to catch up with the community and the cutting edge.

Furthermore, the strategy you're talking about sounds like a practice that already exists to a degree (in blogs and forums) and can be promoted in any language, so I'm not worried the languages will get in your way here. Of course, lots of things are doable in many languages but are better suited to certain ones. Can you think of any particular language features that help out, besides modules? Any that become less important, besides modules? :-p

---

Let us not prematurely generalize interfaces.

I don't know what to tell ya. Sometimes I'm not sure I make anything but generalized interfaces. :-p

EDIT: Hmm, I just found this, which is pretty interesting: http://codecourse.sourceforge.net/materials/The-Importance-o...

-----

2 points by akkartik 5065 days ago | link

Saving keystrokes on kindle makes me seem certain :)

Also can't read pdfs on kindle for a few days.

I meant constraints for lib implementor. Libs rarely delete stale features.

Upgrading libs is simply the most obvious pain point. Successful upgrade is worse, causes complacency. Using abstractions without understanding is cargo-culting. We've all done it.

Easier to learn up front than debug later, to learn in isolation than in a big system.

I can try this in arc because: it's dynamic; few and concise libs, less catching up; we've often ignored bc.

Community is lower priority for now. Let's see what happens.

-----

4 points by rocketnia 5064 days ago | link

Successful upgrade is worse, causes complacency. Using abstractions without understanding is cargo-culting. We've all done it.

I accept the risk of not being an expert in everything I'm using. Otherwise I'd never attempt anything, and I'd never learn. :)

It does go against my grain in a certain way: I like to build things that are precisely what I want, so it's bothersome for those things to depend on anything I don't control. However, usually I don't care about the whole codebase being precise, just the observable outcome of it.

---

Easier to learn up front than debug later, to learn in isolation than in a big system.

I think that's the opposite of your point. I take it by "big system" you mean a large group of concepts with so much interconnectedness between them that it's hard to understand any one concept before understanding them all. But you've mainly been saying you'd rather people not think of each other's code as being separate from theirs. That you'd rather have all the code in a project be developed as one big system.

Well, maybe your ideal scenario is for each borrowable codebase to be a small system, such that people can easily learn it and integrate it into their big-system applications. But what about a borrowable codebase that depends on another borrowable codebase? If that project treats the depended-upon codebase as its own code, it'll become a big system, and it'll be harder to borrow. If instead it just comes with advice about what dependencies to track down to get it to work, it's back to cargo-culting.

Also, if my own application is a big system (thanks to all the borrowed code I've added), that's bound to be annoying to me when I try to understand it in six months.

---

I can try this in arc because: it's dynamic; few and concise libs, less catching up; we've often ignored bc.

The last two are probably related. :) With a smaller quantity of well-borrowed code to break and with more of it being easy to fix, there's less short-term value in preserving backward compatibility.

I agree about it being healthier not to promise or rely on backward compatibility. I think there is a language trait that can actively help with that: hackability. If my code can load versions two and three of a library and patch them together, then I don't have to go patch them together on a textual level and drastically increase the amount of code and complexity that belongs to my project.

Of course, even with hackability handy, it's still possible to take full ownership of certain pieces of borrowed code when it makes more sense to do that, so it's not like we have to choose. :) And both of these approaches are taken in Arc programming already.

---

Community is lower priority for now. Let's see what happens.

Well, I consider myself to be programming for the community of all my future selves. ^^ It's 'cause I have a lot of concrete project ideas that are too grandiose for me to have fun working on right away. In the occasional case I decide to implement one of the more urgent or less fickle ideas, it really pays off that I've amassed a lot of abstract, future-proofed libraries in the meantime. So I'm often interested in developing things that make future-proofing itself easier, like module systems. Ironically, they have the most immediate use for me.

-----

3 points by akkartik 5064 days ago | link

There is no borrow. When i see external code that may be useful to my pgm i want to understand it, tailor it for my needs, prune the unnecessary.

I want to see how long I can keep the codebase small.

You don't return to all your code. 90% of my code never gets read again. Not worth thinking about.

When I do return to old code I lately have no trouble. I think it's the unit tests. Little easily digestible atoms of semantics.

If everyone does this less code will be shared, but shared code will become more timeless. Easy to return to it in six months.

You don't have to be an expert in other people's code, design decisions, error paths. Just concepts and happy paths. So you see opportunities to delete.

Perhaps borrowing code is also learned from static languages. Just a delete-if loop in c can be nontrivial to read.

-----

2 points by rocketnia 5064 days ago | link

We're really getting to the heart of things now, I think. :)

---

There is no borrow. When i see external code that may be useful to my pgm i want to understand it, tailor it for my needs, prune the unnecessary.

That's one kind of borrowing, as far as I'm concerned. All I'm talking about is tech one person comes up with first and another person takes advantage of. Whether it's called code reuse or borrowing or whatever doesn't matter to me.

Borrowing code is essential for arriving at the best combinations of ideas. Two heads are better than one and all that.

---

I want to see how long I can keep the codebase small.

I want my code to be small only because I want it to be valuable to me when I go back to read it. Value comes first.

And on that topic...

You don't return to all your code. 90% of my code never gets read again. Not worth thinking about.

...if I believed that, then maybe I wouldn't even care if my code were small. :)

I return to my code more and more as I've developed better tools to future-proof it. If any piece of code isn't worth returning to, then why was it written in the first place?

Okay, maybe for one-shot scripts or something. ^_^ Like I said, I primarily write abstract, generalized code (or at least that's what I perceive I do).

---

When I do return to old code I lately have no trouble. I think it's the unit tests. Little easily digestible atoms of semantics.

Comments are good for that too, but unit tests are certainly easier to keep up-to-date. :)

---

If everyone does this less code will be shared, but shared code will become more timeless. Easy to return to it in six months.

You don't have to be an expert in other people's code, design decisions, error paths. Just concepts and happy paths. So you see opportunities to delete.

I think we're on common ground here. ^_^

---

Perhaps borrowing code is also learned from static languages. Just a delete-if loop in c can be nontrivial to read.

It took me a Google search to find the meaning of 'delete-if, but I get it now. :) http://www.ai.mit.edu/projects/iiip/doc/CommonLISP/HyperSpec...

What do you mean by a static language? You keep referring to static and dynamic languages, but in my experience "dynamic language" is a term for a language with dynamic typing. I think C loop boilerplate is something to blame on the lack of a convenient closure/block syntax, not the type system.

If by "static language" you mean a language that can't be changed, that makes way more sense. But even in a language like Arc that's open to customization, we still share code.

Actually, you probably mean something different by "borrow" too, such as using code as a black box. In that case, you make sense again. :-p

The full translation is "Perhaps the practice of taking code for granted has come from a history full of languages where code isn't formatted in a personalizable enough way for its purpose to be clear." I don't want to put words in your mouth, though. XD

-----

3 points by akkartik 5061 days ago | link

(Ok, back to the land of internet.)

"Perhaps the practice of taking code for granted has come from a history full of languages where code isn't formatted in a personalizable enough way for its purpose to be clear."

Yep. There's more pressure to take code for granted in languages so verbose that everything is non-trivial to read. It seems to have been a slippery slope from having such languages to assuming take-for-granted-ability was an unabashed virtue.

That is perhaps the biggest disadvantage of C and relatives[1]: they kept programmers from bulking up on their reading muscles, the ability to read concise code patterns at a glance. Perhaps this is partly what Dijkstra was referring to as 'brain damage'. (http://www.cs.utexas.edu/~EWD/transcriptions/EWD04xx/EWD498....) [2] Fortunately it is curable, no matter what he said.

[1] What I was referring to as static languages, where functions can't be redefined dynamically.

[2] In seeking out this reference I just found a Dijkstra quote about the brain damage done by lisp! http://www.cs.utexas.edu/users/EWD/transcriptions/EWD09xx/EW...

-----

2 points by evanrmurphy 5065 days ago | link

> Saving keystrokes on kindle makes me seem certain

Ah, so is this why you've been sounding so prophetic lately? :)

-----

1 point by akkartik 5066 days ago | link

No I think evan got it right. constrain as less as possible. you can constrain more later if you need to.

STL needs a crystalline/congealed interface. you can't inspect running sources, insert prints. all you have are compiler errors and API docs. we dont have those constraints, let's not tether ourselves to libs. libs and APIs are for more static languages. we don't need a new configuration of sources to just work. let's back off. just tell me the moment something fails.

-----

1 point by evanrmurphy 5067 days ago | link

> The need to treat libraries as black boxes is holding back programming language design. It forces the set of inputs a library can accept to grow monotonically, causing a sort of tragedy of the commons.

Very interesting idea. So what are you proposing, that library users be more willing to hack internals instead of always wishing for endless customizability from the outside?

> reread and mulled over: [...]

That link didn't work for me. Is this the same story? http://www.miller-mccune.com/culture-society/a-psychological...

Update: waterhouse and his unmatched internet-fu beat me to correct the link.

-----

1 point by waterhouse 5067 days ago | link

That link doesn't work. My awesome internet-fu (ability to use Google) has found the correct link:

http://www.miller-mccune.com/culture-society/a-psychological...

-----

1 point by akkartik 5066 days ago | link

thanks - I'd typed it it in by hand :)

-----

2 points by Inaimathi 5074 days ago | link | parent | on: Omega, or combining lisp and haskell

I found it; better late than never, I guess.

This is a much more thoughtful response than I was expecting, and I've linked it from the original blog post.

-----

1 point by rocketnia 5074 days ago | link

Oh, sorry! I was working on a more focused and less personalized-for-someone-else version of the response yesterday, but it takes me a while sometimes to get to things. XD Glad you found it!

-----