Help

This is the ninth installment in a series of articles introducing the Ceylon language. Note that some features of the language may change before the final release.

Named arguments

Consider the following method:

void printf(OutputStream to, String format, Object... values) { ... }

(Remember, the last parameter is a sequenced parameter which accepts multiple arguments, just like a Java varargs parameter.)

We've seen lots of examples of invoking a method or instantiating a class using a familiar C-style syntax where arguments are delimited by in parentheses and separated by commas. Arguments are matched to parameters by their position in the list. Let's see just one more example, just in case:

printf(process, "Thanks, %s. You have been charged %.2f. Your confirmation number is %d.", 
        user.name, order.total, order.confimationNumber);

This works fine, I suppose. However, Ceylon provides an alternative method invocation protocol that is usually easier to read when there are more than one or two arguments:

printf { 
    to = process;
    format = "Thanks, %s. You have been charged %.2f. Your confirmation number is %d."; 
    user.name, order.total, order.confimationNumber
};

This invocation protocol is called a named argument list. We can recognize a named argument list by the use of braces as delimiters instead of parentheses. Notice that arguments are separated by semicolons, except for arguments to the sequenced parameter, which are separated by commas. We explicitly specify the name of each parameter, except for the sequenced parameter, whose arguments always appear at the end of the named parameter list. Note that it's also acceptable to call this method like this, passing a sequence to the named value parameter:

printf { 
    to = process;
    format = "Thanks, %s. You have been charged %.2f. Your confirmation number is %d."; 
    values = { user.name, order.total, order.confimationNumber };
};

We usually format named argument invocations across multiple lines.

Declarative object instantiation syntax

Named arguments are very commonly used for building graphs of objects. Therefore, Ceylon provides a special abbreviated syntax that simplifies the declaration of an attribute getter, named parameter, or method that builds an object by specifying named arguments to the class initializer.

We're allowed to abbreviate an attribute definition of the following form:

Payment payment = Payment { 
    method = user.paymentMethod;
    currency = order.currency; 
    amount = order.total;
};

or a named argument specification of this form:

payment = Payment { 
    method = user.paymentMethod; 
    currency = order.currency; 
    amount = order.total;
};

to the following more declarative (and less redundant) style:

Payment payment { 
    method = user.paymentMethod; 
    currency = order.currency; 
    amount = order.total;
}

We're even allowed to write a method of the following form:

Payment createPayment(Order order) { 
    return Payment {
        method = user.paymentMethod; 
        currency = order.currency; 
        amount = order.total;
    };
}

using the following abbreviated syntax:

Payment createPayment(Order order) { 
    method = user.paymentMethod; 
    currency = order.currency; 
    amount = order.total;
}

Perhaps you're worried that this looks like a method that assigns the values of three attributes of the declaring class, rather than a shortcut syntax for a named argument instantiation of the Payment class. And that's a very fair point. To a Java developer, that is what it looks like. There's two things that should alert you to what's really going on. The above method:

  • has no return statement, but it's not declared void, and
  • contains a list of = specification statements instead of := assignment expressions.

Once you're used to Ceylon's more flexible syntax, these differences will usually stand out immediately.

More about named arguments

The following classes define a data structure for building tables:

class Table(String title, Natural rows, Border border, Column... columns) { ... } 
class Column(String heading, Natural width, String content(Natural row)) { ... } 
class Border(Natural padding, Natural weight) { ... }

Of course, we could built a Table using positional argument lists:

String x(Natural row) { return row.string; }
String xSquared(Natural row) { return (row**2).string; }
Table table = Table("Squares", 5, Border(2,1), Column("x",10, x), Column("x**2",12, xSquared));

However, it's far more common to use named arguments to build a complex graph of objects. In this section we're going to meet some new features of named argument lists, that make it especially convenient to build object graphs.

First, note that the syntax we've already seen for specifying a named argument value looks exactly like the syntax for refining a formal attribute. If you think about it, taking into account that a method parameter may accept references to other methods, the whole problem of specifying values for named parameters starts to look a lot like the problem of refining abstract members. Therefore, Ceylon will let us reuse much of the member declaration syntax inside a named argument list. (But note that this has not yet been implemented in the compiler.)

It's legal to include the following constructs in a named argument list:

  • method declarations — specify the value of a parameter that accepts a function,
  • object (anonymous class) declarations — are most useful for specifying the value of a parameter whose type is an interface or abstract class, and
  • getter declarations — lets us compute the value of an argument inline.

This helps explain why named argument lists are delimited by braces: the fully general syntax for a named argument list is very, very close to the syntax for a class, method, or attribute body. Notice, again, how flexibility derives from language regularity.

So we could rewrite the code that builds a Table as follows:

Table table = Table { 
    title="Squares";
    rows=5;
    border = Border {
        padding=2;
        weight=1;
    };
    Column {
        heading="x";
        width=10;
        String content(Natural row) {
            return row.string;
        }
    }, 
    Column {
        heading="x**2";
        width=12;
        String content(Natural row) {
            return (row**2).string;
        }
    }
};

Notice that we've specified the value of the parameter named content using the usual syntax for declaring a method.

Even better, our example can be abbreviated like this:

Table table { 
    title="Squares";
    rows=5;
    Border border {
        padding=2;
        weight=1;
    } 
    Column {
        heading="x";
        width=10;
        String content(Natural row) {
            return row.string;
        }
    }, 
    Column {
        heading="x**2"; 
        width=10;
        String content(Natural row) {
            return (row**2).string;
        }
    }
}

Notice how we've transformed our code from a form which emphasized invocation into a form that emphasizes declaration of a hierarchical structure. Semantically, the two forms are equivalent. But in terms of readability, they are very different.

We could put the above totally declarative code in a file by itself and it would look like some kind of mini-language for defining tables. In fact, it's executable Ceylon code that may be validated for syntactic correctness by the Ceylon compiler and then compiled to Java bytecode. Even better, the Ceylon IDE (when it exists) will provide authoring support for our mini-language. In complete contrast to the DSL support in some dynamic languages, any Ceylon DSL is completely typesafe! You can think of the definition of the Table, Column and Border classes as defining the schema or grammar of the mini-language. (In fact, they are really defining the syntax tree for the mini-language.)

Now let's see an example of a named argument list with an inline getter declaration:

shared class Payment(PaymentMethod method, Currency currency, Float amount) { ... }
Payment payment { 
    method = user.paymentMethod; 
    currency = order.currency; 
    Float amount {
        variable Float total := 0.0; 
        for (Item item in order.items) {
            total += item.quantity * item.product.unitPrice; 
        }
        return total;
    }
}

Finally, here's an example of a named argument list with an inline object declaration:

shared interface Observable { 
    shared void addObserver(Observer<Bottom> observer) { ... }
}
shared interface Observer<in Event> { 
    shared formal on(Event event);
}
observable.addObserver { 
    object observer satisfies Observer<UpdateEvent> {
        shared actual void on(UpdateEvent e) { 
            writeLine("Update:" + e.string);
        }
    }
};

Note that Observer<T> is assignable to Observer<Bottom> for any type T, since Observer<T> is contravariant in its type parameter T. If this doesn't make sense, please read these two sections again. ;-)

Of course, as we saw in Part 8, a better way to solve this problem might be to eliminate the Observer interface and pass a method directly:

shared interface Observable { 
    shared void addObserver<Event>(void on(Event event)) { ... }
}
observable.addObserver {
    void on(UpdateEvent e) { 
    	writeLine("Update:" + e.string);
    }
};

A quick tangent here: note that we need a type parameter T of the method addObserver() here only because Ceylon inherits Java's limitation that function types are nonvariant in their parameter types. This is actually pretty unnatural. We should probably eventually come up with a workaround to make function types contravariant in their parameter types, allowing us to write:

shared interface Observable { 
    shared void addObserver(void on(Bottom event)) { ... }
}

Defining user interfaces

One of the first modules we're going to write for Ceylon will be a library for writing HTML templates in Ceylon. A fragment of static HTML would look something like this:

Html { 
    Head head {
        title = "Hello World"; 
        cssStyleSheet = 'hello.css';
    } 
    Body body {
        Div { 
            cssClass = "greeting"; 
            "Hello World"
        },
        Div {
            cssClass = "footer"; 
            "Powered by Ceylon"
        }
    }
}

A complete HTML template might look like this:

import ceylon.html { ... }

doc "A web page that displays a greeting" 
page '/hello.html' 
Html hello(Request request) {
    
    Head head { 
        title = "Hello World"; 
        cssStyleSheet = 'hello.css';
    }
    
    Body body { 
        Div {
            cssClass = "greeting"; 
            Hello( request.parameters["name"] ).greeting
        }, 
        Div {
            cssClass = "footer"; 
            "Powered by Ceylon"
        }
    }

};

There's more...

There's plenty of potential applications of this syntax aside from user interface definition. For example, Ceylon lets us use a named argument list to the specify arguments of a program element annotation. But we'll have to come back to the subject of annotations in a future installment. In Part 10 we're going to discuss some of the basic types from the language module, in particular numeric types, and introduce the idea of operator polymorphism.

49 comments:
 
02. May 2011, 06:34 CET | Link

Hmmm... I guess I am starting to like this language...

ReplyQuote
 
02. May 2011, 16:55 CET | Link
Georg Ragaller

When and why is the $ syntax needed to refer to a parameter?

 
02. May 2011, 16:59 CET | Link
Georg Ragaller wrote on May 02, 2011 10:55:
When and why is the $ syntax needed to refer to a parameter?

From the usage it looks like a shortcut for .toString()

 
02. May 2011, 17:02 CET | Link
Georg Ragaller wrote on May 02, 2011 10:55:
When and why is the $ syntax needed to refer to a parameter?

It's the format operator. Sorry, I have not discussed it yet, so I should not have used it here. I'll discuss it in a future installment, but all it does is transform an object to a String via a pluggable mechanism. I'll change the code example to just use Object.string instead.

 
02. May 2011, 17:08 CET | Link

Actually, $ is one of the things I'm looking for a bit of feedback on. It doesn't yet look totally natural to my eyes.

 
02. May 2011, 17:14 CET | Link

Can the same syntax be used to initialize publicly accessible attributes of a class like C Sharp (http://msdn.microsoft.com/en-us/library/bb384062.aspx) or is it restricted to the named parameters of the constructor?

Given that there is no constructor overloading, that type of object initialization would give a lot of flexibility...

P.S. Whats up with session timeouts? If I start typing a comment, then leave it up for a few minutes I get kicked to the home page and I lose what I typed.

 
02. May 2011, 17:35 CET | Link
Marc Richards wrote on May 02, 2011 11:14:
Can the same syntax be used to initialize publicly accessible attributes of a class like C Sharp (http://msdn.microsoft.com/en-us/library/bb384062.aspx) or is it restricted to the named parameters of the constructor?

No, at one stage I was considering that idea (long before we discovered that C# would have this feature, actually) and decided against it because we're really trying to encourage a lot more use of immutability. And with Ceylon's very strict handling of null, you're really forced to do instantiation and initialization in one step anyway. You wouldn't want to declare all your attributes both optional and variable.

 
02. May 2011, 18:57 CET | Link
Gavin King wrote on May 02, 2011 11:08:
Actually, $ is one of the things I'm looking for a bit of feedback on. It doesn't yet look totally natural to my eyes.

return $row**2; did throw me off as well, perhaps because of its use in Perl and PHP. return #row**2; would probably look a little better to me, but perhaps some kind of brace syntax would make it even clearer. e.g.

return ${row**2}; or return $(row**2); or return #{row**2}; or return #(row**2);

 
02. May 2011, 20:25 CET | Link
Thorsten
Gavin King wrote on May 02, 2011 11:08:
Actually, $ is one of the things I'm looking for a bit of feedback on. It doesn't yet look totally natural to my eyes.

So, how would it look like when a format is given?

 
02. May 2011, 22:44 CET | Link

So to give a bit more information. The $ operator, and the string template syntax "foo" expr "bar", are defined to be the result of calling the attribute fomatted of Format.

shared interface Format {
    shared formal String formatted;
}

The only type which directly implements Format is String. All other types need to be decorated in order to be Formatable:

interface DefaultNumberFormat 
        satisfies Number & Format {
    shared actual String formatted {
        return string;
    }
}

Then, in some file where we need to be able to format Numbers:

decorate Number satisfies DefaultNumberFormat;

Then $num means num.string and "foo" num "bar" means "foo" + num.string + "bar" for any Number, within the lexical scope of the decorate statement, i.e. within the file.

The idea is that this means you can have different ways of formatting types for different uses, e.g. one way in user interfaces, another way in debug messages.

Of course this whole idea is something I'm looking for feedback on.

 
02. May 2011, 22:48 CET | Link
Marc Richards wrote on May 02, 2011 12:57:
Gavin King wrote on May 02, 2011 11:08:
Actually, $ is one of the things I'm looking for a bit of feedback on. It doesn't yet look totally natural to my eyes.
return $row**2; did throw me off as well, perhaps because of its use in Perl and PHP. return #row**2; would probably look a little better to me, but perhaps some kind of brace syntax would make it even clearer. e.g. return ${row**2}; or return $(row**2); or return #{row**2}; or return #(row**2);

Hum. For some reason I also find ${row**2} easier on the eyes. Can't really think of any rational reason why... Well, could it be because the precedence of $ is not something our brains are trained to understand?

The only major problem with that syntax would be that now ${...} would mean the exact opposite of what it means in languages like Groovy or JSP which use that syntax for string interpolation.

 
02. May 2011, 23:33 CET | Link
Could it be that ${row**2} is easier on the eyes thanks to decades of reading Ant files?
 
02. May 2011, 23:51 CET | Link
The only major problem with that syntax would be that now ${...} would mean the exact opposite of what it means in languages like Groovy or JSP which use that syntax for string interpolation.

Actually it's not really the opposite meaning (it still means cast to a string). It's just the opposite context (outside of a string literal instead of inside).

Another objection could be that ${row**2} is not really any less verbose that ""row**2"", which currently works, since it's a valid string template. Perhaps that's the answer: we don't need $ at all, because this works:

Column {
    heading="x**2"; 
    width=10;
    String content(Natural row) {
        return ""row**2"";
    }
}
observable.addObserver {
    void on(UpdateEvent e) { 
    	writeLine("Update:"e"");
    }
};

And, of course, you can always just call formatted directly like (row**2).formatted, if you wanted to be really explicit...

WDYT?

 
03. May 2011, 00:01 CET | Link

By the way, since we're having this discussion here, it's probably worth mentioning the reason for the slightly nontraditional string interpolation syntax.

Originally, I wanted to use "Hello, ${name}!", like many other languages. But we've found this impossible to lex in Ant. We checked how Groovy does it, and apparently they resort to a complicated hand-written lexer. Anyway, I think "Hello, " name "!" is a little cleaner, even if perhaps a little bit harder to parse visually. And I think with syntax highlighting the visual-parsing issues go away.

 
03. May 2011, 01:32 CET | Link
Gavin King wrote on May 02, 2011 17:51:
Another objection could be that ${row**2} is not really any less verbose that ""row**2"", which currently works, since it's a valid string template. Perhaps that's the answer: we don't need $ at all, because this works: WDYT?

I dunno. I was already on the fence about "Hello, " name "!". I think ""row**2"" is pushing it in terms of being able to visually parse code. Syntax highlighting is nice, but I should still be able read code from a basic text editor or you know, a blog post that doesn't use syntax highlighting ;-)

Quick side question. When you use the multi-line format to for strings, what happens to to the spaces at the beginning. For example, does this string

doc "The root type, supertype of
     both Object (definite values)
     and Nothing (the null value)."

equate to

"The root type, supertype of\n" +
"     both Object (definite values)\n" +
"     and Nothing (the null value)."

or

"The root type, supertype of\n" +
"both Object (definite values)\n" +
"and Nothing (the null value)."
 
03. May 2011, 04:45 CET | Link

The first, but String has a normalize() method to merge whitespace characters. At least, that's what seems to make sense to me...

 
03. May 2011, 05:21 CET | Link
Gavin King wrote on May 02, 2011 22:45:
The first, but String has a normalize() method to merge whitespace characters. At least, that's what seems to make sense to me...

Yea, I guess there is no getting round it. normalize() would help, but then you still end up with an extra space at the beginning of every line except the first right? What is the output expected to look like coming out of your doc generator?

 
03. May 2011, 05:27 CET | Link

I'm including \n as a whitespace character. (Actually normalize() accepts an optional list of whitespace characters.) If you're producing HTML or docbook output, you don't need to preserve newlines.

 
03. May 2011, 05:28 CET | Link
If you're producing HTML or docbook output, you don't need to preserve newlines.

Well, actually in that case you don't even need to normalize whitespace at all...

 
03. May 2011, 05:39 CET | Link
Gavin King wrote on May 02, 2011 23:27:
I'm including \n as a whitespace character. (Actually normalize() accepts an optional list of whitespace characters.)

How does it decide which whitespace character to keep? does it just keep the first one it encounters in a group? In that case the output would vary depending on whether or not there is a space char before the newline char.

03. May 2011, 05:46 CET | Link

Can Ceylon's anonymous class be used for creating adhoc objects that don't implement and interface? These types of objects are used alot in C# for Linq, but they also have other convenient uses like creating object graphs to be converted to JSON without defining a type beforehand. Link

03. May 2011, 07:16 CET | Link
Can Ceylon's anonymous class be used for creating adhoc objects that don't implement and interface?

Yes, they actually have a type, which is an ordinary class, it's just that the class doesn't have a well-defined name. They're not crippled in any other way.

 
03. May 2011, 07:18 CET | Link
Marc Richards wrote on May 02, 2011 23:39:
Gavin King wrote on May 02, 2011 23:27:
I'm including \n as a whitespace character. (Actually normalize() accepts an optional list of whitespace characters.)
How does it decide which whitespace character to keep? does it just keep the first one it encounters in a group? In that case the output would vary depending on whether or not there is a space char before the newline char.

Heh, had not thought of that. Hey, it's not like I've actually implemented the normalize() method yet!

I suppose it puts a single space in place of any string of whitespace characters....

 
03. May 2011, 08:36 CET | Link
Thorsten
Gavin King wrote on May 02, 2011 17:51:
Another objection could be that ${row**2} is not really any less verbose that ""row**2"", which currently works, since it's a valid string template. WDYT?

Well, I think ""row**2"" is quite ugly... Someone on Lambda the Ultimate did note that the Fortress people did back off from using simple concatenation because strings like "Hi, " name ", the " job ", how are you?" are difficult to read. I think the problem is that opening quote and closing quote cannot be distinguished. Therefore the eye should be helped by some operator + or ++ or whatever to clearly separate the parts inside and outside the string.

I think row.formatted is fine, especially with the general tendency of Ceylon to use meaningful words instead of symbolic operators.

This would result in "Hi, " + name.formatted + ", the " + job.formatted + ", how are you?" which is more readable in my opinion.

Actually for localized messages something more elaborate is needed for formatting like Smalltalk's StringParameterSubstitution or Java's MessageFormat with indexed parameters, like "Hi, <1s>, the <2f>, how are you?".formattedWith(name, job) where s means a string is given, f would mean to call formatted on the parameter.

This cannot be made typesafe when reading localized strings at runtime, though -- except if only string parameters would be allowed (modulo parameter count) which would preclude useful and needed features like modifying parts of the message to use singular or plural forms based on a number, e.g. "Really delete <1f> <1#file:files>?".

 
03. May 2011, 08:48 CET | Link
Thorsten
How does it decide which whitespace character to keep? does it just keep the first one it encounters in a group? In that case the output would vary depending on whether or not there is a space char before the newline char.

Haskell solves this with a backslash at the end and beginning of each line:

doc "The root type, supertype of \
     \both Object (definite values) \
     \and Nothing (the null value)."

That's less pretty but non-ambiguous.

 
03. May 2011, 14:46 CET | Link
Thorsten wrote on May 03, 2011 02:36:
Well, I think ""row**2"" is quite ugly... Someone on Lambda the Ultimate did note that the Fortress people did back off from using simple concatenation because strings like "Hi, " name ", the " job ", how are you?" are difficult to read. I think the problem is that opening quote and closing quote cannot be distinguished. Therefore the eye should be helped by some operator + or ++ or whatever to clearly separate the parts inside and outside the string.

Well, I think that's true if you're assuming that lots of people are reading/writing code w/o syntax highlighting. But really, the whole point of a language like this is to enable sophisticated tools. Yeah, I understand the argument about blogs w/o syntax highlighting, but, well, it's not really that technically hard to solve that problem.

I think row.formatted is fine, especially with the general tendency of Ceylon to use meaningful words instead of symbolic operators.

Yeah, I'm starting to lean to the view that it's better than the potentially cryptic $ operator, esp. in regular procedural code.

But for user interfaces defined using the object builder syntax, we definitely still need a clean string interpolation syntax.

 
03. May 2011, 14:47 CET | Link
Haskell solves this with a backslash at the end and beginning of each line:

I'm really not keen on that. Takes away much of the convenience and readability of the feature.

 
03. May 2011, 17:30 CET | Link
Curtis Stanford | curtis(AT)guild1.com
I'm really liking where you're going with this. One silly comment I have is can you get rid of those pesky semi-colons at the end of each line? After 15 years of Java, I'm sick of those things. :)
 
03. May 2011, 18:40 CET | Link
Curtis Stanford wrote on May 03, 2011 11:30:
I'm really liking where you're going with this. One silly comment I have is can you get rid of those pesky semi-colons at the end of each line? After 15 years of Java, I'm sick of those things. :)

I think auto-semicolon insertion (which is typically just something that happens in the lexer, so it's pretty independent of the grammar of the rest of the language) works out pretty nicely in languages where you don't expect expressions to span multiple lines very often. But that's not really the expectation in Ceylon. Especially not if we allow control-structure-y expression like:

repeat (3)
perform {
    writeLine("Hello");
};
assert ("must be positive")
that (x>10);

Which is something we have not covered yet. (And something that we're unsure about and looking for feedback on.)

Honestly I have never found semicolons a significant barrier to readability.

I'm not saying I don't love Python's clean look (I do, very much). But Python's syntax was designed from the ground for significant whitespace, and it works especially well there. I'm not sure that hacking semicolon insertion onto a C-like syntax works out quite as nicely as what Python has.

 
04. May 2011, 08:20 CET | Link
Thorsten
Gavin King wrote on May 03, 2011 08:47:
Haskell solves this with a backslash at the end and beginning of each line:
I'm really not keen on that. Takes away much of the convenience and readability of the feature.

Agreed. Maybe as an option when you want to be explicit with a sensible default (replace all whitespace between lines with a single space) when not using it?

05. May 2011, 07:02 CET | Link
Gavin King wrote on May 03, 2011 01:16:
Can Ceylon's anonymous class be used for creating adhoc objects that don't implement and interface?
Yes, they actually have a type, which is an ordinary class, it's just that the class doesn't have a well-defined name. They're not crippled in any other way.

What would that look like? For e.g in C# I might write

return Json(new { success = false, msg = "somebody set up us the bomb", errors = errorList });
 
05. May 2011, 22:17 CET | Link
Jon
One of the first modules we're going to write for Ceylon will be a library for writing HTML templates in Ceylon.

Sorry Gavin, that looks too much like ECS. Bad idea. http://jakarta.apache.org/ecs/

05. May 2011, 22:19 CET | Link

Well, I suppose:

object result {
    shared Boolean success = false;
    shared String msg = "somebody set up us the bomb";
    shared Error[] errors = errorList;
}
return Json(result);

I don't think it's really meant for the usecase you're interested in.

 
05. May 2011, 22:25 CET | Link
Jon wrote on May 05, 2011 16:17:
One of the first modules we're going to write for Ceylon will be a library for writing HTML templates in Ceylon. Sorry Gavin, that looks too much like ECS. Bad idea. http://jakarta.apache.org/ecs/

Yes, I suppose it's a bit like that. Really what we're getting at is something that could form the view layer for something like Wicket or JSF or Tapestry, without defining the whole web framework request processing lifecycle.

I think that there's real problems making something like ECS usable in Java, simply because Java doesn't have the language constructs to support it, stuff like the declarative object instantiation syntax, and higher-order function support, giving you the ability to define callbacks inline. (Look at some Wicket code, and then reimagine what Wicket would look like in Ceylon instead of Java.)

 
15. May 2011, 05:46 CET | Link

I agree with other sentiments that ""row**2"" is somewhat hard to grok. But then, I have a concern with the java-esque overload of + with this "" + row**2 + "", especially since + was supposed to mean Numeric.plus. It raises questions like Do strings satisfy Numeric? or do they get special treatment? Why can't my class have the same special treatment?

On the other hand--due to the x.formatted mixin-ability--a string format syntax would be really simple:

format("Hello, %1", name); //java-esque

"Hello, %1' % (name); //python-esque

"Hello, %1"
with (name); //seems similar to other Ceylon examples

Of course, these do have to deal with missing parameters that the current syntax avoids:

"Hello, %1, do you %2 regularly?"
with (name);

But then you have to deal with these kinds of things when looking at i18n and differing word order. You're more likely defining the format in a resource file somewhere and plugging in the holes in the program.

resources.greeting
with (name, topic);

Perhaps, string interoperability is not a problem for the compiler and deserves a run-time exception.

 
15. May 2011, 06:03 CET | Link
Michael wrote on May 14, 2011 23:46:
format("Hello, %1", name); //java-esque

"Hello, %1' % (name); //python-esque

"Hello, %1"
with (name); //seems similar to other Ceylon examples

Just remembered that you introduced printf in installment 8.

But my last observation still stands: string interoperability need not be a compiler problem--at least in the name of consistency, if not also in the name of simplicity.

 
15. May 2011, 16:27 CET | Link
It raises questions like "Do strings satisfy Numeric? or do they get special treatment? Why can't my class have the same special treatment?"

Well, there's something I haven't mentioned in these articles, because I'm not totally happy with it. But you've caught me out :-)

There's an interface called Summable, which declares a method called plus(). The + operator operates against Summable things. So the answers are:

  • no, strings aren't Numeric
  • no, they don't have special treatment
  • yes, your class can also implement Summable

I'm not totally in love with the use of + for something that's clearly not numeric addition, but it's just so conventional, and something that all Java developers are so accustomed to. And it's still not like real operator overloading, which would let you make + for Set<String> accept a String and return a Boolean. No, in this case, you can at least be sure that when you see +, then it's a binary operator that accepts two values of the same type and returns a value of the same type.

 
15. May 2011, 16:28 CET | Link
Just remembered that you introduced printf in installment 8.

That was just an example. The Ceylon libraries almost certainly won't have printf because it's not very typesafe.

 
15. May 2011, 16:31 CET | Link
Foo

Why String interpolation is not supported? E.g. like Ruby?

 
15. May 2011, 21:00 CET | Link
Thorsten
Gavin King wrote on May 15, 2011 10:27:
There's an interface called Summable, which declares a method called plus(). The + operator operates against Summable things. So the answers are:

I rather like the Monoid in Haskell which has mappend() with the ++ operator and mempty for the zero element. Will there be something like that in Ceylon?

 
16. May 2011, 16:11 CET | Link
Foo wrote on May 15, 2011 10:31:
Why String interpolation is not supported? E.g. like Ruby?

It is. Check Part 1.

 
16. May 2011, 16:13 CET | Link
Thorsten wrote on May 15, 2011 15:00:
Gavin King wrote on May 15, 2011 10:27:
There's an interface called Summable, which declares a method called plus(). The + operator operates against Summable things. So the answers are:
I rather like the Monoid in Haskell which has mappend() with the ++ operator and mempty for the zero element. Will there be something like that in Ceylon?

It's a very strong possibility that this functionality will be eventually redefined in terms of type classes. The language spec currently has a proposal for how type classes (metatypes) could work. But that won't be in early versions of the language, and it doesn't really solve the problem of what symbol to use of the join operator.

 
16. May 2011, 19:48 CET | Link
Thorsten
Gavin King wrote on May 16, 2011 10:13:
Thorsten wrote on May 15, 2011 15:00:
Gavin King wrote on May 15, 2011 10:27:
There's an interface called Summable, which declares a method called plus(). The + operator operates against Summable things. So the answers are:
I rather like the Monoid in Haskell which has mappend() with the ++ operator and mempty for the zero element. Will there be something like that in Ceylon?
It's a very strong possibility that this functionality will be eventually redefined in terms of type classes. The language spec currently has a proposal for how type classes (metatypes) could work.

Looking forward to that!

But that won't be in early versions of the language, and it doesn't really solve the problem of what symbol to use of the join operator.

Well, using ++ for Monoid's append and + for Numeric's plus() would result in separate operators for these operations (probably no need for Summable anymore as that's just one the Monoid instances for Numbers). And making String a Monoid would mean that ++ should be used for appending strings instead of juxtaposition.

17. May 2011, 21:23 CET | Link
Gavin King wrote on May 05, 2011 16:19:
Well, I suppose:
object result {
    shared Boolean success = false;
    shared String msg = "somebody set up us the bomb";
    shared Error[] errors = errorList;
}
return Json(result);
I don't think it's really meant for the usecase you're interested in.

How would you approach this usecase in Ceylon? Is there a literal syntax for a mapping/dictionary type (arbitrary name/value pairs)?

 
05. Jun 2011, 02:10 CET | Link
Gavin King wrote on May 02, 2011 18:01:
By the way, since we're having this discussion here, it's probably worth mentioning the reason for the slightly nontraditional string interpolation syntax. Originally, I wanted to use "Hello, ${name}!", like many other languages. But we've found this impossible to lex in Ant. We checked how Groovy does it, and apparently they resort to a complicated hand-written lexer. Anyway, I think "Hello, " name "!" is a little cleaner, even if perhaps a little bit harder to parse visually. And I think with syntax highlighting the visual-parsing issues go away.

I think supporting ${} would kill two birds with one stone. It would provide syntactic sugar for .formatted() and it could help with the readability of the string interpolation syntax e.g.

"Hello, " ${name} ", this is Ceylon " ${process.languageVersion}
" running on Java " ${process.javaVersion} " !"
 
26. Jun 2011, 15:06 CET | Link

Coming back to the inline getter declaration Payment.amount, I suppose the code will be evauluated each time? Will there be some support for lazy-initialization, so that it would be easy to define attributes calculated from other (immutable) attributes, but only once?

Adam

 
28. Jun 2011, 19:50 CET | Link
Adam Warski wrote on Jun 26, 2011 09:06:
Coming back to the inline getter declaration Payment.amount, I suppose the code will be evauluated each time? Will there be some support for lazy-initialization, so that it would be easy to define attributes calculated from other (immutable) attributes, but only once?

I believe that something like Fantom's once annotation can be pretty easily implemented using an interceptor.

 
29. Jun 2011, 16:11 CET | Link
Gavin King wrote on Jun 28, 2011 13:50:
Adam Warski wrote on Jun 26, 2011 09:06:
Coming back to the inline getter declaration Payment.amount, I suppose the code will be evauluated each time? Will there be some support for lazy-initialization, so that it would be easy to define attributes calculated from other (immutable) attributes, but only once?
I believe that something like Fantom's once annotation can be pretty easily implemented using an interceptor.

Right, nice :)

Adam

 
22. Dec 2011, 00:06 CET | Link
What about keeping all arguments but the last one inside parenthesis so instead of

Div {
  cssClass = "greeting";
  "Hello World"
}

you could write

Div(cssClass = "greeting") {
  "Hello World"
}

and there could be a visible distinction between (using XML-speak) "attributes" (inside "(...)") and "content" (inside "{...}")?
Post Comment