Arc Forumnew | comments | leaders | submit | rocketnia's commentslogin

This looks like an awesome intro to fexprs. Maybe it should even go on Arc Lang Wiki. XD

-----


Are you uploading a text file encoded in UTF-8, as 'readc expects? It's probably waiting for the rest of a character.

If 'readb gives you issues too, then that's not it.

-----

1 point by lark 5033 days ago | link

readb gets stuck too.

Also, is there something like aform-multi that does not use fnids? I need a static upload url.

-----

2 points by lark 5032 days ago | link

This works at least:

  (mac form-multi (n . body)
    (w/uniq ga
      `(tag (form method 'post
                  enctype "multipart/form-data"
                  action ,n)
         ,@body)))

-----

2 points by lark 5032 days ago | link

Only 389120 bytes are read with a readb.

This is precisely 380 * 1024, which is suspicious.

-----

1 point by akkartik 5032 days ago | link

I can read files past the 380K limit. Here's me reading a 400K file.

  arc> (= i 0)
  arc> (w/infile in "x" (whilet c readb.in ++.i))
  arc> i
  400000
Does this work for you?

-----

1 point by lark 5032 days ago | link

This example works for me, but it's not an example that reads a file that is uploaded. It's an example that reads a file that is local.

Can you read the file that was POSTed and save it? Ignore parsing out the multipart stuff. Just save what came in through the request.

Here's the test at /upload_is_broken

  (mac form-multi (n . body)
    (w/uniq ga
      `(tag (form method 'post
                  enctype "multipart/form-data"
                  action ,n)
         ,@body)))

  (defop upload_is_broken req
    (form-multi "upload_static"                           
      (gentag input type 'file name 'name)
      (submit "upload")))

  (defop upload_static req
     (withs (n req!clen
               f (string "/dev/shm/" (uniq)))
            (pr "saving multipart data\n")
            (pr "clen is " n "\n")
            (w/outfile o f
                       (whilet c (and (> n 0) (readb req!in))
                               (-- n)
                               ;;(pr "n now is " n)                                                                         
                               (writeb c o)))
            (pr "SAVED multipart data. Success!!!\n")))
This does not work for me.

-----

1 point by akkartik 5031 days ago | link

It worked fine for me:

  saving multipart data clen is 1591333 SAVED multipart data. Success!!!
What's the specific file you're uploading?

-----

1 point by lark 5031 days ago | link

I misled you, I'm sorry. The example I just provided works for me.

http://tyche.pu-toyama.ac.jp/~a-urasim/lsvl/data/bzip2_1.0.5...

  $ md5sum bzip2_1.0.5.orig.tar.gz 
  3c15a0c8d1d3ee1c46a1634d00617b1a  bzip2_1.0.5.orig.tar.gz

  saving multipart data clen is 841607 SAVED multipart data. Success!!!
Yet the full app I have that uses this logic does not work. I'm not sure I can explain why.

-----

1 point by akkartik 5031 days ago | link

Thanks for the update. If you manage to narrow it down to a new code sample I'd love to see it.

-----

2 points by lark 5031 days ago | link

I verified that the example code I just provided does not work if nginx 0.7.67-3+squeeze2 from Debian is proxying connections to Anarki with the following configuration:

  server {
    listen 80;
    server_name  somewebsite.com;
    access_log /var/log/nginx/somewebsite.com.access.log;

    location / {
      proxy_pass        http://somewebsite.com:2012;
      proxy_set_header  X-Real-IP  $remote_addr;
    }
  }

-----

1 point by akkartik 5031 days ago | link

That's really useful, thanks. A quick google for 'nginx post length limit' brings up this link: http://www.rockia.com/2011/01/how-to-change-nginx-file-uploa... which suggests changing client_max_body_size. Does that help?

-----

1 point by lark 5030 days ago | link

Thanks for the link.

I tried setting the following in /etc/nginx/sites-available/somewebsite.com:

  server {
  # ... various vars as in http://arclanguage.org/item?id=16317 plus the following:
  client_max_body_size 10m;
  }
I also tried with client_max_body_size 10000000;

In both cases uploading the bzip2 file hangs.

I also tried setting client_max_body_size in /etc/nginx/nginx.conf but there get a different error:

  Restarting nginx: [emerg]: unknown directive "client_max_body_size" in /etc/nginx/nginx.conf:31
So it doesn't work for me. The documentation at http://nginx.org/en/docs/http/ngx_http_core_module.html#clie... suggests that the default value is 1m. This means that bzip2, which is under 1m, should be uploaded without a problem.

Update: Tried setting "client_max_body_size 32m;" under the "http" section in /etc/nginx/nginx.conf but posting still hangs.

-----

1 point by akkartik 5022 days ago | link

Ok I finally got around to trying to replicate this, and you're right, that setting doesn't seem to make a difference.

Sorry this took so long.

-----

1 point by akkartik 5030 days ago | link

I assume you restarted the nginx service?

-----

1 point by lark 5029 days ago | link

Yes. Does upload in Anarki behind nginx work for you?

-----


"No parameter passing, no local variables, no call/return (except in gosub, which is presented later), no blocks...Basic is - in a sense - like assembly language with math expressions and graphics."

I think this kind of technical simplicity can also be found in concatenative programming (no parameter passing, no local variables, no blocks, no math expressions) or point-free functional programming (no local variables, no blocks, nothing but math expressions).

On the other hand, people who want to try teaching those styles can probably fashion their own library in Factor or Haskell that sets up the foundation they want to teach from. Those languages don't impose a lot of syntactic cruft.

---

"...they make the computer seem mysterious and complex, since the child knows that if they write that code so and so will happen, but don't understand how it happens."

I'd say real understanding isn't so easy to find among programmers.[1] Between JIT, predictive branching, optimizing compilers, and other such supercharging platform features, application programmers have long been willing to sacrifice an easy-to-understand code-to-mechanism mapping in favor of performance, even for imperative code.

Most of our high-level languages do leave things mysterious at some point, and in doing so, they save the programmer from peering into the insanity-inducing complexity underneath. :-p

Imperative languages have mystery as much as other languages, but sometimes their mystery matches up with the "how" questions we raise: "How does the computer begin to show a graphic on the screen" might be taken care of a single "begin to show a graphic on the screen" statement.

I think the "how" questions could be less imperative and more reactive: "How is the computer showing a graphic on the screen?" "How does the system know where the mouse is pointing?" In a reactive programming model, these questions could have corresponding mysterious primitives: "Sustain a graphic on the screen." "Watch where the mouse is pointing." When a programmer builds compound demands in terms of these, what else could it mean but to hold more than one demand at the same time? Concurrency is the intuitive default.

I think a programmer who can't say "how" a program works... simply hasn't built up a significant enough program to be worth explaining. They're still using mainly the primitives, whose "how" is mysteriously unanswered by the language.

Don't get me wrong, I'm happy you're making a language to help people teach programming, and I even think you're on a fine path sticking with the more mainstream and classic kinds of programming. I'm just hoping to debunk some assumptions and expand your horizons, either so you can change your direction or be even more confident where you are. :)

[1] Despite my know-it-all tone, I'm not volunteering myself as a counterexample, lol.

-----

2 points by mohamedsa 5036 days ago | link

I appreciate the horizon expansion attempt :)

When I was talking about "less mysterious and complex" I wasn't thinking about how a real CPU works, but how a programmable machine works.

My idea is that a language too high-level would make the child think of a computer as something having human-like behavior, that the computer just "knows" what to do when you talk to it. On the other hand a programming language with a well-known execution model and few assumptions would mean the child can know early how to map a language's syntax to its semantics.

Let's compare two programming languages for children, Logo and Kalimat. To print "hello" ten times in Logo:

repeat 10 [print "hello]

Here the child has to either assume the computer 'just knows'/be told "you'll understand later", or otherwise has to take some conceptual leaps in order to understand what's really happening:

* The stuff between [] is code not to be executed yet, a description of a future action to be applied

* This code is given to 'repeat'

* 'repeat' would apply this code as many times as needed

Now in Kalimat:

label top

x = 0

print "hello"

x = x + 1

if x<10 : goto top

Assuming the child already knows assignment 'if', and 'goto', the code here can be analyzed, traced, and adapted, without having to know little more than what is already known.

When the student becomes more confident, they can learn about 'for' and 'while', while being told that under the hood, those high-level constructs are pretty much the same as the previous version.

As for the many other points in your comment, I'll happily be re-reading them and pondering :)

-----

2 points by rocketnia 5036 days ago | link

  label top
  x = 0
I think these lines should be reversed. :)

(By the way, here are some terse formatting instructions: http://arclanguage.org/formatdoc. You can edit a post for about an hour after making it if you need to experiment.)

---

"When I was talking about "less mysterious and complex" I wasn't thinking about how a real CPU works, but how a programmable machine works."

Where do you make the distinction? These days I like to think of a machine as being any tool at all, and programming as being a way to control that tool by writing specification documents of what it should do.

If someone begins with no understanding of how a computer could work, maybe it makes things plausible to think there's an underlying mechanical device like a player piano reading a sequence of triggers off of a tape. But rather than focusing on that tape, I'd like to think it's also plausible to ground understanding in terms of writing the blueprints to that machine (parts of which may be tapes if necessary).

http://en.wikipedia.org/wiki/Analogue_computer

http://en.wikipedia.org/wiki/Fluidics

http://en.wikipedia.org/wiki/Electronic_circuit

I won't claim my own intuition works this way, but obviously some people work with this stuff. :-p

---

"Let's compare two programming languages for children, Logo and Kalimat."

I actually think you have apples and apples there and you're not comparing them as such. In Logo, if you want to solve 'repeat 10 [print "hello]' in a more complicated way, you can:

  to greet :numberoftimes
    if :numberoftimes > 0 [
      print "hello
      greet difference :numberoftimes 1
    ]
  end
  
  greet 10
(I just tested this code at http://www.calormen.com/logo/. It's my first Logo code. ^_^ )

I think both this and your Kalimat example involve more conceptual leaps than the "repeat" version. Here are just a few of them:

* The stuff after Kalimat's : or inside Logo's [] is code not to be executed yet.

* This code is given to "if".

* "if" applies this code if appropriate.

Beyond this, the student is challenged to understand user-defined names, arithmetic simplification, operator precedence, sequential execution, and bounded scope (in Kalimat's case, the kind of dynamic scope that arises from variable assignment).

On the plus side, it probably pays off best to choose an appropriate level of challenge for the student, and this is a good milestone (if not starting point), since the concepts are used together to solve a problem in a way that may help clarify all of them at once.

-----

3 points by mohamedsa 5034 days ago | link

In essence, my argument was

* How many concepts must be taught to the child before writing useful programs?

* How many of those concepts are right there, and how many are hidden?

I intuitively feel that Kalimat's example is just more simple and direct than the Logo equivalent, but "intuitively feel" and "just more simple..." are not very scientific; perhaps when Kalimat is more experimented with, we could have more empirical results.

But at least I can try to justify my feelings a little:

* The Logo example seems more nested while the Kalimat one is flatter. "Do this task, then that one" seems easier to keep inside one's head than do this task, which is made out of so and so.

* The Kalimat example completely avoids the need to teach function definition and invocation.

* The Logo code needs a discussion about variable scope and function activations; how :numberoftimes has a value in this greet different from that greet.

Yes, in Kalimat we'd have a related discussion about mutable variables, but a variable is an isolated concept that doesn't need to be explained alongside invocation and scope (at this level).

But again, you've made me look again at my assumptions and those of the Logo creators, and ask myself again and again about those assumptions; and that's definitely a good thing :)

-----

2 points by rocketnia 5033 days ago | link

"The Logo example seems more nested while the Kalimat one is flatter. "Do this task, then that one" seems easier to keep inside one's head than do this task, which is made out of so and so."

Surprisingly, I actually see your point. :-p For languages where "stepping" even makes sense, you can focus on explaining a very narrow window of the program, and then explaining another very narrow window of the program, following those steps. If the language is based on GOTO and simple variables, then the state of the program at any window is of a constant size, rather than structured into a stack or a tree.

Concatenative programming would pretty much lead to a stack-like growing and shrinking state right away (regardless of whether it takes shape as an actual stack).

The kind of programming I guess I'd call combinatoric programming (point-free functional, such as arithmetic expressions) would lead to a tree of partial results.

These systems could probably be broken down into even simpler subsets to limit the complexity, but probably not in a way that feels as open-ended as GOTO.

---

"The Kalimat example completely avoids the need to teach function definition and invocation."

The Logo example avoids the need to teach label definition and jumping. :-p

There's a little hiccup in understanding functions when GOTO's around. When I studied C after Applesoft BASIC, I remember being confused and curious about what would happen if I did a goto from one function to another. That's just an erroneous use of C's goto, and I think when I found that out, I got a little frustrated that anyone would program in a language with such arbitrary limitations. :-p

---

"Yes, in Kalimat we'd have a related discussion about mutable variables, but a variable is an isolated concept that doesn't need to be explained alongside invocation and scope (at this level)."

In case you missed it, the converse is also true: Invocation and scope could be introduced without introducing mutability. I can't say it's easier to do that, but it's at least independent.

Once again, I think there's some negative interplay between concepts here. Classmates from my intro programming classes found it nontrivial to grasp that passing a value to a function wasn't just the same thing as assignment to its parameter. That flawed interpretation worked pretty well until recursion came along. Even then, it was easy to repair the understanding by assuming it's a temporary assignment that reset itself after the function ended. Then they'd run into trouble when lexical scope came along, but... you know, that never came along. XD

Once I was out of the intro programming classes, they switched the intro curriculum from Java to Python. That means lexical scope might actually become relevant to the students before they learn another language... so I'm actually very glad in hindsight.

Eep, I'm not sounding like a history lesson of some sort, so for reference, my college days were 2005-2009. >.>; Pretty recent... but not as recent as I'd like, lol.

---

"But again, you've made me look again at my assumptions and those of the Logo creators, and ask myself again and again about those assumptions; and that's definitely a good thing :)"

Yeah, hopefully you feel justified in whatever you settle on. ^_^ Nothing's without downsides to me, so I don't tend to do value judgments, just a stream of personalized suggestions.

-----

1 point by rocketnia 5040 days ago | link | parent | on: Apologies for the frequent downtime

I think you may already have an inkling somewhere in the back of your mind.... http://arclanguage.org/item?id=15341

-----

1 point by akkartik 5040 days ago | link

Lol.

-----


That didn't show up until 30 seconds had passed, right?

It's easy to find the code where this takes place:

  $ grep "took too long" . -r
  ./lib/srv.arc:                 (prn "srv thread took too long for " ip))
It's an intentional feature of 'handle-request. It waits for 'threadlife* seconds (30) and then gives up. Maybe you need to set 'threadlife* to a larger value for your application... but since 30s for an 86KB upload is pretty ridiculous, maybe there's another issue.

-----

1 point by lark 5059 days ago | link

Makes sense. Here's the issue: how do I save the multipart data of an file upload to a file quickly? Is there something more efficient than this:

  (w/outfile o f
    (whilet c (and (> n 0) (readc req!in))
              (-- n)
              (disp c o)))
This is derived from: http://github.com/nex3/arc/blob/master/lib/webupload.arc

Using a:

              (writec c o)))
produces the same effect.

-----

2 points by rocketnia 5059 days ago | link

I found some cool stuff at (http://docs.racket-lang.org/reference/port-lib.html#(def._((...).

This might work:

  (w/outfile o f
    (let i ($.make-limited-input-port req!in n)
      (after ($.copy-port i o)
        close.i)))
For all I know, closing i may not be necessary:

  (w/outfile o f
    ($.copy-port ($.make-limited-input-port req!in n) o))
Note that this copies n bytes. The code you posted deals with n characters for some reason, even though Arc provides 'readb and 'writeb for dealing with bytes.

-----

1 point by lark 5059 days ago | link

Thanks for the help. I tried with readb instead of readc and received a browser error with the 874996-byte file.

-----

1 point by lark 5059 days ago | link

It seems saving w/outfile saves the file correctly as sent. It's just that Anarki does not return after that.

Also, uploading a 874996-byte file results into only 183045 of those bytes being available in the multipart data that end up being saved, and the browser reports an error:

  The webpage at http://lovelywebsite.com/x?fnid=6bl1DEG5yq
  might be temporarily down or it may have moved permanently
  to a new web address.
Also, when this error uploading a 874996-byte file occurs, the 'srv thread took too long' error message is not displayed at all: the browser error occurs within 1-2 secs.

-----

2 points by rocketnia 5059 days ago | link

That sounds almost like you're coming across a request size limit somewhere in your server (maybe in news.arc or app.arc or srv.arc, maybe in Apache or nginx or whatever)... but probably not since the upload is less than 4MB. All I know is PHP tends to limit uploads to 4MB unless you reconfigure it. :-p

Maybe you're coming across an issue with the fact that bytes are being treated as characters? Do you have another file you could test? (Maybe one you could share with us?)

-----

1 point by lark 5059 days ago | link

I thought there might be a request size limit, since there's an nginx in front of Anarki. But then I tried connecting directly to the Anarki web port and got the same result.

Here's a 822K file which I just confirmed fails to be uploaded:

http://tyche.pu-toyama.ac.jp/~a-urasim/lsvl/data/bzip2_1.0.5...

Can anyone get this to upload with webupload.arc successfully?

-----

1 point by rocketnia 5054 days ago | link

Sorry for leaving you hanging here.... I'm not really in the practice of running news.arc, and when I asked for a file to use for testing it's because I thought maybe someone else would pick up my slack. XD

-----

1 point by rocketnia 5059 days ago | link | parent | on: Save-table in Anarki: is it different?

I just looked in Arc 3.1, in all of Anarki's branches, a few commits back in Anarki's master ac.scm, and I didn't find anything at line 972 that would cause an error like that.

I did notice that some functions that are supposed to expect 1 argument in news.arc currently expect 0, so I fixed that: https://github.com/nex3/arc/commit/f311b3879c15a27518bbe0fad...

I don't know where to start testing news.arc, so please speak up if I've made things even worse.

-----

2 points by rocketnia 5059 days ago | link

Looks like I came in just after akkartik noticed the same thing, and I just fixed some overlooked places. XD

-----

3 points by rocketnia 5064 days ago | link | parent | on: Ask Arc tut: what is the trick in this?

The = macro is set up to special-case certain function names, such as car. The point is to let us treat certain compound expressions as though they were assignable variables:

  arc> (= l '(x))
  (x)
  arc> l
  (x)
  arc> (= (car l) 2)
  2
  arc> (car l)
  2
For a more technical explanation, = is a macro.

Evaluation in Arc has two stages: First, macros expand. Then the expanded code executes, doing function calls and branching and so on.

After one step of macro expansion, (= (car l) 2) looks something like this:

  arc> (macex1 '(= (car l) 2))
  (do (atwith (gs1822 l gs1823 2) ((fn (val) (scar gs1822 val)) gs1823)))
That's a lot of cruft, but what's important is (scar gs1822 val). For almost all purposes, (= (car l) 2) is equivalent to (scar l 2), just easier to read.

The effect of (scar l 2) is that it modifies l by changing its car to 2, and then it returns 2. That is, it implements exactly the behavior we want from (= (car l) 2).

-----

1 point by FredBrach 5064 days ago | link

>> The = macro is set up to special-case certain function names, such as car.

Here we are. :D Yup sure. Sounds just terribly bad.

Same for:

arc> (+ (eval (car '(x))) 2)

7

AND the fact we can't assign a value to x with a derivative of that expression - that expression being good in itself.

But well. I'll continue the tutorial because I've just read half of it - and I just blocked on the hashtables the true purpose of my questions here. Why - I'm looking for a semantic reason, not a syntaxic one - can't we get a hashtable like this (or kind-of of course):

(map = (x y z) '(1, 2, 3.14))

Please note that map should map eh. And there is no reason it don't. At least in your answer - by the way thank you for it, I'll win a lot of time by studying it.

I'm pretty sure we can do absolutely everything with symbols, evaluation (and non-evaluation), calls, lists, bindings and some generic flow controls and some things I'm missing...

After that we can bind a combinaison of those things to a symbol like 'map or 'my-super-hastable-über-easy-to-use and win LOCs, it's not a problem.

If I'm missing something, a true reason that, for example, map can't build a map - that is bind symbols to values and returning the whole in a list the same way we can ________ symbols to values and returning the whole in a list, you gonna be my new best friend :)

To be clear: whether I'm missing a terribly obvious syntax to do this with map or there is a semantic reason I'm missing or it's a problem.

-----

1 point by rocketnia 5064 days ago | link

Since = is a macro, (map = ...) is not going to work.

This might do what you want:

  (listtab:map list '(x y z) '(1 2 3.14))
The result of map is ((x 1) (y 2) (z 3.14)), and listtab converts that to a table.

Arc's (a:b ...) syntax is short for (a (b ...)), in case that's confusing. :)

If you insist on using map and =, here's a way to do it:

  (let tbl (table)
    (map (fn (k v) (= tbl.k v))
      '(x y z)
      '(1 2 3.14))
    tbl)
The tbl.k syntax is short for (tbl k).

You might also be interested to know that (= tbl.k v) expands to (sref tbl v k).

---

"the fact we can't assign a value to x with a derivative of that expression - that expression being good in itself."

Yeah, it would be kinda nice if (= (eval 'x) 2) worked. It could expand to (eval `(assign x (',(fn () 2)))). In fact, the defset utility lets us add that behavior ourselves if we want it.

-----

1 point by FredBrach 5064 days ago | link

>> In fact, the defset utility lets us add that behavior ourselves if we want it.

At this stage, I also can go back to C :)

Do you get what I mean? Arc, if I may, should be a language which is damn hard consistent at least on the basic concepts and which use kind of self generated strategy to reduce LOCs, not hard coded pseudo-concept like most languages.

Ok. I will maybe try to write an implementation by myself with C.

Hey thank you :)

-----

1 point by rocketnia 5060 days ago | link

"Arc, if I may, should be a language which is damn hard consistent at least on the basic concepts and which use kind of self generated strategy to reduce LOCs, not hard coded pseudo-concept like most languages."

You might be surprised to hear this, but many of us here like Arc for exactly this reason. It inherits its simplicity and conistency from Scheme.

However, Arc's main improvement upon Scheme is the fact that it uses some quirky abbreviations. These abbreviations do take the form of "hard coded pseudo-concepts."

I might have been wrong to inform you about these abbreviations when you were just starting to learn Arc. If you can, forget about (= ...) and (a:b ...) and stuff, and then see if you like the language any better. ;)

-----

1 point by FredBrach 5064 days ago | link

>> (let tbl (table) (map (fn (k v) (= tbl.k v)) '(x y z) '(1 2 3.14)) tbl)

Haha. just rewrite = as it should - the binding operation being an atomic operation. = should be

(fn (k v) (bind k v))

EDIT: oh ok it's assign not bind.

-----

1 point by FredBrach 5064 days ago | link

>> (listtab:map list '(x y z) '(1 2 3.14))

Pretty much better I agree.

-----

1 point by FredBrach 5064 days ago | link

By the way it also leads to: may a symbol be unbound (=bound to nothing)? I think not. I think that a symbol should be bound to a function self assignment and then we might do

> (x 1)

x bound to 1

And then, a hastable would be:

> '((x 1) (y 2) (z 3))

blabla

By the way the quote here which is required seems be the beginning of an absolutely beautiful system - the way one bind x, y and z before the retreiving of a value of such a hashtable might be the start of such dynamic evaluations/calls... And of course, one might not bind x, y and z before such an expression.

EDIT: so there should be kind of bound and unbound hashtables. Sounds good. I might be wrong.

Also, =, which do not evaluate the first argument which is pretty ok (other functions we write do the same on arguments), is able to bind something new to x.

Oh and

> (x 1 2)

x bound to '(1 2)

which is a good start for the consistency of the functions.

I'm looking for something like that when I'm working on something like arc.

-----

1 point by FredBrach 5064 days ago | link

a kind of bound hashtable is

> (h (x 1) (y 2) (z 3))

h bound to '(x y z)

if h is unreferenced.

Also, retreive pairs by key with

>(keep-is x h)

(x)

and by value with

>(keep-iso 2 h)

(y)

Something like that.

Now a typic function call is:

>(fn '( myargs ) '( myinstructions ))

and fn does what it should

Oh there is scope misconceptions at this stage, nothing really worrying.

Anyway, Something like that.

-----

1 point by FredBrach 5064 days ago | link

And

> (car '(x))

x

>(= (car '(x)) 2)

2

> x

2

let's runtime error for the moment

-----

2 points by FredBrach 5063 days ago | link

I've finished the tutorial.

For the fun, I gonna make a few try of implementations of such a language. Let's see if I can do a little bit better/atomic/consistent/powerful in the basic concepts.

One of my other concern is to generalize terms. And the spaces idea looks like a pretty good idea: a name space, a symbol/bind space, a type space, an algebric space and a call space .slt. Each term having something in those.

So I can bind things to 3.14 or to "hello world" and so I can call the function bound to 4 with ((+ 2 2)).

I'm not sure it will work at all ie. consistency/useability.

Also, macros are very useful because of the scope flexibility they offer (the only other thing they offer is code text über tweaking): we love use so-called global/context variables and we certainly need more flexibility for that. I think that there is a misconception in the programming world behind the fact that scopes are defined by functions. I think - in fact I'm sure because I've already done it - you can define scopes independently of everything else. And then, you can call a function which will use variables of the call context, arguments becoming true arguments, not context vars. OO tried to solve that with just the bad idea.

I will use {} for scopes.

So let's try with the bind rules I've defined earlier:

(def y '2)

{

(def x '1)

(def MyFn '((pr x) (pr y)))

(MyFn)

{

(x '10) ; let's say its possible in the call space since 1 is not a list

(def y '3.14)

(MyFn)

}

(MyFn)

}

> 1

> 2

> 10

> 3.14

> 10

> 2

Don't see any problem in that except there is no argument in my function. I've not found something I like for the args already but it will come.

Note that: (MyFn '({(def x '"blabla") (pr x) (pr y)}))

is possible to. I don't see any problem in that. The rule is: scope are independant of everything.

We even probably can do that:

(MyFn '({(def x "blabla") (pr x} x)))

An idea: an inner zap for the need of a beautiful

(x 10)

-----

2 points by akkartik 5063 days ago | link

Interesting idea to have functions not create a new scope by default. But it would make it too easy to create dynamically-scoped variables.

  (def foo() {
    (def a 34)
    (bar)})

  (def bar()
    a)
Here a acts dynamically scoped. I think it's very valuable to have a way to say "this variable is lexically scoped", meaning that functions called within the scope can't access it.

-----

1 point by FredBrach 5063 days ago | link

Ok let do that if I've well understood. That's cool :)

>> I think it's very valuable to have a way to say "this variable is lexically scoped", meaning that functions called within the scope can't access it.

Do you mean can't access it in the sense of c++ private lib or kind of can't use it?

In the case of can't use it: Why would a function evaluate a variable which does not exist from its point of view? ie. compilation error

I've found for the inner zap.

{

(def MyFn '( (= MyFn '(3.14)) (1) ) )

(pr (MyFn))

(pr (MyFn))

}

> 1

> 3.14

:)

EDIT: you have edited your text, I need to re-evaluate it. But unfortunately, I've to sleep now :D Let's continue tomorrow :) Thank you, that's pretty interresting :)

-----

1 point by akkartik 5062 days ago | link

Yeah, sorry I got rid of the let from my code example. I thought I should follow your example more closely. Was that the change you noticed?

I think it would be really hard to implement let to have lexical scope. To do so you'd have to delete some bindings from the current scope before each function call. In that case functions modify the scopes going into them, sometimes deleting bindings and sometimes not. Seems confusing.

---

I don't follow your distinction between access and use..

"Why would a function evaluate a variable which does not exist from its point of view?"

Primarily because it assumes some implicit 'global' bindings. Like function names:

  (def foo() {
    (def list '(1 2 3))
    (bar)})

  (def bar()
    (list 1 2)) ; error
Much of the power of lisp derives from having just a few powerful concepts; function names are symbols just like any other and you can shadow their bindings like anything else.

Even aside from functions, codebases tend to have variables that are implicitly accessed without being passed in as arguments. Implicit variables can be either global or in packages. If two subsystems have common names and you make a stray call between them, it can get hard to debug.

---

I don't understand your code example; can you edit it to add indentation? Just put two spaces at the start of every line of code and it'll preserve indentation. (http://arclanguage.org/formatdoc)

-----

1 point by FredBrach 5061 days ago | link

>> Primarily because it assumes some implicit 'global' bindings. Like function names:

Nothing in that which must drive a language design.

>> Implicit variables can be either global or in packages.

This is what I'm trying to improve above.

>> it can get hard to debug.

Debug is about bad programming.

-----

1 point by akkartik 5059 days ago | link

> Debug is about bad programming.

If so I'm a terrible programmer :)

There seem to be two schools of thought around debugging today. The first is to minimize debugging by use of types, like in Java or Haskell. The second is to embrace debugging as an eternal fact of life, and to ease things by making code super lightweight and easy to change.

Both approaches are valid; combining them doesn't seem to work well. The combination of having no safety net at compile time but forcing the programmer to get his program right the very first try -- this seems unrealistic.

PG's style seems to be akin to sketching (http://paulgraham.com/hp.html; search for 'For a long time'). That implicitly assumes you're always making mistakes and constantly redoing code. My version of that is to add unit tests. That way I ensure I'm always making new mistakes.

-----

1 point by rocketnia 5059 days ago | link

I'd say both approaches you're talking about are all about failing fast, and that unit tests are a way to shove errors up to compile time manually, by running some arbitrary code after each compile. Languages that let the programmer run certain kinds of code at compile time anyway (like a type system or a macroexpander) have other options for where to shove these errors, though they may not always make sense there.

Conversely, they may not make sense in unit tests: If we want to know that a program behaves a certain way for all inputs, that might be easy to check with a static analysis but difficult (or effectively impossible) to check using example code.

---

"The combination of having no safety net at compile time but forcing the programmer to get his program right the very first try -- this seems unrealistic."

I'd say Arc is a demonstration of this option. XD I thought the whole point of Arc being for sufficiently smart programmers was that no guard rails would be erected to save programmers from their own buggy programs.

---

Anyway, if a language designer is trying to make a language that's easy to debug, static types and unit tests are hardly the only options. Here's a more exhaustive selection:

- Reject obviously buggy programs as being semantically meaningless. This could be any kind of error discovered by semantic analysis, including parse errors and type errors.

- Give the programmer tools to view the complexity of the program in intermediate stages as it simplifies. Step debuggers do this for imperative languages. Other languages may have bigger challenges thanks to staging (like macroexpansion) or notions of "effect" that feature simultaneous, time-sensitive, or tentative behavior, for instance.

- Create rich visualizations of the program's potential behavior. We discussed Bret Victor's demonstrations of this recently (though I didn't participate, lol): http://arclanguage.org/item?id=15966

- Collapse the edit-debug cycle so that diagnostic information is continuously visible as the programmer works. Again, this is something Bret Victor champions with a very visual approach. IDEs also provide this kind of information in the form of highlighting compile time errors.

- Give the running program extra functionality that exposes details of the codebase that would normally be hidden. If a program runs with a REPL or step debugger attached, this can be easy. (Also, a programmer can easily pursue this option in lots of languages by manually inserting these interaction points, whether they're as simple as printing to the console or as complicated as a live level editor.)

- Provide tools that write satisfactory code on the programmer's behalf. IDEs do this interactively, especially in languages where sophisticated static analysis can be performed. Compilers do this to whole programs.

- Provide abstraction mechanisms for the programmer to use, so that a single bug doesn't have to be repeated throughout the codebase.

- Provide the programmer with an obvious way to write their own sophisticated debugging tools. A static analysis library might help here, for instance. An extensible static analysis framework, such as a type system, can also help.

- Provide the programmer with an obvious way to write and run unit tests.

- Simply encourage the programmer to hang in there.

-----

1 point by akkartik 5059 days ago | link

"I'd say Arc is a demonstration of this option."

You don't hear people say of Arc, "it worked the first time I wrote it." That's more Haskell's claim to fame.

The dichotomy I'm drawing isn't (in this case) about how much you empower the user but how you view debugging as an activity. I claim that Haskellers would like you to reason through the correctness of a program before it ever runs. They consider debugging to be waste. I consider it to be an essential part of the workflow.

The points on the state space that you enumerate are totally valid; I was just thinking at a coarser granularity. All your options with the word 'debug' (at least) belong in my second category.

Perhaps what's confusing is the word 'debugging' with all its negative connotations. I should say instead, relying on watching the program run while you build vs relying just on abstract pre-runtime properties. It's the old philosophical dichotomy of finding truth by reason vs the senses.

-----

1 point by FredBrach 5062 days ago | link

By fixing some mistakes I've made, I can go forward.

I think I'm able to eliminate the def and have a working evaluation/call system.

Let's say, we can have symbols and lists of symbols only. Symbols can be bound to another symbol or list.

For number and integer, the arithmetic functions work on the symbols as if they were number or integer. I don't see any problem in that, ie. lambda calculus.

Also, let the previous scope system.

Evaluation. An evaluation of a symbol gives its bound symbol or list. If one evaluates a list, it's a call.

And now, the calls. We can call everything. A call on a symbol bind the symbol to the following argument or to a list of the following arguments. If the symbol hasn't been called before in the current scope, it is defining a new symbol on the scope. And if one call a list, it's a function call.

So the previous code looks like this now:

('y '2)

{

('x '1)

('MyFn '((pr x) (pr y)))

(MyFn)

{

('x '10) ;no problem in this anymore

('y '3.14)

(MyFn)

}

(MyFn)

}

> 1

> 2

> 10

> 3.14

> 10

> 2

What we can see now is that, everything ends up with a '.

That's why I would like to explore the opposite strategy, an ' in front of what I want to evaluate.

It gives:

(y 2)

{

(x 1)

('pr x)

(MyFn (('pr 'x) ('pr 'y)))

('MyFn)

{

(x 10)

(y 3.14)

('MyFn)

}

('MyFn)

}

> x

> 1

> 2

> 10

> 3.14

> 10

> 2

I would like to put a star (like in C) instead of a ' for evaluation but I didn't succeeded.

-----

2 points by rocketnia 5061 days ago | link

"And if one call a list, it's a function call."

That's a lot like PicoLisp. In PicoLisp, functions are just lists:

  : (de foo (X Y)            # Define the function 'foo'
     (* (+ X Y) (+ X Y)) )
  -> foo
  : (foo 2 3)                # Call the function 'foo'
  -> 25
  : foo                      # Get the VAL of the symbol 'foo'
  -> ((X Y) (* (+ X Y) (+ X Y)))
(Example taken from http://software-lab.de/doc/ref.html.)

Unfortunately, this approach means not having lexical scope. If any function has a parameter named * or + and it calls foo, foo's behavior might be surprising. Worse, you can't use lambdas to encapsulate state! (Or other context...)

With dynamic scope, you might as well define every function at the top level; local function syntax is only useful for code organization.

In some cases, dynamic scope can be useful for certain variables (typically configuration variables), but it's actually very easy to simulate dynamic scope in a non-concurrent program; just change a global variable and reset it afterwards.

---

"I would like to put a star (like in C) instead of a ' for evaluation but I didn't succeeded."

That's because asterisks ( * ) create italics on this forum. http://arclanguage.org/formatdoc

-----

2 points by FredBrach 5061 days ago | link

>> That's a lot like PicoLisp. In PicoLisp, functions are just lists:

Functions are lists of instructions/operations you can call and re-call. In every languages of the world. - meaning there is no reason they are treaten in a special case or with a special type.

The true concept is the call.

>> Unfortunately, this approach means not having lexical scope. If any function has a parameter named or + and it calls foo, foo's behavior might be surprising.

That's about bad programming. Just know what you're doing.

>> Worse, you can't use lambdas to encapsulate state! (Or other context...)

I gonna look at those lambdas. Thx

>> With dynamic scope, you might as well define every function at the top level; local function syntax is only useful for code organization.

That's not a question of code organization. That is a question of sense. If you define your functions at the top level because you can do it, you'll need a debugguer and a default scope system based on lexical scope. Beleive me :)

So with dynamic scope, you just have a functions system which plays its role: the possibility to repeat code. And a macro system which plays its role: the possibility to - over - tweak the source text in a way which has nothing to do with programming in itself. Functions and macros should be orthogonal concepts. That's the meaning of a concept: something which is orthogonal to every other concepts in the system.

>> In some cases, dynamic scope can be useful for certain variables (typically configuration variables), but it's actually very easy to simulate dynamic scope in a non-concurrent program; just change a global variable and reset it afterwards.

The fact that Object Oriented programming exists tell you re wrong here.

>> That's because asterisks ( ) create italics on this forum*

Cool, thanx for the link :)

-----

1 point by rocketnia 5060 days ago | link

"Functions are lists of instructions/operations you can call and re-call. In every languages of the world."

I'm going to nitpick your use of "list" there. There's no reason effects need to be short actions in sequence. We might want to apply effects continuously, in parallel, in combination, in reverse, under supervised control, or distributed on machines under multiple people's (sometimes untrustworthy) administration. I doubt there's one definition of "effect" that will satisfy everyone, but that doesn't mean we should settle for the same old imperative effects in every single language. :)

I'm also going to nitpick the premise that a function is something "you can call and re-call." It can be useful to write a function that you only call once... if you intend to define it more than once. And sometimes it's useful to enforce that a function that can only be invoked once, perhaps for security; languages can potentially help us express properties like that.

---

"That's about bad programming. Just know what you're doing."

If I write a library with (de foo (X Y) (* (+ X Y) (+ X Y))) in it, would you say I should document the fact that it isn't compatible with programs that use + and * as local variables? Fair enough.

However, suppose we were given a language that had foo in it, and it behaved strangely whenever we used + or * as a local variable. Outrageous! That's a "hard coded pseudo-concept"! :-p We should ditch that language and build a new one with more consistent and orthogonal principles.

Alas, that language is exactly what we're using as soon as we define (de foo (X Y) (* (+ X Y) (+ X Y))).

---

"If you define your functions at the top level because you can do it, you'll need a debugguer and a default scope system based on lexical scope."

No need for a debugger if you're a good programmer, right? :-p And I'm not sure what you mean by needing lexical scope, since we're assuming that we've given up lexical scope already.

But I forgot, one downside to defining functions at the top level is that you need to give them names (global names). Maybe this is related to what you mean.

---

"Functions and macros should be orthogonal concepts."

Who's saying otherwise?

---

"The fact that Object Oriented programming exists tell you re wrong here."

The fact that OO exists is irrelevant. My point is that Arc's global scope is enough to achieve dynamic scope in a non-concurrent program. Who cares what other languages do?

(Incidentally, most popular OO languages also have global scope--static fields--which allows exactly the same kind of dynamic scope technique.)

-----

2 points by akkartik 5061 days ago | link

I'm a little lost :) Are you in favor of lexical scope or against it?

The argument that lexical scopes are entangled with our notion of functions, so let's drop them since they're not an orthogonal concept -- that seems internally consistent and worth thinking about.

-----

1 point by FredBrach 5060 days ago | link

Oh sorry if I've not been clear: I'm in favor of dynamic scope :)

>> The argument that lexical scopes are entangled with our notion of functions, so let's drop them since they're not an orthogonal concept

Exactly. In the fun exploration of an ultimate language for good programming, name conflicts should not drive the language design at all. Programmers should manage their name spaces with care. Also, having a tool for this, like namespaces, is not a problem. Seems even pretty good and it fixes everything.

-----

2 points by akkartik 5060 days ago | link

"Programmers should manage their namespaces with care."

Totally. I think I'm closer to your point of view than anybody here (http://arclanguage.org/item?id=15587, footnote 1; http://arclanguage.org/item?id=12777). I've gradually moved to the dark side in several ways: I no longer care about hygiene[1] or making macros easy to compile[2]. But I still hold on to lexical scope for reasons I can't fully articulate. If unit tests are as great as I say they are, do we need lexical scope? Without them changes may break seemingly distant, unrelated code. Something to think about.

[1] http://arclanguage.org/item?id=15907, especially the http://arclanguage.org/item?id=15913 subtree.

[2] http://arclanguage.org/item?id=13585; http://www.arclanguage.org/item?id=14947; http://www.arclanguage.org/item?id=13319.

-----

1 point by FredBrach 5060 days ago | link

>> If unit tests are as great as I say they are, do we need lexical scope?

Very very interesting. Unit testing.. This is such an engineering concept. Why not built in the language with meta tags (I don't know if it's possible at all)?

>> Without them changes may break seemingly distant, unrelated code. Something to think about.

Let's try the fun of an extreme code expansion language without any compromise :)

-----

1 point by FredBrach 5060 days ago | link

>> and it fixes everything.

Oh no I'm completely wrong here.

Some of you were right, there is a sensible problem in names/scope I've not expected. But I've the answer to everything :)

Libraries.

What are libraries? There are application foundations. In other words, applications are built on top of libraries.

So let's make it as it should.

A libraries is a function which takes in arguments an other libraries or an end application.

Let loadlast be a function which bind to a symbol the eval of the last instruction of a file. And let use the arc evaluation syntax.

App.ext:

  ////////////// app.ext ////////////////////

  (loadlast '"lib1.ext" 'MyLib1)

  (loadlast '"lib2.ext" 'MyLib2)

  (loadlast '"lib3.ext" 'MyLib3)

  (= 'MyApp
     '(*put your application here*))

  (MyLib1 '(MyLib2 '(Mylib3 MyApp))) ; This launches the whole

  MyApp ; that makes MyApp a lib. MyApp is working with MyLib1, MyLib2 and MyLib3 and thus must be embed at least on top of a stack which contains them.
Lib1.ext:

  ///////////// lib1.ext ////////////////////

  {

  *blabla*

  {

  arg1; it evaluates (MyLib2 '(Mylib3 MyApp))  which can now  use lib1 via the dynamic scope system

  }

  *blabla*

  }
I'm not saying this is strictly innovative.

-----

2 points by FredBrach 5060 days ago | link

I'm definitly going to write an implementation of my exploration :)

I've decided to call the resulting language Prompt: http://promptlang.org

-----


  (+ (car (car '(x))) 2)
You're trying to access the first element of the first element of '(x). You can't access the first element of a symbol....

---

  (+ car( (eval (car '(x)))) 2)
You're trying to add three things together here:

- The car function

- ((eval (car '(x)))), which is a roundabout way of saying (x) here

- 2

---

  car x
Here you've entered two commands at once: One is "car", which evaluates to the car function, and the other is "x", which evaluates to 5. They're both printed to the console, but "arc>" is printed before each command is read, so the output looks quirky.

-----

2 points by Pauan 5059 days ago | link

"They're both printed to the console, but "arc>" is printed before each command is read, so the output looks quirky."

Not to be a bother, but Arc/Nu and Lite-Nu don't have this problem:

  > car x
  #<fn:car>
  5
I still highly recommend either of them to anybody who wishes for a simple, working, no-nonsense implementation of Arc 3.1.

-----

1 point by FredBrach 5064 days ago | link

oh well, the

car x

instead of

(car x)

was my mistake and why I've asked the question.

-----

2 points by rocketnia 5064 days ago | link | parent | on: Ask Arc tut: what is the trick in this?

"Also, please just copy-paste the session from your terminal in future."

To do this in a Windows terminal, right-click anywhere in the text and select "Mark". (You can also select "Mark" by clicking on the top-left icon and going to the "Edit" submenu.) Now you can draw a rectangle with your mouse, or with the arrow keys and shift.

Once you're satisfied with your rectangle, right-click in the window again or press enter. The contents of the rectangle are now in your clipboard, not including trailing whitespace.

You can cancel by doing something else, like pressing escape.

The rectangle interface might seem a little insane, but I'm half-looking-forward to the time when it comes in handy for copying tabular data or ASCII art. Or Epigram programs. :-p

-----

3 points by rocketnia 5065 days ago | link | parent | on: Pythonect = Python + Shell pipes

Seems like this is the intro for people who would like to see how Pythonect code looks: http://groups.google.com/group/pythonect/browse_thread/threa...

-----

More