Using the Python Interpreter as a Reference

Travis Parks jehugaleahsa at gmail.com
Sun Nov 27 17:21:01 EST 2011


On Nov 26, 1:53 pm, Rick Johnson <rantingrickjohn... at gmail.com> wrote:
> On Nov 20, 6:46 pm, Travis Parks <jehugalea... at gmail.com> wrote:
>
> > Hello:
>
> > I am currently working on designing a new programming language. It is
> > a compiled language, but I still want to use Python as a reference.
> > Python has a lot of similarities to my language, such as indentation
> > for code blocks,
>
> I hope you meant to say "*forced* indention for code blocks"! "Forced"
> being the key word here. What about tabs over spaces, have you decided
> the worth of one over the other or are you going to repeat Guido's
> folly?
>
> And please, i love Python, but the language is a bit asymmetrical. Do
> try to bring some symmetry to this new language. You can learn a lot
> from GvR's triumphs, however, you can learn even more from his follys.

Personally, I find a lot of good things in Python. I thinking tabs are
out-of-date. Even the MAKE community wishes that the need for tabs
would go away and many implementations have done just that. I have
been seriously debating about whether to force a specific number of
spaces, such as the classic 4, but I am not sure yet. Some times, 2 or
even 8 spaces is appropriate (although I'm not sure when).

I have always found the standard library for Python to be disjoint.
That can be really beneficial where it keeps the learning curve down
and the size of the standard modules down. At the same time, it means
re-learning whenever you use a new module.

My language combines generators and collection initializers, instead
of creating a whole new syntax for comprehensions.

[| for i in 0..10: for j in 0.10: yield return i * j |]

Lambdas and functions are the same thing in my language, so no need
for a special keyword. I also distinguish between initialization and
assignment via the let keyword. Also, non-locals do not need to be
defined explicitly, since the scoping rules in Unit are far more
"anal".

In reality though, it takes a certain level of arrogance to assume
that any language will turn out without bumps. It is like I was told
in college long ago, "Only the smallest programs are bug free." I
think the same thing could be said for a language. The only language
without flaws would be so small that it would be useless.

I love these types of discussions though, because it helps me to be
aware. When designing a language, it is extremely helpful to hear what
language features have led to problems. For instance, C#'s foreach
loops internally reuse a variable, which translates to something like
this:

using (IEnumerator<T> enumerator = enumerable.GetEnumerator())
{
    T current;
    while (enumerator.MoveNext())
    {
        current = enumerator.Current;
        // inner loop code goes here
    }
}

Since the same variable is reused, threads referencing the loop
variable work against whatever value is currently in the variable,
rather than the value when the thread was created. Most of the time,
this means every thread works against the same value, which isn't the
expected outcome. Moving the variable inside the loop _may_ help, but
it would probably be optimized back out of the loop by the compiler.
With the growth of threaded applications, these types of stack-based
optimizations may come to an end. That is why it is important for a
next-gen language to have a smarter stack - one that is context
sensitive. In Unit, the stack grows and shrinks like a dynamic array,
at each scope, rather than at the beginning and end of each function.
Sure, there's a slight cost in performance, but a boost in
consistency. If a programmer really wants the performance, they can
move the variable out of the loop themselves.

In fact, there are a lot of features in Unit that will come with
overhead, such as default arguments, non-locals, function-objects,
etc. However, the game plan is to avoid the overhead if it isn't used.
Some things, such as exception handling, will be hard to provide
without overhead. My belief is that, provided a tool, most developers
will use it and accept the slight runtime overhead.

I think everyone has an idea about what would make for the perfect
language. I am always willing to entertain ideas. I have pulled from
many sources: C#, Java, Python, JavaScript, F#, Lisp and more. The
hope is to provide as much expression with as much consistency as
possible. Just the other day I spent 2 hours trying to determine how
to create a null pointer (yeah, it took that long).

let pi = null as shared * Integer32 # null is always a pointer

Originally, I wanted 'as' to be a safe conversion. However, I decided
to make use of the 'try' keyword to mean a safe conversion.

let nd = try base as shared * Derived
let d = if nd.Succeeded: nd.Value else: null
# or, shorthand
let i = try Integer32.Parse("123") else 0

Of course, the last line could cost performance wise. For that reason,
Unit will allow for "try" versions of methods.

let Parse = public static method (value: String)
throws(FormatException UnderflowException OverflowException)
returns(Integer32): ...
and Parse = public static try method (value: String)
returns(TryResult<Integer32>): ...

Of course, such methods are required to never throw and must return a
specific named tuple type.

Type, type, type. In short, I can only hope my language is useful and
that I dodge as many inconsistencies as possible. The good thing about
an unknown language is that no one gets mad when things change. In
that sense, Python's annoyances are probably an indication of its
quality. :-)



More information about the Python-list mailing list