Sign in to follow this  
Ryan_001

Language Syntax

Recommended Posts

This is a pretty general question. What language do you find has the best/easiest to read/cleanest/(insert superlative here) syntax? Ok I'm not trying to start a language war. We all (I imagine) would agree that language syntax isn't the main factor of why someone would use a language; and for the most part one syntax is just as good as the next. I'm just curious as to what ppl have seen that may have made them go, 'oh wow cool', or 'never seen that before'. Or maybe played around with a scripting language or some esoteric language that had a really nice/clean syntax. Or maybe just a general philosophy on why a type of syntax is beneficial for whatever reason.

Share this post


Link to post
Share on other sites
Ease of reading is a matter of habit. If you spend a few weeks working with a language, you will start finding its syntax clean and nice.

I tend to consider statement-and-expression languages to have an inferior syntax to the expression-only languages. While statements are a reasonable legacy of the good old days, having everything be an expression is a huge boost in expressiveness. Even more so when you have access to anonymous lambdas.

Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVykI tend to consider statement-and-expression languages to have an inferior syntax to the expression-only languages. While statements are a reasonable legacy of the good old days, having everything be an expression is a huge boost in expressiveness. Even more so when you have access to anonymous lambdas.


Any particular examples you have in mind?

Share this post


Link to post
Share on other sites
I find C# 2.0 syntax to be the best in my experience. Low punctuation ambiguity, little excessive verbosity, only a few 'shapes' in the code and the shapes tend towards similar constructs.

But we'll see. My toy language supports some things that might lead to nicer syntaxes, but it's not there yet. And I can easily admit that improvements can be made than what we've got now.

Share this post


Link to post
Share on other sites
Quote:
Original post by Ryan_001
Any particular examples you have in mind?


Typical Objective Caml code:
let abs x = if x < 0 then -x else x
Equivalent C code, as short as possible:
int abs(int x) { return x < 0 ? -x : x; }


Notice the absence of return statement or brackets in the first code, one of the benefits of expression semantics. Other benefits include throwing an exception or placing an assert at any point of an expression. It also includes placing a switch statement anywhere, instead of having to put it on its own somewhere. Consider a small code sample for choosing a color:

type color = Black | White

try
let input = read_line () in
let color = List.assoc [ "black", Black ; "white", White ] input in
printf "You have chosen the %s side\n"
(match color with Black -> "dark" | White -> "light")
with Not_found -> (* Thrown by List.assoc *)
printf "Invalid color choice.\n"


Equivalent C++ code:
enum color { Black , White };

std::string input;
std::getline(std::cin, input);
color c;
if (input == "black") c = Black;
else if (input == "white") c = White;
else {
std::cout << "Invalid color choice.\n";
return;
}
std::cout << "You have chosen the ";
switch(input) {
case Black: std::cout << "dark"; break;
case White: std::cout << "white"; break;
}
std::cout << " side\n";



Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
type color = Black | White

try
let input = read_line () in
let color = List.assoc [ "black", Black ; "white", White ] input in
printf "You have chosen the %s side\n"
(match color with Black -> "dark" | White -> "light")
with Not_found -> (* Thrown by List.assoc *)
printf "Invalid color choice.\n"


Whoa, I think my brain just exploded. I see what you mean though, about the switches, and how you can do other stuff like that. I'm curious, what does the in keyword mean? And are the indentation and line returns syntactically significant at all (besides just as token separators)?

Share this post


Link to post
Share on other sites
Quote:
Original post by TelastynBut we'll see. My toy language supports some things that might lead to nicer syntaxes, but it's not there yet. And I can easily admit that improvements can be made than what we've got now.


Sounds interesting. Any examples? Even theoretical ones?

Share this post


Link to post
Share on other sites
I find python the clean language. I wish other languages would take after it.


class Test():
def __init__(self):
self.foo = "";

def foo(self, x, y):
print x + y




Though I I don't really like weak typed language like Python all the much. I prefer the C/C++ declaration of variables. Python makes it to where you have to indent. Which I love. No more sloppy indention by people because they are too lazy to indent their code.

Share this post


Link to post
Share on other sites
Quote:
Original post by theOcelot
Quote:
Original post by ToohrVyk
type color = Black | White

try
let input = read_line () in
let color = List.assoc [ "black", Black ; "white", White ] input in
printf "You have chosen the %s side\n"
(match color with Black -> "dark" | White -> "light")
with Not_found -> (* Thrown by List.assoc *)
printf "Invalid color choice.\n"


Whoa, I think my brain just exploded. I see what you mean though, about the switches, and how you can do other stuff like that. I'm curious, what does the in keyword mean? And are the indentation and line returns syntactically significant at all (besides just as token separators)?


in does not really have a meaning, it's just a syntactic part of the expression: let <id>=<expr> in <expr>, which introduces a local (readonly) variable. The indentation and newlines have no significance in O'Caml.

F# however, a language based on O'Caml, supports a "light" syntax where some keywords are exchanged for indentation, e.g. the in can be left out but correct indentation is mandatory:

#light

open System.Collections.Generic

type color = Black | White

try
let input = Console.ReadLine ()
let color = Map( [ "black", Black; "white", White ] ).[input]
printfn "You have chosen the %s side"
(match color with Black -> "dark" | White -> "light")
with
:? KeyNotFoundException -> printf "Invalid color choice.\n"

Share this post


Link to post
Share on other sites
Quote:
Original post by SamLowry
in does not really have a meaning, it's just a syntactic part of the expression: let <id>=<expr> in <expr>, which introduces a local (readonly) variable. The indentation and newlines have no significance in O'Caml.


Meaning enough for me. Thanks

@xZekex: it would be kind of cool to have a statically typed language with Pythonish syntax. Come to think of it, I'm sure there is one. I don't like the indentation thing when I'm doing something at the interactive prompt. I don't like having to worry about formatting when I'm just hacking something out. Maybe Python is just the wrong language for that.

Share this post


Link to post
Share on other sites
Even though I think C# is the greatest thing since sliced bread, I find Python's syntax to be refreshing and clear. It's almost like the quintessential programming language.

Also, for a little slice of awesome, take a look at this complete raytracer written not in C# 3.0, but LINQ query statements inside of C# 3.0. It reminds me of some of the more functional languages. Not a bracket in sight.

Share this post


Link to post
Share on other sites
Quote:
Original post by xZekex
I find python the clean language. I wish other languages would take after it.

*** Source Snippet Removed ***

Though I I don't really like weak typed language like Python all the much. I prefer the C/C++ declaration of variables. Python makes it to where you have to indent. Which I love. No more sloppy indention by people because they are too lazy to indent their code.


Agreed. I <3 python. And the django framework.

I do have to correct one inaccuracy though: Python is a strongly typed, dynamic language. Variables are dynamically typed, not weakly typed. You don't have to declare a variable's type before the program is run, but once you assign a value to a variable, it can only be used to store that type of value. C++ is both strongly typed and statically typed. You must tell the compiler what type you intend to store in it.

Share this post


Link to post
Share on other sites
Quote:
Original post by smrI do have to correct one inaccuracy though: Python is a strongly typed, dynamic language. Variables are dynamically typed, not weakly typed. You don't have to declare a variable's type before the program is run, but once you assign a value to a variable, it can only be used to store that type of value. C++ is both strongly typed and statically typed. You must tell the compiler what type you intend to store in it.


Um, no actually.

>>> l = [12, 4, 3]
>>> print l
[12, 4, 3]
>>> l = 'fooie'
>>>


Runs just fine. Or did you mean something else?

Share this post


Link to post
Share on other sites
Python is strongly and dynamically typed. Strong typing does not prevent changing the type of a variable later. It does prevent changing the type of a value; i.e., there are no implicit conversions. Python values have types (and they are rigid); Python variables don't.

Share this post


Link to post
Share on other sites
Quote:
Original post by Zahlman
Python is strongly and dynamically typed. Strong typing does not prevent changing the type of a variable later. It does prevent changing the type of a value; i.e., there are no implicit conversions. Python values have types (and they are rigid); Python variables don't.


Yes... That's what I meant!

Share this post


Link to post
Share on other sites
I figured it was just a misunderstanding. I hope we haven't totally side-tracked this thread. It was just starting to get interesting. I would like to see some samples of Telastyn's language.

Share this post


Link to post
Share on other sites
Quote:
Original post by Ryan_001
Quote:
Original post by ToohrVyk. While statements are a reasonable legacy of the good old days, having everything be an expression is a huge boost in expressiveness. Even more so when you have access to anonymous lambdas.


Any particular examples you have in mind?
In Ruby, output every line in a file to screen:

File.readlines(filepath).each { |line| puts line }

Share this post


Link to post
Share on other sites
I like the syntaxes of your typical functional language. So stuff like ML and Haskell. Different strokes. A matter of opinion and preferences. Which you can respect and also respectfully disagree.

Not to cause any trouble or anything but the best description of Python's type system is loose - dynamic works but hides the nature. There are types kinda there but not really/only loosely in the type theoretic sense (for example at first glance it appears to be simply typed but it clearly is not, having far more power than a language with only such. Types from type theory do not really map to its notions of 'types'). I mean the whole monkey patching duck typing farm animal thing it has going flies in the face of type theory.

Python is not strongly typed - if (c = 7) comes up as a syntax error not a type mismatch, you can add floats and ints without conversion, the following:
def myfun(x):
if (x == 7): print "NN"
else: x
runs. in a language with strong typing it would be invalid since the type of the first branch does not match the second, etc. And to be honest strong typing done properly is an annoyance. Python semantics are closer to an untyped language than a typed one. And that is its strength really, why it is able to leverage its metaprogramming like facilities.

C++ is definitely not strongly typed - no need to look further than the existence of raw pointers. In some languages you can have implicit typing and still be static, such as haskell. You dont tell the compiler any types it infers the most general one.

[Edited by - Daerax on October 9, 2008 10:05:03 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by theOcelot
I figured it was just a misunderstanding. I hope we haven't totally side-tracked this thread. It was just starting to get interesting. I would like to see some samples of Telastyn's language.


Check out his journal. He has been posting about it since forever and you can download an old version.

Share this post


Link to post
Share on other sites
Quote:
Original post by Daerax
Quote:
Original post by theOcelot
I figured it was just a misunderstanding. I hope we haven't totally side-tracked this thread. It was just starting to get interesting. I would like to see some samples of Telastyn's language.


Check out his journal. He has been posting about it since forever and you can download an old version.


Indeed (though only for about 9 months). And for equality's sake, Daerax and ApochPiQ also have language posts around August. And it does have a site with more docs and v0.1

Most of the syntax is like C#, and the behavior is like Scala (though I didn't know about Scala until recently, and Scala is a lot more functional leaning).

The major distinction comes that:

- Methods can only be unary plus 1 or infix 2+2
- infix methods needn't be special characters and are user definable: 2 plus 2
- Multiple param methods look like normal: add(2,2)
- Though technically it forms the tuple 2,2 and passes that as one argument.
- And the parens are not required for simple arguments.

Thus these are all valid statements in the language (assuming the appropriate operators are defined):


Ogre smash Knight;
Ogre smash Knight with Boulder;
print "moo" + "cow";


in addition to normal C# looking syntax where everything is method(parameters);

The order of operations is... non-existent. The compiler uses the types involved to determine what order 'makes sense'. (Every statement must result in void, so there's going to be one interpretation where that works out. If ambiguous or unavailable, you get compile time errors)

Making compound functions (like the 2nd example above where one of the operators interacts with a 'preposition') is aided by a slight variation on method declarations:

public operator void smash (Actor Smasher, Actor Smashee){...}
public operator void smash (Actor Smasher, Actor Smashee) with (Object Weapon){...}
// with is not a keyword here, it's an identifier that tells the compiler that literally 'with' must appear in that position to match the method.


Which is simply sugar for a sort of currying setup. There are other goodies mostly in the looser, structural, mix-in style type system and the ability to do polymorphic dispatch on parameters as well as the calling object, but they don't influence syntax.

From the journal, here's code that uses the current unreleased version to make a maze based on the C# workshop's example:


// no enums yet... (bug #9)
public goose class PathState{}
public goose class Wall: PathState{}
public goose class Clear: PathState{}

public Wall wall = new Wall;
public Clear clear = new Clear;

public goose class TileState{}
public goose class Unvisited: TileState{}
public goose class Visited: TileState{}

public Unvisited unvisited = new Unvisited;
public Visited visited = new Visited;

public goose class CardinalDirection{}

public static CardinalDirection direction from code (int DirectionCode){
if(DirectionCode == 0){
return(north);
}else if(DirectionCode == 1){
return(east);
}else if(DirectionCode == 2){
return(south);
}else if(DirectionCode == 3){
return(west);
}else{
return(null);
}
}

public static CardinalDirection random direction{
return(direction from code rng.Next(0,4));
}

public goose class East: CardinalDirection{}
public goose class North: CardinalDirection{}
public goose class West: CardinalDirection{}
public goose class South: CardinalDirection{}

public East east = new East;
public North north = new North;
public West west = new West;
public South south = new South;


public Random rng = new Random;

public class Coordinate{
public int x;
public int y;

public bool this exists in (Map map){
if(x < 0 || map.Tiles.Count < x || map.Tiles.Count == x){
return(false);
}
if(y < 0 || map.Tiles[x].Count < y || map.Tiles[x].Count == y){
return(false);
}
return(true);
}

// Should be this methods so can do 'Coord move north' (bug #15)
public Coordinate move (CardinalDirection direction){return(null);}
public Coordinate move (North direction){
local Coordinate rtn = new Coordinate;

rtn.x = x;
rtn.y = y - 1;
return(rtn);
}

public Coordinate move (East direction){
local Coordinate rtn = new Coordinate;

rtn.x = x + 1;
rtn.y = y;
return(rtn);
}

public Coordinate move (South direction){
local Coordinate rtn = new Coordinate;

rtn.x = x;
rtn.y = y + 1;
return(rtn);
}

public Coordinate move (West direction){
local Coordinate rtn = new Coordinate;

rtn.x = x - 1;
rtn.y = y;
return(rtn);
}
}

public class Tile{
public PathState EastPath = wall;
public PathState SouthPath = wall;

public TileState Status = unvisited;
}

public class Map{
public int maxX;
public int maxY;

public List<List<Tile>> Tiles = new List<List<Tile>>;

public void Initialize(){

Tiles.Clear();

// no for loops yet... (bug #11)
local int x;
local int y;
do{
y = 0;
Tiles.Add(new List<Tile>);
do{
Tiles[x].Add(new Tile);
y=y+1;
}while(y<maxY);
x=x+1;
}while(x<maxX);
}

}

public static void display( Map TargetMaze ){
local int x;
local int y;

x = 0;
print "#";
do{
print "##";
x = x + 1;
}while(x<TargetMaze.maxX);

print " \r\n";

x = 0;
y = 0;
do{
print "#";
x = 0;
do{
print ".";
if( TargetMaze.Tiles[x][y].EastPath == wall ){
print "#";
}else{
print ".";
}
x = x + 1;
}while(x<TargetMaze.maxX);

print " \r\n";

x = 0;
print "#";
do{
if( TargetMaze.Tiles[x][y].SouthPath == wall ){
print "#";
}else{
print ".";
}
print "#";
x = x+1;
}while(x<TargetMaze.maxX);

print " \r\n";
y = y+1;
}while( y < TargetMaze.maxY );

}

public static bool done in (Map workMap) at (Coordinate Cursor){
local Coordinate workCoordinate = new Coordinate;

workCoordinate = Cursor.move north;
if(workCoordinate exists in workMap && workMap.Tiles[workCoordinate.x][workCoordinate.y].Status == unvisited){
return(false);
}

workCoordinate = Cursor.move east;
if(workCoordinate exists in workMap && workMap.Tiles[workCoordinate.x][workCoordinate.y].Status == unvisited){
return(false);
}

workCoordinate = Cursor.move south;
if(workCoordinate exists in workMap && workMap.Tiles[workCoordinate.x][workCoordinate.y].Status == unvisited){
return(false);
}

workCoordinate = Cursor.move west;
if(workCoordinate exists in workMap && workMap.Tiles[workCoordinate.x][workCoordinate.y].Status == unvisited){
return(false);
}

return(true);
}


public static void MazeStep( Map workMap, Coordinate Cursor){

workMap.Tiles[Cursor.x][Cursor.y].Status = visited;

// Not declared in the while loop due to bug #14.
local CardinalDirection RandomDirection;
local Coordinate NewCursor;

while( !done in workMap at Cursor ){


do{
RandomDirection = random direction;
NewCursor = Cursor.move RandomDirection;

}while( !(NewCursor exists in workMap &&
workMap.Tiles[NewCursor.x][NewCursor.y].Status == unvisited));

if( RandomDirection == east ){
workMap.Tiles[Cursor.x][Cursor.y].EastPath = clear;
}else if(RandomDirection == south){
workMap.Tiles[Cursor.x][Cursor.y].SouthPath = clear;
}else if(RandomDirection == west){
workMap.Tiles[NewCursor.x][NewCursor.y].EastPath = clear;
}else if(RandomDirection == north){
workMap.Tiles[NewCursor.x][NewCursor.y].SouthPath = clear;
}else{
print "!!! Unknown random direction?!?";
}

MazeStep(workMap,NewCursor);
}

}

public static Map create (int xsize) by (int ysize) maze{

local Map rtn = new Map;

rtn.maxX = xsize;
rtn.maxY = ysize;

rtn.Initialize();

local Coordinate Cursor = new Coordinate;

Cursor.x = rng.Next(0,xsize);
Cursor.y = rng.Next(0,ysize);

MazeStep(rtn,Cursor);

return(rtn);
}

public static void main(){

display (create 40 by 40 maze);
}



Like I said, not sure how well it might work in practice. Things might prove to be too ambiguous in too many cases, or the baby-speak english won't be unambiguous enough for programmers to deal with, or the design burden might be too great to keep people from mis-using common words causing problems.

Ideally, it will allow programmers to design (or use via library) a DSL for their environment/industry and then program/script against that. And it should make optional parameters more natural as well as provide info about the parameters at calling site, eliminating confusion there. That's the idea anyways.

Share this post


Link to post
Share on other sites
That's pretty cool, Telastyn. I've been thinking lately about functions that have name bits in the argument like DoFooOn(x)with(y);, just to make function calls read more like sentences. I never really thought of extending it to an operator type syntax. I've also always wanted polymorphic dispatch on parameter types.

What does goose mean?

Share this post


Link to post
Share on other sites
Quote:
Original post by theOcelot
What does goose mean?


By default, types are structural.


class HasName{
public abstract string Name;
}

class Pirate{
public string Name = "Blackbeard";
}


Pirate will be considered a subclass of HasName even though it doesn't explicitly inherit from it. Conceptually, it acts as a statically typed form of duck-typing. goose (looks like a duck, but doesn't talk like one) disables that behavior. To be considered a subtype of a goose class, the other class needs to explicitly inherit from it. It's used in the maze example to force the different empty types to be not equivalent.

Share this post


Link to post
Share on other sites
As we are talking about syntax:
Quote:
Ogre smash Knight with Boulder;

Is the Ogre using the Boulder to smash the Knight or
is the Ogre smashing a Knight who is carrying a Boulder?
The definitions of the operations explain it but these lines can be more ambiguous than expected.

That is why I like Functional Programming languages (F#, Haskell etc.): when functions are named correctly, nothing else is going on, there are no side effects. As readable as some syntax might appear it's the side effects that throw me off balance...

Share this post


Link to post
Share on other sites
Quote:
Original post by ernow
Is the Ogre using the Boulder to smash the Knight or
is the Ogre smashing a Knight who is carrying a Boulder?
The definitions of the operations explain it but these lines can be more ambiguous than expected.
Yeah, that confused me as well. Looking at the method declarations though, it's basically like writing the following in C++:
void smash( Actor& Smasher, Actor& Smashee, Object& Weapon );
...
smash( Ogre, Knight, Boulder );

Without looking at the declarations, this would be ambiguous to the reader though, right?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this