"in my repo i only got "diy main.arc misc README.md static www arc""
Hmm, that looks weird, actually. I haven't used openshift-arc myself, but it supposedly installs Arc in the data/ directory. Is that really your repo/ directory? What does your data/ directory look like?
Here's the OpenShift directory structure if it helps:
By the way, you can type code on the Arc forum by indenting it by two spaces:
abc
def
By the way, you can format code here on Arc Forum by indenting it by two spaces:
abc
def
You can also write multiple paragraphs by putting a blank line in between. For instance, in the code above, "abc def" would be a single paragraph, but "By the way" would start a new one.
"the echo of my admins is admin.(not "admin") ...when log as admin, i only have 'discuss' rights,and do not see the other rights.( sry, cannot upload photo here)"
I think that's where the Arc code is actually supposed to be installed, at least according to the openshift-arc readme. Maybe the readme is out of date.... :/
The arc git repo is cloned under app-root/data, but it's not used as the root directory for the web application in case the user has other files they want to load.
I'll admit, the readme isn't the best. I mostly copied it from the original and changed the relevant bits.
Unfortunately, certain files like adminfile* should probably not be relative to the repo directory, because it's likely to get wiped. I'm not sure where the right place to put it would be though. Maybe data/config?
I'm submitting this because I found the first half of it to be a pretty interesting point of view. The latter half isn't bad, but it retreads a lot of the same ground.
I had more to say on this all along, but I didn't have the time to write it out.
---
Performance-wise, it's interesting to keep CSS's abstractive power relatively simple, because (naively[1]) the relationship between N CSS rules and M DOM nodes has an overall complexity on the order of (N * M). Since it's pretty easy to understand the optimization of minimizing file sizes, programmers will just so happen to optimize this N factor without thinking about it. But if CSS had abstractions, the file size wouldn't correlate with N anymore.
Programming-in-the-small-wise, CSS properties are designed in a natural-language like way. What I mean is, there are lots of defaults, and there's often more than one way to specify the same formal behavior, so it's relatively easy to get something to work by taking small steps and guessing. Visual feedback further supports this style of trial-and-error programming.
Programming-in-the-large-wise, CSS is already full of ad hoc special-case interactions between properties to support the defaulting behavior I just described. This means it would be easy to make leaky abstractions. Even the meaning of "90%" depends on what property it appears in, so number-valued variables would likely have a few gotchas all on their own. If abstractions are leaky in practice, their programmer-given names will often be deceptive about their actual functionality, making large programs even harder to comprehend.
So adding abstractions to CSS would tempt people into an in-the-large programming style that not only fails to solve in-the-large maintenance problems, but also complicates performance and puts pressure against having a smooth CSS learning curve.
[1] I expect it's much more expensive than that due to text flowing, but also somewhat time-cheaper (albeit more space-expensive) if indexes or memoization are used.
---
For language design in general, recently I'm interested in finding ways to program without discrete abstraction boundaries or steep learning curves. I've often found CSS inspiring as an example of a language that invasively fills in details of another structured program (in this case a DOM), not to mention that it's an example of a continuous reactive language.
On the other hand, I'm interested in Turing-complete programming, I'm interested in programs that combine the efforts of independent library developers, and I'm interested in network services that have synergy despite having independent maintainers. Some kind of abstraction seems necessary for those things, but I'd rather find abstractions that leak in the right ways to correspond to the abstraction leaks in reality.
For instance, independent library developers or service maintainers aren't strictly independent as long as any user cares about both of them at once. It's often feasible for the user to prod the two projects to work together, or for the user to create or promote another project that makes that interaction irrelevant. Open source ideology and reverse engineering play into this, making it easier for users to manage these collisions. Abstractions, on the other hand, typically work against it. If an abstraction can be leaky in just the right way to be easy to reverse-engineer, that would be an interesting option here.
I have been embracing less precise ways of thinking this year, but I've been using logic as a stepping stone toward vagueness for a long time.
If an abstraction leaks, for instance by allowing reverse engineering, we can usually fix this without any code changes: Just change the language docs to say that abstractions like this one are explicitly noncommittal about whether people can reverse-engineer them. If we don't like the abstraction once its idiosyncrasies are out in the open like that, we can develop an abstraction we do like and use that instead.
I don't think I ever liked these leaks, but elegance and convenience has kept me away from exploring alternative abstractions myself.
The use of mathematical formalism is a practice that has a certain cultural context, and that context is often unfair. Furthermore, formalized ideas are as rigid and neutral as they can possibly be, so it's highly questionable to say people "should" express ideas this way, as though being rigid and neutral is morally right. There's no easy answer, because the cultural process of deciding what's fair or moral can also have effects that are unfair and immoral. For these reasons, it's quite understandable to pursue alternatives to mathematics, and this year I've been particularly interested in how those alternatives might work and succeed. This might explain the shift you're seeing.
Personally, I was very literal-minded as a kid. If someone couldn't tell me exactly what they wanted from me, right down to a fully executable program, I assumed they were either being maliciously equivocal or that someday they'd make a serious error: Genies, robots, aliens, and cartoon tricksters don't allow much tolerance for vagueness, so what cushy precedent would it set if I allowed vagueness to go unchecked?
So I may at some points build up to or rationalize vagueness in terms of precise reasoning, but I don't mean that as an endorsement of precise reasoning. It's a quirk of where I'm coming from.
I've been confirming my biases :) along similar lines this year, and ended up with some surprising conclusions.
It's easy to forget, surrounded as we are with computers, that mathematical proofs were not intended to be "rigid" the way programmers understand the term. Proofs were intended to persuade other mathematicians. Standards of rigor varied depending on who the audience was.
When mathematics made its way to software the notion of rigor hardened to a sort of math envy. Endless man-years have been spent trying to "prove programs correct" rather than to do computer-aided proofs as a human activity. But the best practitioners have always known that you can't take the human out of the proof life-cycle. Just look at ACL2.
The most harmful result of this slippery slope has been to enshrine and ossify notation as something most programmers are to be prevented from changing. A particularly startling experience was to reread Ken Iverson's great Turing Award lecture, "Notation as a tool for thought" (http://www.eecg.toronto.edu/~jzhu/csc326/readings/iverson.pd...) keeping in mind what I wrote earlier. Before programming existed, if a mathematician wanted to change his notation, he did so with the stroke of a pen and without a second thought. Now here's a great programmer, at the very outset, simultaneously holding math up as the gold standard for notation and claiming the flexibility of its notation to be self-evidently deficient.
I'm mostly preaching to the choir about syntax enhancement, given this is a lisp forum. But I'd argue that even lisp doesn't go far enough. We're still obsessed with finding the ideal visual notation to let a program be taken in at a glance. We don't pay nearly enough attention to the fact that large programs can't be taken in at a glance, and so at large scales the visual effects of notation must be subordinated to a tactile feedback-based environment.
Lately I've been building such an environment from scratch. It'll support programming in a sort of assembly language, not just to ease my implementation burden but also to explore if a good environment might reduce the need for a high-level declarative language. Concretely, the environment will log scenarios tried out during development, recording inputs and outputs for all function calls. The binary log will augment the version control history of a project, so that you can clone a repo, download its log and then start seeing warnings anytime you make a change, something like "hey, somebody tried this function with these inputs last week, but got a different output. Would you like to a) override that output, b) pin it as a unit test that must always pass, or c) tag this function as stateful so you don't see these warnings again?"
Beyond that initial use case, I want to make it easy to do things like:
a) Create a test with a single keystroke or gesture,
b) Visualize the space of input for a function as examples organized by different code paths taken,
c) Explore how the set of inputs tried has grown over the version history of a function,
..and so on.
I'm very surprised with this goal; nobody started out more notation-obsessed than myself.
"When mathematics made its way to software the notion of rigor hardened to a sort of math envy. Endless man-years have been spent trying to "prove programs correct" rather than to do computer-aided proofs as a human activity. But the best practitioners have always known that you can't take the human out of the proof life-cycle."
Reflexively, I don't like drawing a distinction between human activity and mathematics. What else in the universe does mathematics except humans?
Any field is going to keep humans in the loop or else it won't be useful to humans. Any field is going to strive to minimize humans' responsibilities in the loop or else it won't be useful to humans.
Right now I'm in the mood to drill down on "field" and "useful." A "field" implies there's some kind of shared research going on, and "useful" implies that it comes in handy for people's naive needs. (Who knows if something that's useful is really beneficial?)
Shared research, in itself, crucially involves summaries and citations to cash in on prior work. But when we snapshot these summaries and citations (e.g. writing them on paper), the discreteness of our snapshots permeates into discreteness of the citation expressions, which then permeates into discrete methods for locally assessing the value of the research, which then permeates into discrete valuations of the research. So I suspect this one useful application domain -- that is, the meta-theory of snapshotted shared research -- is sufficient to give a strong identity to the field of logic[1], regardless of what culture currently surrounds it and what other application domains intersect with it.
[1] I thought I was going to say "mathematics," but "logic" is a culturally closer term for what I described. The impression I've gotten is like this: Logic gives you a canvas for reasoning. Mathematics equips that canvas with intermediate scaffolding for the subjects you care about. It's paint by number! :-p
---
"Just look at ACL2."
Er, what about ACL2?
---
"The most harmful result of this slippery slope has been to enshrine and ossify notation as something most programmers are to be prevented from changing."
For at least this reply, I'd like to break down "harmful" into "less useful" and "less beneficial."
A programmer will find it more useful to be able to use the notation of their choice, especially notation of their own design. However, if many programmers each willingly stray from standard notations, they make the world of program code less useful in at least one way, to at least one audience: Newcomers face a steep learning curve, encountering different notations everywhere they look.
As for whether this is "beneficial" one way or the other, I don't know. I just wanted to cast doubt on the moral connotations of "useful" again.
---
"We don't pay nearly enough attention to the fact that large programs can't be taken in at a glance, and so at large scales the visual effects of notation must be subordinated to a tactile feedback-based environment."
I don't know if they can be completely "taken in," but making them interactive is great. Logic (in the sense I described above) is already essentially interactive because of the process of tracking down citations to convince oneself of any pieces that are hard to believe. It would be pretty useful to be able to navigate a program to see various views that are bite-size enough to take in.
Ooh, as an extrapolation of your unit-test-based ideas, it would be especially nifty to view a codebase as a fractal pattern where every possible run-time state corresponds to one location in the fractal. XD Do just enough fuzzing to draw a good density of pixels for the overall program fractal, render the errors in red, and then do more localized fuzzing when the user zooms in. Some design flaws and corner cases may be visually apparent without even being proper errors!
If you're starting with a conventional assembly language, you can probably display each subroutine call as a pyramid of the calls that happen inside, with each level being some fraction as tall as the previous one. Conditionals would be treated similarly to calls, except that they don't always happen, so sometimes they can be omitted from the inner levels of a pyramid. Non-call, non-conditional statements would be omitted from the fractal altogether to save room.
Sorry, I guess I got carried away on a specific idea. XD Your ideas are already pretty nice too.
"you can probably display each subroutine call as a pyramid of the calls that happen inside"
Oops, just after I posted this I realized that the "calls that happen inside" a subroutine may vary depending on the program state or arguments. If these separate possibilities are not shown as separate locations in the fractal, then zooming in won't be sufficient to narrow down these parameters in the view. If they are shown as separate locations, then a mere 8-bit parameter will shrink each sub-pyramid by a factor of 256.
It's likely the zooming interface will need to be accompanied by a timeline scrubber or tableau view to visualize number-like parameters.
Pointer-like parameters would get out of hand either way, let alone strings, lists, trees, and functions.
Just that it's interactive. To prove anything non-trivial you have to guide the system with the right lemmas. My example wasn't meant to be particularly insightful..
"Any field is going to strive to minimize humans' responsibilities in the loop or else it won't be useful to humans."
You: "Just that it's interactive. To prove anything non-trivial you have to guide the system with the right lemmas. My example wasn't meant to be particularly insightful.."
Oops, thanks for assuming I was making a rhetorical point or something. I actually asked because I didn't know even that much about ACL2. :)
---
Me: ""Any field is going to strive to minimize humans' responsibilities in the loop or else it won't be useful to humans."
You: "Lots of ways to do so have long-term ill-effects."
Yeah. I said "useful" and "humans" in a way that I hoped would cast doubt on the long-term effects. :)
The author has more faith in the morality of human values than I do. I think there's quite a lot that's just an accident of the culture we're currently in.
On the other hand, the author has far less confidence in the resilience of art, science, and philosophy than I do. I think any competitive process will spend some effort on research to make sure it's spending the rest of its effort wisely. In fact, I strongly suspect that we have no innate goal to pursue, so all our effort eventually serves the needs of research.
What's the problem here? The fact that people are being bombarded with lies? Consider the alternative:
- If we tell the same "truth" to everyone, then everyone will find themselves persuaded by the same few marketing pitches, and the wealth gap increases.
- If we educate everyone well, we develop technology faster, and that technology further disrupts our social ties to each other, displaces jobs, increases the risk of world annihilation, accelerates our population growth, and introduces new intelligent beings who may have even more severe problems to deal with than humans do.
- If we take our claims seriously enough not to include a snide hint of "you'd better fact-check this yourself," then the burden of proof remains with the speaker, so fewer people will be able to invest in the task of passing along the message.
- If we try to tell the whole truth to someone, but fail to discuss each topic in the proper proportion, then they may be frustrated by non-sequiturs, or they may mistake certain topics to be more relevant than they are.
There's probably no way to tell the truth to humans without being deceptive and exploitative. There's probably no such thing as truth at all, only the natural selection of processes that are good at doing research.
That doesn't mean we won't care about this, but the result of our caring is to bombard humans with lies.
I think if we stop encoding our claims using discrete snapshots, we can stop judging them on the discrete scale of whether they're "true" or not, and we might find a better overall quality of communication. (Of course, I don't mean we could stop altogether. I mean we could use discreteness less frequently.)
To return to your statement that "any field is going to strive to minimize humans' responsibilities in the loop or else it won't be useful to humans," this isn't a simple gradient descent problem, where there's a single tightly constrained way to add automation and we always minimize responsibility in the exact same way. There are lots of different ways to automate, and we think up new ways all the time. My claim is that it's worth being thoughtful and restrained about what automation we introduce, thinking hard about what its effects may be, and how we may roll it back if we find overly high unanticipated costs.
BTW, I encountered another example of runaway automation since I wrote my earlier comment. http://www.foreignaffairs.com/articles/141729/francis-fukuya... was a great narration of the recent history of the US in using people as automation by creating rules and institutions, and the incredibly poor consequences of some of those decisions.
I don't know :) I struggled to share some half-baked ideas in my post above, but yeah it's very hard to do rollbacks in the real world when people start to rely on infrastructure. Even my example of forcing people to sell houses in some situations is probably politically infeasible.
I'm far more confident :) just pointing at these problems to motivate that certain kinds of constructs might be counter-productive in software because they encourage the wrong kinds of dynamics. For example, I think namespaces and modules are socially counter-productive even when implemented in a technically impeccable manner, because they encourage division of labor. In even the smallest of projects, without thinking about it, we often start out with the goal of working with someone, and proceed to do so by defining an interface and then working apart. That is no fun, and it is gradually self-limiting. I think we do this because it's so easy to create interfaces and silos, to isolate ourselves from each other's changes using namespaces and similar constructs. By creating these constructs, by making them too easily available, we forget to ask if we should use them. This is, I think, an example of taking the human out of the loop. But even here, the only way I can think of to do a rollback is by trying to build a software stack from scratch.
"For example, I think namespaces and modules are socially counter-productive even when implemented in a technically impeccable manner, because they encourage division of labor. [...] it's so easy to create interfaces and silos [...]"
Well, I think module systems are an essentially flawed idea just like everything else, and "socially counter-productive" is something I might say alluding to specific ways they're used, but I don't think they're implicated like that.
These days it's very easy to whip up ad hoc module systems with precise encapsulation properties, because we've already had such a push toward OO encapsulation; shared-nothing concurrency; proof systems with cut elimination; a concept of independent free will of each human; etc. However, some of the easiest ad hoc systems have obvious shallow flaws, like allowing accidental namespace collisions.
On a good day, encapsulation is great for vagueness: If I have to encode a vague concept in a precise programming language anyway, I'm going to think of more than one way to formalize it, and encapsulation gives me a buffer for trying several possible formalizations without changing the rest of the system. Encapsulation lowers the cost to tackle vague projects. It's arguably the kind of rollback you're looking for!
A technically impeccable module system would lower the cost to create technically impeccable encapsulations, thus facilitating vague projects better than ever... on a good day.
However, module systems will always have incremental upgrades to work on, especially regarding performance and logical completeness, not to mention keeping up with innovations in how we choose to share our vague concepts or how we choose to audit them back to their authors. And module systems will at best be a bit of a siren's song, since they facilitate the irresponsible practice of making things.
---
"In even the smallest of projects, without thinking about it, we often start out with the goal of working with someone, and proceed to do so by defining an interface and then working apart ."
Yeah, even in cases where people specifically try to work together more seamlessly, it's hard. To be blunt about it, human bodies are already silos, and our natural language utterances are practically discrete snapshots already.
As a way forward, I'm interested in encapsulation methods that don't lie about how encapsulated they really are in practice, and I'm interested in finding a more "direct manipulation" style of programming rather than the text-editing/tree-editing we use now.
I'm kind of liking David Barbour's plan and your plan of keeping a history recording. Perhaps this recording could be the primary thing that is actually "made," and programs can be invoked from examples there rather than meticulously authored.
That's interesting. I guess I'm only opposed to namespacing once the experiments stop ^_^. But it seems hard to draw that line..
"I'm kind of liking David Barbour's plan and your plan of keeping a history recording. Perhaps this recording could be the primary thing that is actually "made," and programs can be invoked from examples there rather than meticulously authored."
Interesting. I'm not sure how that would work, but in the past week I thought of a maybe-similar hypothesis: perhaps we need notational help only for scenarios. So when you open a new codebase, the first thing you should see is example runs. If you're lucky they'll be curated by the previous programmer, guiding your attention first to the most basic functionality. But regardless, you see some notational representation for scenarios, while the code itself that implements that functionality remains in a sort of assembly like language. I'm not opposed to high-level languages, but if we have a limited amount of bandwidth for managing notations, perhaps we should feed tests before code.
And yeah, I too noticed that my attention has moved from notation to tools, just like David Barbour. It happened without conscious desire to imitate him, as far as I am aware. Perhaps that means we're on the right track.
Upon further thought, I'd take a world where users and forkers can create new namespaces, but every individual project restricts itself to a single one.
I'm a bit confused about where this is going. I was hoping you'd stay skeptical of division of labor and either:
- bring up some alternatives to making things:
- bring up some inexact techniques for buffering an unstable vague subproject from the rest of a project, rather than using precise modularity/encapsulation; or
- bring up some more specific technical arguments against module systems, so that I know what to look for when I'm contemplating inexact techniques myself.
I don't want to treat you as a vending machine of arguments, but if you just so happen to have ideas in these categories, I'd find them particularly interesting. :-p
---
Regarding example runs: What if the only way to share a program were by example? In fact, is that true right now? What would distinguish program notation from scenario notation?
I'm probably at the limit of my ideas :) I don't want to overstate how confident I feel about them. So far the best way I can think of to prevent module overuse from making codebases harder to understand is: don't support namespaces.
But I hadn't considered inexact alternatives, and I still don't understand what that entails. But it might well work better than dropping namespaces entirely. So my previous comment was in the vein of vague encouragement. Just throwing out ideas as they occur to me.
---
"What if the only way to share a program were by example?"
There might well be something here! We'd need a system that can integrate multiple scenarios into a single function. But I have no idea how to build such a system, and it seems far harder than what I'm trying to build. Then again, maybe that will change as we talk about it.
---
"What would distinguish program notation from scenario notation?"
I don't know yet. I suspect every program would need to invent its own notations mimicking its target domain. A browser may need notations like "when click(...) ...", while a server might need a notation like "when received(...) ...". Maybe those two can be integrated.
The general template for a scenario seems to be when X then Y. X would include events and fakes to insert into the system at various points, either carefully orchestrated or randomized like in Quickcheck. Y would include assertions of constraints of various kinds, either on the output, or on the trace generated by the system, or on invariants at various static points in the code. That's what I have so far. Can you think of anything else?
"But I hadn't considered inexact alternatives, and I still don't understand what that entails. But it might well work better than dropping namespaces entirely."
What's your goal with dropping namespaces? You keep bringing up namespaces as though they're what I'm talking about, but I don't even know what kind of namespaces you have in mind. :)
I am indeed interested in dropping certain approaches almost entirely in favor of inexact alternatives, but until I understand what those alternatives are, I can't claim it'll work very well. :-p
One example of an inexact encapsulation technique is physical separation: Two people build Lego structures on separate corners of a table, and sometimes they may construct bridges linking the two, but they can return to working on separate parts at any time. (Unfortunately, this is still an example of making things.)
There are probably lessons in architecture, improvisational theatre, sociology, and a lot of other fields where the boundaries are soft. Just because the boundaries are soft doesn't mean they can't go fast enough to compete with the automation power of logical precision... or does it?
One thing that might help in the meantime is to think not about making things but about augmenting ourselves. This way we may still be dealing with discrete this-person/that-person divisions, but at least we aren't dealing with a discrete creator/creation divide on top of that.
(David Barbour has been talking about encouraging the self-augmenting task of programming the programmer-UI, but I'm not sure I remember his motivation. Maybe I'm getting this from him.)
---
"The general template for a scenario seems to be when X then Y . [...] That's what I have so far. Can you think of anything else?"
That actually sounds a lot like the Era module system. Not in the idiosyncratic details, but in the fact that we're both managing top-level sets of things that do Y when hedged under some modality X. I'm thinking of them as programs with imports, and you're thinking of them as testing scenarios with initial conditions. Perhaps scenarios are a special case of programs?
I'd say "perhaps they're complements," except that it's hard to be specific about any concept without it turning into a program at some point.
This might be why I'm interested in understanding programs as a special case of scenarios, if that's possible.
Sorry, no offense intended. I think I'm ascribing to you something closer to the mainstream view than my own, so you're a stand in for the world :) Not entirely without reason; you haven't said anything in this thread but at other times you've wanted to define your modules in such a way that they'd continue to work regardless of what surrounding modules contain. I'm not sure how far you've moved away from such ideas, and I don't mean to attack you, so where I imply 'you', please assume 'everyone but me'. :)
My radical approach is: "let the caller hack my library to get it to work." Have you been converted quite that far? :)
Er, sorry, I think I must talk in a decisive way that makes me look offended. I'm so indecisive in general, I have to take what I can get. :)
I don't know what would have offended me, so don't worry about it.
---
"I think I'm ascribing to you something closer to the mainstream view than my own, so you're a stand in for the world :)"
As someone who has spent this thread denouncing the evils of truth and making things, when I hear you calling me mainstream, it's heartwarming. :)
---
"Not entirely without reason; you haven't said anything in this thread but at other times you've wanted to define your modules in such a way that they'd continue to work regardless of what surrounding modules contain."
Yes, that's something I still consider an essential feature of a module system. And I think module systems themselves are a matter of course if we have a certain culture around collecting discrete snapshots (research).
But I'm interested in how much we can avoid discrete snapshots in the first place, as well as what different cultural attitudes we could adopt.
---
"My radical approach is: "let the caller hack my library to get it to work." Have you been converted quite that far? :) "
That depends on what context we're discussing this in.
- In the here and now, I'm taking this day to day. I don't have a strong opinion on how much I should indulge my fondness of making things, what I should make or procure for whom, and how much I should work closely with whom. Nor do I have a strong opinion on what you should do. :-p Or maybe it's more accurate to say that I might have strong opinions if presented with more specific situations.
- For thousands of years down the line, I hope we get to "let the caller communicate with me if they want to, under the ethical and logistical supervision of our local peers." There's rarely a "library" involved, and if there is, it's the one who's saying "me" here.
- In the projects I've been working on and pondering, I already have a (very incomplete) plan to allow Era modules to break each other's encapsulation. The idea is that one module should have special privileges with another module as long as it can prove it already knows that module's implementation code or has that module author's permission. A user must have the implementation code of a module in order to install it in the first place, so they can of course hack on it and break compatibility and find themselves rewriting a whole chain of depenencies to use their customization, but this way they can sometimes just add a new module that takes advantage of having extra insider knowledge about the original.
In fact, now that I think about it, this "extra insider knowledge" access is similar to the extra information you're trying to access from your scenarios concept. Hey, we might be able to design a formal system together. :) Did you ever get around to looking at linear type systems?
EDIT to add: I was encouraged to think about this "extra insider knowledge" plan thanks to a recent LtU thread about "private" and "public" access controls: http://lambda-the-ultimate.org/node/4965. The Era module system has only one top-level definition per module, so a private definition doesn't make sense unless there's some way to break into it. Before that thread, I was already worried about what it would take to allow an author to prove extra things about the other definitions they had already published, but that's where I started thinking about a solution.
"Did you ever get around to looking at linear type systems?"
I was (and still am) waiting for you to teach them to me :p
My recent explorations are hardening my bias for operational/ugly/imperative approaches over declarative/elegant/functional ones. The trouble with being elegant is that the implementation is harder to tinker with without causing subtle issues. Later readers have to understand global properties at a deep level.
Don't get me wrong, there's room for high-level languages in my world-view. They're just harder to communicate the big picture of, and I want to start with simpler problems. High-level languages and type systems are hard crystals, and I'm currently more interested in building squishy cells.
"I'm currently more interested in building squishy cells."
I respect that, but just to get it written down, here's what I've come up with so far:
All lambda expressions must be lambda-lifted so they're at the top level of some module. (I guess this might count as a lambda-free logical framework.)
Every function type (X -> Y) is annotated as (X -> [C] Y) with the implementation code C of that function's source module. A simple export/import of an X-to-Y function will actually bind an import variable of the type (existscode C. (SatisfiesSignature S C * (X -> [C] Y))), where S is the import signature.
If a module has access to installed implementation code C and it knows SatisfiesSignature S C and SatisfiesSignature S C', then it can conclude C = C'. It can take advantage of this knowledge to get access to fuller knowledge about how an (X -> [C'] Y) function behaves.
---
"I was (and still am) waiting for you to teach them to me :p"
Really? I don't remember where we left off, but let's get in touch on this.
"..if many programmers each willingly stray from standard notations, they make the world of program code less useful in at least one way, to at least one audience: Newcomers face a steep learning curve, encountering different notations everywhere they look."
But if you rely less on notation and more on interactive feedback, this might be less of a problem :)
Since Arc compiles each Arc expression to MzScheme before executing it, and since MzScheme compiles each of those expressions, Arc inherits this on-demand compilation behavior.
Yeah, atstrings is a pretty awkward feature. It is possible to opt in and out of it, but only at the top level:
(declare 'atstrings t)
(declare 'atstrings nil)
This affects how strings are processed by 'ac. That is, it only matters for compilation/macroexpansion phases.
I see some of Anarki's tests set 'atstrings to a particular value as they go along, but then they don't reset it. This might be why you see some tests working when yours don't.
(In Anarki, the declarations* global table holds these values, so it's technically possible to save and restore the old value by reading that table.)
---
The REPL behavior you observed is confusing in a different way than you thought. It treated your input as a malformed command, three well-formed commands you didn't know you were entering, and finally an unfinished string literal:
"abc\@
example.com
"
"
abc@@example.com
"
This is where the "\n" response came from, and it's why you received multiple responses per input line.
I'm unable to reproduce it now. I disabled the bug fix, and I believe I am now running the same code as I did when the bug occurred, but I no longer get this exception when I visit that url (it's an ordinary GET request without parameters). The state of the app has also changed and I wonder if this state change has something to do with it.
What's certain is that it's a call that passes 3 arguments to cut. I had logged the exact call to cut to be:
Yeah, none of those call sites seem responsible, as far as I can see. The calls in markdown and unmarkdown only trigger if the string starts with certain chars, which it doesn't. Remind me, are you using anarki?
Ah, you're using arc 3.1 without the mutable-pair bugfix: http://arclanguage.org/item?id=18070. Anything can happen after a race condition. Hold on, let me patch that bug to the stable branch so you can pick it up.
Edit 1 hour later: Done, https://github.com/arclanguage/anarki/commit/b683a84a68. lark, can you start using this stable branch? Should be a lot less disruptive than migrating to anarki, and will also help us better help you.
Thank you for tracking down and pointing out the mutable pairs issue.
I wish there was a version of Arc that made it clear what was changed in it, and made it easy to not apply some changes. A version that consisted only of:
- the original, untouched, Arc 3.1 -- however broken it might be.
- a single Arc file called fixes.arc that contains all fixes needed as redefinitions (say, the bugfix for cut). This is different than modifying the originals; you always know what was modified.
- a collection of .patch files, to patch what can't be provided in fixes.arc (like ac.scm)
- a script called arc.sh that (a) deletes all patched files, (b) creates new copies of the original files (untouched), (c) patches them, (d) starts Arc.
About the github commit link, I don't understand what's going on with it:
- I don't know what kind of branch this is -- is this Arc 3.1 or Anarki? I want to keep using Arc 3.1.
- If it's Arc 3.1 why is it named Anarki?
- Is there a way to get a diff? If not, how do I extract this branch and then get a diff (do I need a different link, and if so which one?) Is there a way to not use git?
(Couldn't this be easier? Why is there so much work involved? I think I want a new way to organize programs.)
You know, you don't have to switch to the entire 'stable' branch. Just apply the patch linked to in my last comment. It doesn't rely on anything outside arc3.1.
Anarki has multiple branches: http://i.imgur.com/WefAVGX.png. Notice the branches 'official' and 'stable'. 'Official' is just the various releases of arc, and 'stable' is releases + small bugfixes. To see what's changed in 'stable' since arc3.1, look at the list of commits: https://github.com/arclanguage/anarki/commits/stable. There's only 15 commits or so on that page after the arc3.1 release, and you can click on them to see what was changed. Scanning the list, really there's no other important bugfixes.
I keep a directory called arc3.1 by doing:
$ git clone http://github.com/arclanguage/anarki arc3.1
$ cd arc3.1
$ git branch -a # list all branches
$ git checkout stable
I can then see the same list of commits by typing 'git log'. Or generate diffs by typing:
$ git diff remotes/origin/official
This is enough information for you to create and manage a repo just how you described. I have a slightly different itch, so I have a different approach: http://github.com/akkartik/arc. Back before me, a user called aw created a new approach as well: http://awwx.ws/hackinator, which I doubt is actively maintained. You should create one too and show us. If it's substantially better we'll switch to it. Over time.
I'm sure everything could be much better. (I'm trying to create a new way to organize programs, after all: http://akkartik.name/about.) But no matter how you organize programs, people would still need to know about some baseline tools, like diff and patch and scripts in your description above. For anarki you need to learn some git at the moment.
If you're going to resort to patches to manage code as text, that's what source control systems like Git are good for. They also manage the emergent complexities with this approach:
- They help highlight one version of the code as the canonical one that most people are expected to be using. That way development effort isn't wasted on out-of-date code combinations that nobody cares about.
- They provide ways to merge textual changes, revert them, and otherwise apply them out of order.
What you have in mind might be a coherent alternative to the existing version control systems, so I'm not just saying to give up. :)
---
"Is there a way to get a diff?"
Git makes it easy to create diffs (using the "git diff" command with various arguments), and GitHub makes it particularly easy to view certain kinds diffs on the Web. In GitHub, if you're on the stable branch and you click the "commits" button you get to this page:
Don't bother trying to copy and paste a diff from GitHub though. I think you'd have better luck at a command line, even if it means learning several commands. (I'm not sure I know all the right commands myself though. >.> )
---
"If it's Arc 3.1 why is it named Anarki?"
There are a lot of ways to answer that question. If someone wants to make a contribution to Arc so that every other Arc user can benefit, that's what Anarki is for. However, not everyone uses Anarki, so the fantasy is that it's Arc, but the reality is that it's a specific project that we refer to by a specific name.
I'm not actually sure why that branch of Anarki exists, but it looks like it was created to support plain Arc 3.1 coding without all the extensions people made to the master branch. Its differences mostly have to do with adding text editor extensions and shell commands to support Arc 3.1 programs.
"Don't bother trying to copy and paste a diff from GitHub though. I think you'd have better luck at a command line, even if it means learning several commands. (I'm not sure I know all the right commands myself though. >.> )"
True. I coulda sworn github had a raw view. Was that just for snapshots?
I'm concerned about looping. It looks like you're implementing function calls in terms of JavaScript function calls, so there's no tail call elimination, and a recursive loop will soon exhaust the JS stack. A while loop could be a practical alternative.
Thanks to your advices. The runtime is based on Node.js, so there is no tail recursion elimination, I'm finding a method to transform recursion code to iteration code automatically, do you have some advice?
If you want to have full tail recursion elimination, I recommend changing everything to work in continuation-passing style. That will overflow the JS stack even sooner, so I recommend using a trampoline.
// before
function plus( a, b ) {
return a + b;
}
// after
function plus( a, b, then ) {
// We call `then()` only after we've unwound the stack.
return trampolineBounce( function () {
return then( a + b );
} );
}
// the trampoline system
function trampolineBounce( func ) {
return { type: "bounce", func: func };
}
function runTrampoline( func ) {
var step = func( function ( result ) {
return { type: "result", result: result };
} );
while ( step.type === "bounce" )
step = step.func();
return step.result;
}
// example usage
var onePlusTwoPlusThree = runTrampoline( function ( then ) {
return plus( 1, 2, function ( onePlusTwo ) {
return plus( onePlusTwo, 3, function ( result ) {
return then( result );
} );
} );
} );
This part is a bit redundant:
function ( result ) {
return then( result );
}
It's equivalent to `then` but uses one more JS stack frame. We can just reuse the `then` continuation directly. In general, the places we can do this are tail calls.
// example usage with a tail call
var onePlusTwoPlusThree = runTrampoline( function ( then ) {
return plus( 1, 2, function ( onePlusTwo ) {
return plus( onePlusTwo, 3, then );
} );
} );
---
"I'm finding a method to transform recursion code to iteration code automatically"
I don't think it's possible to get full tail recursion elimination that way unless you statically analyze the whole program, or you transform the code so it uses the CPS-and-trampolining technique above.
Maybe you can get something somewhat less powerful than full tail recursion, but I don't have advice on that. If you do want to work on that, here's an example of a tail-recursive while loop you can test with:
; Evaluate an expression repeatedly until it returns something falsy.
( (lambda (f body) (f f body))
(lambda (f body)
(if (body)
(f f body)
#f))
(lambda () ___)) ; insert expression here
"It's the kind of takeover any language could do to any other"
JavaScript's a big language. It comes standard with complex (and I'd say quirky) systems for floating point numbers, date handling, and regexes. ECMAScript 5's strict mode repurposes the same JavaScript syntaxes for slightly different semantics, fracturing the language even within the same spec. That spec also defines at least three different ways of interpreting a JS string as JS code, if we count eval(), strict mode eval(), and the Function() constructor.
The standard is just one small glimpse of the landscape of JavaScript compatibility:
The language is commonly split into intentionally different dialects. The HTML spec willfully violates the ECMAScript spec so that document.all can be falsy[1]. Mozilla's asm.js repurposes the same JavaScript syntaxes to have substantially different performance when they're used a certain way. And of course there are proper extensions and variations of the language, like TypeScript, Harmony, Mozilla's own line of JavaScript versions, the JavaScript-inspired UnityScript and ActionScript, etc.
Nonstandard features are frequently needed. Different JavaScript platforms like the HTML spec, Node.js, Rhino, RingoJS, and Windows Script Host add (or don't add) their own varying tools for concurrency, sandboxing, error handling, debugging, global scope management, package distribution, and module imports, which tempts even general-purpose library writers to stray from standards. Meanwhile, JavaScript libraries sometimes silently take advantage of widespread but underspecified features such as function names, function toString() representations, object.__proto__, and object key iteration order.
Given all this, the good news is that a translator from JavaScript can't be expected to have 100% compatibility. Like Caja, the Clojure Compiler, and Browserify, it may have to put quirky limits on the subset of JS code it intuitively supports. One place to look for a not-so-quirky subset might be lambdaJS[2], which has been used for formal correctness checking.
Versu is an interactive story platform that was developed in a project overseen by Linden Labs, best known for Second Life. The drivers behind this project were Emily Short, a famous author of text-based interactive fiction, and Richard Evans, who had previously worked on the AI for The Sims 3. These are some documents describing the platform they built.
I think one of the docs people here would like is "Prompter: A Domain-Specific Language for Versu":
Graham Nelson made Inform 7, an interactive fiction engine with English-like syntax. Now he's made Prompter, a similarly naturalistic language for Versu. In that introduction to Prompter, he talks about motivations and principles for DSL design.
Prompter compiles to Praxis, which is a logic programming language Richard Evans designed for Versu to handle characters' reasoning, decision-making, emotions, manners, etc. The core of the formal approach is described in "Computer Models of Constitutive Social Practice":
I haven't had a chance to read "The AI Architecture of Versu" yet, but seems to describe the practical details of the development tools for Versu, focusing around Praxis:
I expect almost anything that makes sense for Versu AI authoring will probably be possible to achieve in Praxis thanks to its highly logic-influenced design, and that the most frequently needed things will be ergonomic for writers thanks to Prompter.
I'm not sure how much these tools are public yet, and actual playable Versu stories have only been built for iOS, but it'll be nice to see this improve. Linden Labs had actually shut down the Versu project earlier this year, and this version of the Versu website was just opened up this week, so the project seems to be in a trajectory toward more openness.
It actually surprises me how much Versu development resembles some of my own thoughts about organizing large-scale interactive story plots. I once dreamed of making a language for building interacting FSMs, with support for automatic testing or formal model-checking to find dead code. Apparently this was all in the scope of Praxis and Prompter tools, and I didn't have to lift a finger to see this dream come true. ;)
I'm concerned about some drawbacks this project might face, having to do with the use of instantaneous events for modeling story actions and the use of inline dialogue generation.
Instantaneous events are something I tend to see as a misfeature[1]. The notion of a discrete timeline is baked into the sequence-of-paragraphs UI, the screenplay-like Prompter language, the state machines, and the visualization graphs. Even the logical framework needs it, since the characters instantaneously "remove" past beliefs as they're convinced of incompatible beliefs to replace them.
At least it can be said that in Versu's chosen problem domain, events are essential. They couldn't be "fixed" without changing basically the entire story UI, the tools, and the formalism, leading to a completely different product. At least this project seems to have good tooling to tackle many event-related issues head-on.
As for inline dialogue generation, I've just never been comfortable with the thought of generating story prose using string concatenation; at least a more structured format would be nice. Emily Short and Graham Nelson are two of the people who think about this topic quite a lot, and their context-sensitive text macro approach is impressive, but it uses too much stateful magic for me to want to dive in and use it. (Same with some of the naturalistic syntax...) It's not like I dive into story-writing regardless, but it's one of the basic motivations I use to write programs, which is why I have opinions like this one. :-p
Regardless of any drawbacks--even if Versu had been a closed system under Linden Labs--I was always eager to use it. I may not write full stories, but I do design lots of original characters and settings, and Versu has been designed with the high-level idea that characters can be swapped out for each other to see how they interact in different circumstances. It could be just the right platform to give some life to my ideas.
How would you build a declarative continuous state system on top of arc? Or would it be better to start from scratch?
And what kind of metaprogramming models besides function/macro/fexpr do you see working on top of such a system?
It seems like it could either be a really 'alien' programming model that requires very different approaches, or a simple set of wrappers on top of existing models. I don't know enough about it yet.
Good questions! I keep wanting to tie this stuff back to Arc somehow, so that people here can care, lol, but it's hard to see where to start.
I'll assume we're talking about something like RDP, but not quite RDP. David Barbour's goals for RDP include safe distributed programming, secure mashups, and sometimes even type-directed reduction of boilerplate. Thus RDP isn't necessarily RDP without an appropriate static type system, and we proabably don't need to commit to that part here.
Let's just call what we're talking about "continuous reactive" code, or "continuous" for short. :)
I'll answer out of order a bit.
---
"And what kind of metaprogramming models besides function/macro/fexpr do you see working on top of such a system?"
All of these actually keep making sense in a continuous system. The only things that can't technically be carried over are certain kinds of side effects.
Here's a more complete description of the differences:
- Most imperative side effects don't make sense. If (do (= x 1) (= x 0)) is executed continuously at every instant, then at no time is x just 1 or just 0. Continuous reactive programming needs its own particular side effects to deal with continuously displaying things, continuously maintaining connections to things, continuously monitoring the current state of the mouse, etc. Instead of mutation, continuous reactive code can use demand monitors, which aggregate sets of values. Similarly, we would have to avoid effects related to first-class continuations, threads, etc., unless we can make sense of them.
- Since all the effects at a given instant are simultaneous, the data/control flow doesn't need to be processed in a particular order, and it can naturally be concurrent, which in turn means each concurrent part can be undisturbed for a good period of time without needing to be recomputed. This means certain constructs like (if ...) stick out as expensive operations, since they must synchronize their inputs and outputs rather than letting them pass through concurrently.
- Eval and function calls are pretty weird operations. In a continuous program, the entire data flow graph exists like a constantly marching trail of ants, sometimes switching between different conditional branches. I think a dynamic call causes that trail of ants to sometimes snap between different iterations of a fractal pattern. My current implementation[1] supports "functions" but doesn't support arbitrary recursion, because I keep every possible trail instantiated regardless of whether it's currently in use. It's like all the functions are inlined whether we like it or not. (I actually kinda like it.)
- If we care about secure mashups, passing a big context object around tends to be a bad idea, especially if it's implicit. Fexprs may need a redesign in order to avoid passing authority to code that doesn't need it.
---
"How would you build a declarative continuous state system on top of arc? Or would it be better to start from scratch?"
(Building the execution semantics is probably a bigger challenge than building the state resources, so I'll assume that's what you mean.)
While starting from scratch would keep things simpler, I don't think it's necessary, as long as there's some bridge to manage the continuous side from the procedural side and/or vice versa. A single language could have dedicated syntaxes (e.g. lisp special forms) both for procedural code and for continuous code.
I've actually been thinking about how a single (fn ...) syntax could be used for both. Pure code is a special case of error-free continuous impure code, and that in turn is a special case of error-free procedural code, so a single abstraction might cover it all.
However, dynamic errors make things more interesting. In procedural Arc code, (do (foo) (err "oops 1") (bar) (err "oops 2")) would not execute (bar) because that comes after an error has occurred. In continuous code, all of the code is executed at every instant, so there is no "after" to speak of, and (bar) executes at the same time as both errors occur. (Actually, David's approach would be to terminate the computation as soon as possible and try to roll back any effects that have occurred in the meantime, so in that case (bar) would not usually be seen to execute.)
This is enough of a jarring discrepancy that I've tabled the idea of unifying the (fn ...) syntax in the procedural language I'm working on.[2] I think it's something that can be done, but it's more complex than I first thought.
Anyway, suppose we have two different syntaxes, or suppose (as David prefers) we don't have a continuous (fn ...) syntax at all but instead use concatentive programming techniques.[3]
Either way, it's painful to bridging between one style of computation and another.
Procedural code managing continuous code: Procedural code has to deal with continuous signal input similarly to how it would traditionally deal with keyboard and mouse input, and it has to deal with continuous signal output kind of like drawing to a canvas. This means writing 2-5 imperative code blocks for every bridge, at least until some common bridge patterns can be abstracted away.
Continuous code managing procedural code: One way to use continuous code to trigger an imperative action would be to build a continuously updating collection of the imperative actions we want performed, annotating each one with a timestamp so it's clear when it's supposed to happen exactly. This is cumbersome enough that I haven't tried it yet, but it's basically the kind of FFI layer that a continuous-only language would need in order to play nicely with existing OSes and protocols.
The Web is a good example of a system that already has bridges like what we'd need. An HTML document can have continuously existing tags and attributes that cause JavaScript code to be executed at certain times. In turn, JavaScript code can insert and remove declarative HTML and CSS as it goes along.
Sorry for using so many JS metaphors. :) Aside from Rainbow's UI experiments, I don't know of any Arc applications which continuously monitor or display something.
[3] Either way, I've pretty much made this in JavaScript already,[1] but my (fn ...) syntax is a bit cumbersome because I'm using strings for variable identifiers, and my concatenative syntax is even more cumbersome because it has to pass around explicit first-class type annotations with no type inference. I think there's room to improve on this, especially in something like Arc that has macros.
In case anyone wonders what's wrong with (if ...), I've put together a demonstration of translating Arc-like code into the flavor of generalized arrows[4] David and I use for implementing this stuff:
(if foo
(bar baz)
qux)
( | + | ) | | |
( foo(true) + foo(nil) ) bar baz qux
=======================Disjoin=====================
foo(true) bar baz qux + foo(nil) bar baz qux
| | | | + | | | |
Drop Drop Drop | + Drop =Apply= Drop
| + |
=========Merge========
|
In this graph, sum types (A + B) are edges separated by + and product types (A * B) are simply juxtaposed edges. I've labeled the edges around Disjoin to show which signals go where.
The "Disjoin" operation is the distributive law on product and sum types, taking ((A + B) * C) to ((A * C) + (B * C)). In simple cases, it has to synchronize C with a combination of A and B so it knows which output to send C's packets down.
The "Merge" operation takes (A + A) to A, forgetting which branch it was on. In simple cases, it has to synchronize the original two signals becuase the output should only be inactive when both inputs are inactive. If we're using dynamic typing, we may also need to check the inputs dynamically to make sure their periods of activity don't overlap.
Overall, one call to (if ...) causes multiple synchronization points in the code.
You might wonder what Arc could use here if not (if ...), and my guess is it could use a structured data type with associated special forms that can be implemented with implicit concurrency:
(def left (x) (annotate 'left x))
(def right (x) (annotate 'right x))
; Sum types distribute over product types.
; ((A + B) * C) -> ((A * C) + (B * C))
(disjoin (left a) c) --> (left (list a c))
(disjoin (right b) c) --> (right (list b c))
(disjoin other c) --> (err "Can't disjoin")
; Sum types are commutative.
; (A + B) -> (B + A)
(mirror (left a)) --> (right a)
(mirror (right b)) --> (left b)
(mirror other) --> (err "Can't mirror")
; Sum types are associative.
; ((A + B) + C) -> (A + (B + C))
(assocsl (left (left a))) --> (left a)
(assocsl (left (right b))) --> (right (left b))
(assocsl (right c)) --> (right (right c))
(assocsl other) --> (err "Can't assocsl")
; (A + (B + C)) -> ((A + B) + C)
(assocsr (left a)) --> (left (left a))
(assocsr (right (left b))) --> (left (right b))
(assocsr (right (right c))) --> (right c)
(assocsr other) --> (err "Can't assocsr")
; (A + A) -> A
(merge (left a)) --> a
(merge (right a)) --> a
(merge other) --> (err "Can't merge")
; (A -> C) -> (B -> D) -> ((A + B) -> (C + D))
((lift-sum l r) (left a)) --> (left (l a))
((lift-sum l r) (right b)) --> (right (r b))
((lift-sum l r) other) --> (err "Can't lift-sum")
This is quite a bundle of special forms to add, and I'm probably forgetting a few. I'm not exactly sure how to implement them, especially considering they would have to deal with Arc's arbitrary recursion. ^_^;
Thx,i have almost done my arc site,but when a user forgot his password and he need to reset by email,but cannot get the email...i wanna know how to add email feature in arc site? by the way,i use mailgun.com to send and they give me HTTP API.like this http://documentation.mailgun.com/quickstart.html#sending-mes...
Anarki doesn't have mailgun support yet. It will reset passwords if your server supports sending email using the local unix sendmail command. So I can think of two options:
a) Does mailgun provide a sendmail command that you can download? If so, just installing that might be all you need.
b) Anarki has rudimentary support for GET and POST on arbitrary urls.
Can you try to figure out the right URL to put into the get-url call or similar post-url call? They're implemented in lib/web.arc, and feel free to ask us questions to help get you on your way.