Saturday, March 01, 2014
The Story Continues
http://cheersgames.com/swym/wiki
Tuesday, June 05, 2007
More Flan iteration
#print(each 1..10);
As requested, this produces 12345678910.
Let's examine it piece by piece.
The .. operator produces a type. (Earlier I said it produced a list fragment; change of plan. It's less comon to need a list fragment, and you can quite trivially generate one from the type.)
The type produced by a..b is "any integer from a to b, inclusive". In case you're wondering, there's also a range of companion operators such as a<..b: "integers from a+1 to b", and a..<b: "integers from a to b-1". The latter is probably useful more often than the straightforward .. operator.
As we saw last time, the "each" keyword can be used to iterate over a list; however, it can also iterate over an enumerable type. (In fact the list iteration system is based on this.)
An enumerable type is one that's finite, and has some kind of order to it. For example:
@SingleDigitNumber = 0..9;
@Vowel = 'a'¦'e'¦'i'¦'o'¦'u';
@ListItem = thelist.Element;
@ListIndex = thelist.Index;
@ThreeVowelString = [Vowel, Vowel, Vowel];
This can be very useful. It's a convenient way of declaring "the solution must look something like this", and then searching for a valid solution among the things that look like that.
For example, in a recent discussion on Reddit, I gave the following (slightly reformatted, but otherwise the same) Flan solution to this knapsack problem:
@prices = [215, 275, 335, 355, 420, 580];
@Solution = [0..7:6]; // a Solution is a list of 6 numbers between 0 and 7
int Solution.@totalPrice
{
return sum(# this[each @i] * prices[i] );
}
@result = #first Solution:totalPrice.is 1505;
Let's step through this to make sure it's clear -
prices is just a list of the 6 appetizer prices. Nothing much to say here.
Solution is our enumerable type. It's a list of 6 integers, representing the number of times to order each of the 6 appetizers.
Solution.totalPrice declares a method belonging to the type Solution, to represent the total price of the order. I haven't shown a method declaration before, but it's more or less the same as a C++ one - including the use of this to refer to the Solution it's acting on. The price is calculated using an iteration, much like the dot_ab example I showed last time.
There's only one really new feature here, and that's the construct
#first Solution:totalPrice.is 1505
This iterates through all the possible Solutions, looking for the first one whose totalPrice is 1505.
(I've used the iteration controller first, so that it stops after the first solution is found. To generate a list of all solutions I could have used each.)
The colon operator is filtering the iteration, similar to the ~ operator I introduced last time. In fact, I could equally well have written -
@prices = [215, 275, 335, 355, 420, 580];
@Solution = [0..7:6]; // a Solution is a list of 6 numbers between 0 and 7
is Solution.@CorrectPrice
{
return sum(# this[each @i] * prices[i] ).is 1505;
}
@result = #first Solution ~CorrectPrice;
More to come soon.
Wednesday, May 30, 2007
Iterators in Flan
#println( greetings[each] );
Even if you haven't seen the syntax before (and you haven't, because I made it up), I think this should be fairly self-explanatory. It takes a list of strings called "greetings", and prints each one (with a newline at the end, thanks to println). So if you start with a declaration such as...
@greetings = [ "Hello world!", "Goodbye world!", "Don't forget to write, world!" ];
...then the result is:
Hello world!
Goodbye world!
Don't forget to write, world!
Semantically, this has the same effect as rearranging the line into a C#-style foreach loop.
foreach( string g in greetings )
{
println(g);
}
(The above is pseudo-C#, not real Flan code).
Ok then, one question remains - what's with the # at the beginning of the line?
#println( greetings[each] );
Well, it's called the multiple execution operator, and all uses of [each] must be contained in one: otherwise the compiler will complain.
This operator does two jobs: firstly, it serves as a visual warning to people reading the code, "careful, this line is not as simple as it looks". (That's why it's such a big black character.)
Secondly, it's a merge point, letting you move code out of the foreach expression. Code that's syntactically "outside" the # will be executed only once, taking the list of values as a whole instead of each individual value. Hard to describe, easy to show by example:
@vec = [44, 62, 10];
@veclength = sqrt( sum( #square(vec[each]) ));
This is calculating the length of the vector vec. The important bit is the expression #square(vec[each]), which evaluates to a new list: [square(44), square(62), square(10)]. The sum function then takes the sum of those items, and so on.
Useful enough yet? We're just getting started. You can also filter the list this way, with the ~ operator. Read it as "that is".
For example, to print the integers in a list:
#println( thelist[each] ~int );
And as you may recall a Flan type can be, in fact, any value or predicate:
#println( thelist[each] ~even );
#println( thelist[each] ~prime );
#println( thelist[each] ~1 ));
#println( thelist[each] ~ >0 ) );
#println( thelist[each] ~divisibleby(7) );
#println( thelist[each] ~[3] ) );
#println( thelist[each] ~[char*, 'hello', char*] );
You can also filter by index, by putting a type inside the brackets instead of outside. For example, to select alternate items in the list:
#println( thelist[each ~even] );
Or the first 10 items:
#println( thelist[each ~ <10] );
Flan's declare-anywhere syntax also comes in handy here. You can name the index, and refer back to it later. (For obvious reasons, identifiers declared this way will go out of scope when the loop ends.)
For example, search for items in the list that are greater than their index.
#println( thelist[each @i] ~ >i );
Or, borrowing an example from my last post, you can iterate over two lists at the same time.
@dot_ab = sum( #a[each @i] * b[i] );
(In case you're wondering, the # operator has the lowest precedence of any operator.)
In much the same way as the indices, you can name the values. For example, in the vector length example from earlier, instead of calling square, you could write:
@veclength = sqrt( sum( #thelist[each] @v * v ));
And finally, it sometimes happens that you're not interested in the whole list - 'each' is not the behaviour you want. If you just want to find one item, say 'find' instead:
@x = #thelist[find] ~prime;
(NB: rather than evaluating to a list of matches, a # expression controlled by find will return a single value. If the list contains no prime numbers, it will return null.)
Another type of iteration control is every. This is a logical AND applied to every member of the list, shortcutting at the first false result. This is mostly useful in the context of an 'if' statement. For example, to test whether every entry in the list is positive:
if( #thelist[every].is(>0) ) { ... }
And of course where you have an AND, you sometimes need OR...
if( #thelist[some].is(>0) ) { ... }
That's all for now. Hope this has been interesting.
Oh, and since nobody will take a language design seriously if there's no compiler that runs it, I'm working on changing my interpreter into a compiler that will generate CIL, via the handy Cecil library. This seems like the ideal compilation target for any nascent language - debuggable, (slightly) portable, and with a load of predefined libraries. I'll let you know how the conversion goes.
Thursday, April 19, 2007
Zip means zip
sum $ zipWith(*) a b
(It's calculating the dot product of vectors a and b. In other words, a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + ...)
In short - I hate it.
I'm not talking about any intellectual or pragmatic objection - it just hits me with a gut-level "yuck", like a bowl of maggots. So what exactly is my problem with it? Here's my attempt to understand my reaction and explain it better.
Really, the only part I object to is the zipWith function. It's simply poor communication. A program's job, or a large chunk of it, is to explain your intent to other programmers, not to the compiler. It can be helpful to name an abstract concept like this, but it's important to keep the target in sight: you're doing this to make the program more readable. A bad abstraction is just an indirection: it makes the program less clear, not more.
How would you describe the dot product, in words? Perhaps something like...
"Multiply each number in A by the corresponding number in B, and total the results."
Does "zip" appear in there at all? Would anyone use that word to describe what the dot product is doing? "It zips two lists with a multiplication"?
Moreover, the visual metaphor of a "zip" is inappropriate. The teeth of a zip don't meet head on - they interlock, with each tooth held by the two teeth opposite. So the zipWith(*) operation ought to be multiplying each item by the two opposite:
a[0]*b[0] + a[0]*b[1] + a[1]*b[1] + a[1]*b[2] + a[2]*b[2] + ...
All right then, let's make this criticism more constructive. What should this say?
Let's look at what zipWith is actually doing. zipWith(*) evaluates to a function: one that multiplies the matching elements in two lists together. In other words, by applying zipWith we've taken a function (*) that multiplies numbers, and elevated it into one that multiplies lists together. List multiplication.
But we can't call it list(*). So how about this?
sum $ listwise(*) a b
Ok, it's not ideal; there are loads of different ways one could elevate a function to work "listwise", so the ignorant reader might wonder, what exactly is the above doing? Perhaps it's...
Multiplying a single value with a list?
a*b[0] + a*b[1] + a*b[2] + a*b[3] + ...
Multiplying each item in list a with each in list b?
a[0]*b[0] + a[0]*b[1] + a[0]*b[2] + ... a[1]*b[0] + a[1]*b[1] + ...
Or even something esoteric, like multiplying each item with the two opposite? :)
a[0]*b[0] + a[0]*b[1] + a[1]*b[1] + a[1]*b[2] + a[2]*b[2] + ...
There are plenty of other possible patterns, too. Any of these could justifiably be called "listwise multiplication" - the name isn't really giving the reader enough information.
So, we're looking for a more precise name. One that says "listwise, applying to pairs of elements with the same index". pairwise(*) is just as vague (when does multiplication not apply to a pair of numbers?); zipping(*) could work but has the same bad metaphor as before.
What other metaphors could work? Teeth clenching... fingertips touching... passengers moving from one train to another... aha! How about two ships shooting cannonballs at each other?
It's got the right metaphor (each cannonball hits the corresponding part of the opposite ship), and a snappy name that works as an adjective, and makes sense even if you don't make the mental leap to ships.
Ladies and gentlemen, I give you - "broadside".
sum $ broadside(*) a b
This is a function, also known as zipwith, which elevates a function into its "broadside form" - in this case, broadside multiplication. Multiplying two lists together along their broadest sides.
Like it? Hate it? I'd be interested to know what you think.
Thursday, March 16, 2006
Regular expressionescence
One of the key features of Flan is its type system. You can have type-checking applied statically or dynamically, and you can make types more-or-less as restrictive or as loose as you want. In the end, it turns out that you can use this one system to do Prolog or Haskell-style data deconstruction, SQL-style filtering and pattern-matching, Eiffel-style compile-time contracts, and a really readable version of regular expressions.
Although such flexible type systems seem to be in vogue lately, I've actually been working out the details of this system for several years now.
Let's start with a demo of Flan's static type declarations:
int @a = 0; // declare an integer variable a, which is initially 0.
To quickly summarize: the basic syntax is C. Comments are marked with // and /**/, assignment with =, statements delimited with ;. (Because as far as I'm concerned, if it ain't broke, don't fix it.)
As I've mentioned before, @foo means "create a new variable called foo". If you're used to Perl or Ruby it takes a little time to adjust to declaring variables with @foo and then using them with just foo - but I'm finding that the more I use this syntax, the more I like it. It turns out that it even helps people learn the language. You know when you look at a piece of code in an unfamiliar language, and you're not sure which identifiers are being declared, and which have been declared elsewhere? That simply doesn't happen in Flan.
Some more examples:
char @b = 'B'; // b can be any character - initially, 'B'. (The audience gasps!)
value @c; // c can be any value - character, number, list, whatever.
4 @d; // d is always the number 4.
'E' @e; // e is always the character 'E'.
"ffFF" @f; // f is always the string "ffFF".
Notice that compile-time constants don't need special treatment in this system: they're just variables with a very constrained type.
Flan supports lists as first-class values, denoted Python-style with square brackets and commas. (Yeah, that's different from what I said last time; bear with me.)
[4,3,2,1] @g; // the constant list [4,3,2,1].
[int,int] @h = [2,7]; // any list of exactly two integers; initially [2,7].
[int,1,int] @i = [200,1,-200]; // any list of 3 integers with 1 in the middle.
If you're making a long list, this gets annoying - you don't want to have to write [int,int,int,int,int...], so you can instead use :, the "repeat" operator.
[int:7] @j = [0:7];
...is the same as...
[int,int,int,int,int,int,int] @j = [0,0,0,0,0,0,0];
This is a very flexible system. For example, you can mix and match commas and colons...
[1:4, 2, 3:2, 4] @k; // the list [1,1,1,1,2,3,3,4]
['aci', 'e':8, 'd'] @m; // the string "acieeeeeeeed".
Aside: I hope you found that last example a bit startling. We're using the comma
operator to build a string out of three sequences of characters - 'aci', 'e':8 and 'd'. The last one is just a single character; the other two are structures known as "list fragments". They're almost, but not quite lists.
Some more examples of list fragments and the operators that generate them...
4,5
'h','e','l','l','o'
1:6 //equivalent to 1,1,1,1,1,1.
'hello' //equivalent to 'h','e','l','l','o'.
1..5 //the "range" operator: equivalent to 1,2,3,4,5.
'1'..'5' //equivalent to '12345'.
/[4,5] //equivalent to 4,5 - the / operator is
the inverse of [].
Some of these look a bit like tuples in Python, but actually they're rather different: for a start, list fragments cannot contain other list fragments, because the only way to merge them is concatenation. The only thing you can make is one big list fragment, not a hierarchy.
List fragments can be stored in a variable...
@n = 1,2,3,4;
...concatenated with the comma operator...
44,n,99,n,44 // equivalent to 44,1,2,3,4,99,1,2,3,4,44;
...and if you put a list fragment in square brackets, you get a full-fledged grownup list that you can index and do useful things with.
[n] // equivalent to the list [1,2,3,4].Yep, this is the same "every value is a single-element list" philosophy that I talked about in the last post. Unfortunately, since then I've realised that people will find it confusing that both [1,2,3] and 1,2,3 are legal (but subtly different) syntax for lists, and it'll be a pain having to convert between the two when something is supplied in the wrong form, and so on. I had to pick one as the "default" syntax: [1,2,3] won. Now a list fragment is a list that's been deliberately disabled: to index it you have to put square brackets around it.
Ok, where was I...?
Oh yeah.
For even more flexibility, the colon operator can be followed by an arbitrary type instead of a number:
[char:int] @o = []; // a list containing any number of characters.
[char*] @p = []; // the same thing. (but more convenient.)
That * operator is just one of the operators that let you construct complex types...
4¦5 @q = 4; // can be either 4 or 5 - initially 4.
>0 @r = 1; // any number greater than 0.
>=0 @s = 0; // any number greater than or equal to 0.
[int:>=1] @t = [0]; // a list of one or more integers.
[int+] @u = [0]; // same thing.
!null @v = 45; // any value except 'null'.
You can also intersect types to make stricter types - just write them in series one after another.
!0 int @w = 4; // a non-zero integer.
>-100 <100 !0 int @x = 4; // a non-zero integer between -100 and 100.
And finally, all of these types are first class values: you can store them in variables, return them from functions, and so on. Flan's equivalent of a C typedef expression is just a normal assignment:
@natural = int >=0; // any natural number.
@vowel = 'a'¦'e'¦'i'¦'o'¦'u'¦'A'¦'E'¦'I'¦'O'¦'U'; // any vowel.
natural @y = 4; // a variable containing any natural number.
vowel @z = 'o'; // a variable containing any vowel.
Wow, that was a lot of code snippets. If it's a lot to digest, don't worry about it too much; for now let's move on to a discussion.
Practicalities:
The first question that probably comes to mind is - "Hey, didn't you call these static type declarations? How can you check a type like '>0' at compile time?"Well, there are several ways.
1) Simple case: for a literal value or constant, e.g. "7", the value is known at compile time and can obviously be checked.
2) Marginally less simple: If another variable is declared as the same type (or a stricter one), its contents are always guaranteed to be acceptable, so you can assign one to the other. And the same goes for function calls with an appropriate return type.
3) Explicit type cast: If you're certain that a variable will satisfy a given type restriction, the ".assert" method allows you to force it to be treated as that type. Here's an example of this used in a function declaration...
natural @abs(int @i)
{
..if ( i >= 0 )
....return i.assert(natural);
..else
....return (-i).assert(natural);
}
Yep, this is the equivalent of a static typecast in C. As Bjarne Stroustrup has said, the syntax (type)value for casts in C was a rather unfortunate choice. Slightly dodgy operations like this should not be quite so hard to search for and hard to see. Unfortunately, I feel C++ overreacted with static-cast<type>
As the name implies, the program will halt (in debug builds) if a type can't cope with the value it's given; another feature lacking from C's casts.
4) Explicit type check: If you don't know whether a value satisfies a type, test it at runtime. For example:
natural @abs(int @i)
{
..if ( i.is(natural) )
....return i.assert(natural);
..else
....return (-i).assert(natural);
}
Every value supports the ".is" method - it's essentially the same as the "is" operator in C#. It takes a type as a parameter, and returns true or false depending on whether that type will accept the value.
Unfortunately, as seen above, once you've determined that a value is of a given type, you usually want it to be treated as a value of that type... but in a statically-typed language, you'll have to explicitly cast it. This is a pain in the arse.To simplify cases like this, Flan lets you declare a new variable inside your pattern-match expression:
natural @abs(int @i)
{
..if ( i.is(natural @n) )
....return n;
..else
....return (-i).assert(natural);
}
Even cleaner, if you're doing this with a local variable, a "where" statement can be used to temporarily narrow its type:
natural @abs(int @i)
{
..where i.is(natural)
....return i;
..else
....return (-i).assert(natural);
}
In effect, within the body of the where statement, the type of the variable 'i' becomes (natural int).
Frankly I'm not completely happy with the syntax of the "where" statement, but I'm definitely happy with how useful it is. As I develop the language further, I'm sure more conversion expressions will continue to suggest themselves.
Regular expressionescence:
We've now reached the point where I can demonstrate Flan's regular expression-like structures, as I mentioned earlier.
@teststring = "hello";
The expressions "foo", ['foo'] and ['f','o','o'] are all equivalent. In other words, this defines that teststring is a list of characters: ['h','e','l','l','o'].
if ( teststring.is([char*,'o',char*]) ) ...
Here we're defining a new type - "lists that contain some characters, then an 'o', then some more characters" - and testing whether "hello" is accepted by that type. Yes, it contains an 'o', so it's accepted.
The test above is pretty cumbersome. Simple stuff should be simple, so instead of "is", you probably want to write "contains":
if ( teststring.contains('o') ) ...
What if you didn't care about 'o', but wanted to look for any vowel instead? Simple:
if ( teststring.contains(vowel) ) ...
(This is using the 'vowel' type I declared earlier.)
What if you want to search for a particular sequence of characters, instead of a single character?
if ( teststring.contains('l','l') ) ...
Yes, "hello" does contain two consecutive 'l's.
In case you were wondering, no, the contains function does not have a form that takes two arguments. I hope you were paying attention earlier: it's actually taking a list fragment! Notice that there aren't any square brackets here. We didn't write:
if ( teststring.contains(['l','l']) ) ...
That would treat teststring as a list of strings, and look to see whether any of them was the string "ll". (The test would fail, because teststring isn't a list of strings.)
Ok, so far so good. What if you wanted to generalise this to accept any double letter? Well, I already showed how to declare temporary variables within a type. You can reuse these variables within the same pattern, like so:
if ( teststring.contains(char @x, x) ) ...
What if you don't care whether the characters are consecutive? No problem...
if ( teststring.contains(char @x, char*, x) ) ...
I could keep going, but hopefully you get the idea. (This post has already taken an unhealthy amount of time to write, anyway...)
Although the Flan type system can't compete with the terseness of conventional regular expressions (they're pathologically terse), I think it's significantly more readable - mainly thanks to the way it lets you compose an expression out of simpler expressions.
Thanks for reading! I'll leave you with some more examples of Flan code...
// when used in a function declaration instead of the return type,
// the "is" keyword signifies that it's actually a type declaration.
// The resultant type can only accept values for which the function
// body returns true.
is @even( int @n )
{
..n.mod(2).is(0);
}
even @x = 2;
even @y = 3; // compiler complains because 3.mod(2) is 1, not 0.
// in Flan, what would be a template in some languages
// is simply a function that returns a type.
// oneof([1,2,3]) is equivalent to the type 1¦2¦3.
is @oneof( [value*] @v )( value @x )
{
..v.contains(x);
}
// some useful types for characters:
@digit = oneof(['0'..'9']);
@lowercase = oneof(['a'..'z']);
@uppercase = oneof(['A'..'Z']);
@letter = lowercase¦uppercase;
@alphanum = letter¦digit;
@whitespace = ' '¦'\t'¦'\n'¦'\r';
// a type that matches strings from "0" to "255"
is @string0to255( [digit:1..3] @s )
{
..s.toNumber().is( >=0 <=255 );
}
string0to255 @str1 = "177";
string0to255 @str2 = "256"; // compiler complains
// a type that matches IP addresses
@ipAddress = (/str0to255,'.'):3, /str0to255;
// one (inefficient) way to implement the "contains" method we saw earlier.
bool value. @contains( type @t )
{
..return this.is([value*,t,value*]);
}
Monday, February 06, 2006
Why "Flan"?
Its main experimental feature was an unusual way for lists to work. The idea: what if every primitive value was effectively a single-element list, and the only way you had to combine them was concatenation via the comma operator?
a = 1 // the list [1]
b = 2,3,4 // the list [2,3,4]
c = a,b // the list [1,2,3,4]
There's one immediately obvious consequence: there's no way, syntactically, to express lists of lists. In a fit of cute, I called the language Flatlang, because all its lists were flat.
(In case you're wondering: lists of pointers to lists were legal. So in a sense, C works the same way.)
Flan, my spiritual successor to Flatlang, retains this concept as the core of the language, but unlike its predecessor, it actually (I hope to show) it makes this a useful feature. (In Flatlang it wasn't, really. The rest of the language didn't really harmonize with it; in my dissertation I called it a gimmick.)
To demonstrate the power of the flat list, I'm going to have to introduce some other features first. They're pretty cool, though, so I don't think you'll get bored.
@mylist = 1,2,3,2,1;
Oh, ok, this bit's not especially cool.
Not much to say here - I'm simply declaring that the identifier "mylist" corresponds to the list [1,2,3,2,1]. Recall that the @ prefix is my syntax for declaring a new identifier.
print(each in mylist);
This prints 12321. The "each...in" operator is very powerful. It's derived from an idea in Jonathan Blow's language Lerp; essentially, it wraps an expression in a C#-style "foreach" loop. So this could be rewritten as:
foreach @x in mylist
{
..print( x );
}
(This is Flan, not C#, so when we declare a loop variable we use the @ syntax.)
There's more to come...
print(1 + each in mylist);
Now we're starting to get somewhere! The above prints 23432. It's equivalent to writing:
foreach @x in mylist
{
..print( 1+x );
}
Yes, that's a simple and (more importantly) natural syntax for map. (I mean the function, not the data structure). I think the unnaturalness of many higher-level functions is one of the key obstacles to the mainstream acceptance of languages like Lisp or Haskell. There's no question that map expresses an abstraction of something that we do all the time... but every time I do it, I have to rearrange what's in my head to match how map makes me express it.
With the syntax above, I can simply write down what I want to do.
So far all we've done is transform the individual items in a list. In the real world we're going to need the ability to operate on the whole list. Come up here and take a bow, sum:
print( sum( mylist ));
Not much to say there. sum takes a list, adds up its elements, and returns the total. This prints 9.
print( sum( 1+each in mylist ) );
And this prints... 23432 again? Oops. Well, every language has its gotchas.
Why does this happen? Let's do the rearranging thing:
foreach @x in mylist
{
..print(sum(1+x));
}
Huh... when I said that "each" wraps a foreach loop around the whole statement, I really did mean the whole statement. We're calling sum five times, and passing it one number at a time. To control this we're going to need a bit more new syntax...
print( sum #( 1+each in mylist ));
There we go! This prints 14 as expected.
# means, essentially, "put the foreach loop here". It collects up an "each" loop into a list, so that the code outside can treat it as a whole. I'd prefer to use an english term that captured this (all?), but I don't think a clear one exists.
It's also not obvious how to rearrange this example. The nicest way to express it without using # or map is probably:
@temp = _; // temp is the empty list
foreach( @x in mylist )
{
..temp = temp,1+x; // append to temp
}
print( sum( temp ));
Bleah.
Anyway, one more new feature...
print( x*x foreach @x in mylist );
This prints the square of each number; 14941. This one can be rearranged to:
foreach( @x in mylist )
{
..print( x*x );
}
Or if you prefer, it can also be rearranged to
print(x*x) foreach @x in mylist;
Until now the "foreach" statements I've shown you have been laid out like a C# foreach loop, but Flan allows them to be rearranged to whatever feels more natural - much like if statements in perl.
Phew. Ok, I know I said I'd explain the advantage of flat lists, but I think this is enough for one post.
Thanks for reading; join me next time when I cover accumulate, and explain why flat lists are cool.
PS:
If you're interested, why not try these exercises? Then post a comment to let me know what I've failed to explain...
1) Print each number in mylist multiplied by 100.
2) Print each number in mylist divided by 2. (use the / operator.)
3) Print the sum of squares of the numbers in mylist. (i.e. 1*1 + 2*2 + 3*3 + 2*2 + 1*1.)
4) Print the length of mylist.
5) Generate a "stuttering" version of mylist, with each element repeated: [1,1,2,2,3,3,2,2,1,1].
(Recall that I defined the comma operator to mean "concatenate".)
Answers: (highlight to read)
1) print( 100*each in mylist );
2) print( (each in mylist)/2 );
3) print( sum#( x*x foreach @x in mylist ));
4) print( sum#( 1 foreach in mylist ));
5) x,x foreach @x in mylist;
Monday, July 18, 2005
@beginning
Manifesto: This is my website for documenting the design of the programming language Flan.
I've been thinking about it and making notes about ideas for about a year and a half now, but hopefully this will be a good way to get it all in one place. And maybe even get some good suggestions from the general public.
First principles -
This is a language designed by me, for me. It would be nice if someone else in the world found it useful... but as long as I do, I'll consider it a success. So ner.
I'm most experienced as a C++ programmer, and it's becoming clear to me (and plenty of others, I'm sure) that the language has some annoying features in it. The Boost libraries go a remarkable way towards fixing them, but they're unreadable and unmaintainable. If I want a little feature that Boost doesn't provide, how do I add it? I wouldn't know where to start.
My wife says I should mention her. So I am. Happy, dear?
I don't have any old C programs that I want to convert, so I'm not worried about backwards compatibility. I'm just going to invent a new language from the ground up, with all the really whizzy new features that I think a language needs.
Let's start with something simple and petty:
In C++, I find declarations a little hard to spot. It would be useful to have an unambiguous, visually large prefix symbol that marks when you declare something. For instance, to declare a variable x, you could write something like "int @x = 4" or "int #x = 4". I'm going to go with @.
(I'll be using # for some other stuff later.)
Convenient results of this -
- Declarations stand out as you read the text.
- You can trivially perform a text search for the place where something is declared. (To help, I'm going to forbid whitespace between the @ and the new identifier.)
- Now that declarations are self-evident even without a type specification, we don't need one if the type is implicitly specified in other ways - i.e. constants.
For example, "const double @pi = 3.14159" is redundant. It can only evaluate to 3.14159, which we know is a double. So we can abbreviate it to "const @pi = 3.14159".
Indeed, since only constants can be abbreviated this way (the same assumption can't be made for variables - they might need to store other data later), the "const" is implicit and we can actually just write: @pi = 3.14159.
- Now that a constant doesn't need an explicit type specification, it makes sense to declare it in a function call. "Out" parameters usually require you to declare a variable to store their results.
vec3d pos;
getPosition(&pos);
I hate doing unnecessary typing, so I'm going to let you declare a constant and infer its type from the function signature, thus simplifying this down to:
getPosition(&@pos);
This saves you some typing and also makes it trivially obvious to the reader that getPosition won't (or at least, shouldn't) use any predefined data stored in pos.
Obviously the compiler will complain if the function is overloaded so that it's ambiguous what type should be used.
That's about it for now.
Next time I'll be taking the & out of getPosition(&@pos)...