After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 727553 - Dart-like syntax for very concise methods
Dart-like syntax for very concise methods
Status: RESOLVED OBSOLETE
Product: vala
Classification: Core
Component: Parser
unspecified
Other All
: Normal enhancement
: ---
Assigned To: Vala maintainers
Vala maintainers
Depends on:
Blocks:
 
 
Reported: 2014-04-03 17:09 UTC by Adam Stark
Modified: 2018-05-22 15:07 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
Add support for expression-bodied function (7.14 KB, patch)
2017-01-17 06:59 UTC, Jeeyong Um
none Details | Review
parser: Add support for expression-bodied function (8.83 KB, patch)
2018-04-20 08:16 UTC, Jeeyong Um
none Details | Review
parser-Add-support-for-expression-bodied-function (8.79 KB, patch)
2018-04-24 18:44 UTC, Jeeyong Um
none Details | Review
parser: Add support for expression-bodied function (8.89 KB, patch)
2018-04-25 01:16 UTC, Jeeyong Um
none Details | Review

Description Adam Stark 2014-04-03 17:09:01 UTC
Dart (another language in the C#/Java/Vala syntax family) has a piece of syntactic sugar that I like.

  int _cancelTimerLoopAnimationFrame () => __timerLoopAnimationFrame.cancel();

which expands to.

  int _cancelTimerLoopAnimationFrame (){
    return __timerLoopAnimationFrame.cancel();
  }

It's a pretty simple syntactic change, and totally backward compatible. It's explained here <https://www.dartlang.org/dart-tips/dart-tips-ep-6.html>

It could be particularly handy for wrapped classes like this

class WrappedArrayList<T> : GLib.Object, Gee.Iterable, Gee.Traversable {
  private Gee.ArrayList<T> values;
  public WrappedArrayList() {
    values = new Gee.ArrayList<T>();
  }
  public override Iterator<T> iterator () => values.iterator();
  public override bool @foreach (ForallFunc<T> f) => values.foreach(f);
}
Comment 1 Jeeyong Um 2017-01-17 06:59:00 UTC
Created attachment 343622 [details] [review]
Add support for expression-bodied function
Comment 2 Jeeyong Um 2017-01-17 08:48:35 UTC
Comment on attachment 343622 [details] [review]
Add support for expression-bodied function

>From deb1264512957a33f99ebbf535f84fa4f4ed6c64 Mon Sep 17 00:00:00 2001
>From: Jee-Yong Um <jc9.um@samsung.com>
>Date: Tue, 17 Jan 2017 15:43:51 +0900
>Subject: [PATCH] vala: Introduce expression-bodied function members
>
>Functions (property getter or method) which return or execute only one
>expression can use lambda operator "=>" for their declarations.
>---
> vala/valaparser.vala | 142 ++++++++++++++++++++++++++++++++-------------------
> 1 file changed, 89 insertions(+), 53 deletions(-)
>
>diff --git a/vala/valaparser.vala b/vala/valaparser.vala
>index 3c47dbe..a13f0a4 100644
>--- a/vala/valaparser.vala
>+++ b/vala/valaparser.vala
>@@ -2340,6 +2340,7 @@ public class Vala.Parser : CodeVisitor {
> 					}
> 				case TokenType.OPEN_BRACE:
> 				case TokenType.THROWS:
>+				case TokenType.LAMBDA:
> 					rollback (begin);
> 					parse_property_declaration (parent, attrs);
> 					return;
>@@ -2733,7 +2734,11 @@ public class Vala.Parser : CodeVisitor {
> 			expect (TokenType.CLOSE_PARENS);
> 		}
> 		if (!accept (TokenType.SEMICOLON)) {
>-			method.body = parse_block ();
>+			if (!accept (TokenType.LAMBDA)) {
>+				method.body = parse_block ();
>+			} else {
>+				method.body = parse_expression_bodied_function (type);
>+			}
> 		} else if (scanner.source_file.file_type == SourceFileType.PACKAGE) {
> 			method.external = true;
> 		}
>@@ -2741,6 +2746,25 @@ public class Vala.Parser : CodeVisitor {
> 		parent.add_method (method);
> 	}
> 
>+	Block parse_expression_bodied_function (DataType type) throws ParseError {
>+		var begin = get_location ();
>+		var block = new Block (get_src (begin));
>+
>+		Statement stmt;
>+		var expr = parse_expression ();
>+		expect (TokenType.SEMICOLON);
>+		if (type is VoidType) {
>+			stmt = new ExpressionStatement (expr, get_src (begin));
>+		} else {
>+			stmt = new ReturnStatement (expr, get_src (begin));
>+		}
>+
>+		block.add_statement (stmt);
>+		block.source_reference.end = get_current_src ().end;
>+
>+		return block;
>+	}
>+
> 	void parse_property_declaration (Symbol parent, List<Attribute>? attrs) throws ParseError {
> 		var begin = get_location ();
> 		var access = parse_access_modifier ();
>@@ -2796,68 +2820,80 @@ public class Vala.Parser : CodeVisitor {
> 			} while (accept (TokenType.COMMA));
> 			Report.error (prop.source_reference, "properties throwing errors are not supported yet");
> 		}
>-		expect (TokenType.OPEN_BRACE);
>-		while (current () != TokenType.CLOSE_BRACE) {
>-			if (accept (TokenType.DEFAULT)) {
>-				if (prop.initializer != null) {
>-					throw new ParseError.SYNTAX ("property default value already defined");
>-				}
>-				expect (TokenType.ASSIGN);
>-				prop.initializer = parse_expression ();
>-				expect (TokenType.SEMICOLON);
>-			} else {
>-				comment = scanner.pop_comment ();
>+		if (accept (TokenType.OPEN_BRACE)) {
>+			while (current () != TokenType.CLOSE_BRACE) {
>+				if (accept (TokenType.DEFAULT)) {
>+					if (prop.initializer != null) {
>+						throw new ParseError.SYNTAX ("property default value already defined");
>+					}
>+					expect (TokenType.ASSIGN);
>+					prop.initializer = parse_expression ();
>+					expect (TokenType.SEMICOLON);
>+				} else {
>+					comment = scanner.pop_comment ();
> 
>-				var accessor_begin = get_location ();
>-				var accessor_attrs = parse_attributes ();
>-				var accessor_access = parse_access_modifier (SymbolAccessibility.PUBLIC);
>+					var accessor_begin = get_location ();
>+					var accessor_attrs = parse_attributes ();
>+					var accessor_access = parse_access_modifier (SymbolAccessibility.PUBLIC);
> 
>-				var value_type = type.copy ();
>-				value_type.value_owned = accept (TokenType.OWNED);
>+					var value_type = type.copy ();
>+					value_type.value_owned = accept (TokenType.OWNED);
> 
>-				if (accept (TokenType.GET)) {
>-					if (prop.get_accessor != null) {
>-						throw new ParseError.SYNTAX ("property get accessor already defined");
>-					}
>+					if (accept (TokenType.GET)) {
>+						if (prop.get_accessor != null) {
>+							throw new ParseError.SYNTAX ("property get accessor already defined");
>+						}
> 
>-					if (getter_owned) {
>-						value_type.value_owned = true;
>-					}
>+						if (getter_owned) {
>+							value_type.value_owned = true;
>+						}
> 
>-					Block block = null;
>-					if (!accept (TokenType.SEMICOLON)) {
>-						block = parse_block ();
>-						prop.external = false;
>-					}
>-					prop.get_accessor = new PropertyAccessor (true, false, false, value_type, block, get_src (accessor_begin), comment);
>-					set_attributes (prop.get_accessor, accessor_attrs);
>-					prop.get_accessor.access = accessor_access;
>-				} else {
>-					bool writable, _construct;
>-					if (accept (TokenType.SET)) {
>-						writable = true;
>-						_construct = accept (TokenType.CONSTRUCT);
>-					} else if (accept (TokenType.CONSTRUCT)) {
>-						_construct = true;
>-						writable = accept (TokenType.SET);
>+						Block block = null;
>+						if (!accept (TokenType.SEMICOLON)) {
>+							block = parse_block ();
>+							prop.external = false;
>+						}
>+						prop.get_accessor = new PropertyAccessor (true, false, false, value_type, block, get_src (accessor_begin), comment);
>+						set_attributes (prop.get_accessor, accessor_attrs);
>+						prop.get_accessor.access = accessor_access;
> 					} else {
>-						throw new ParseError.SYNTAX ("expected get, set, or construct");
>-					}
>-					if (prop.set_accessor != null) {
>-						throw new ParseError.SYNTAX ("property set accessor already defined");
>-					}
>-					Block block = null;
>-					if (!accept (TokenType.SEMICOLON)) {
>-						block = parse_block ();
>-						prop.external = false;
>+						bool writable, _construct;
>+						if (accept (TokenType.SET)) {
>+							writable = true;
>+							_construct = accept (TokenType.CONSTRUCT);
>+						} else if (accept (TokenType.CONSTRUCT)) {
>+							_construct = true;
>+							writable = accept (TokenType.SET);
>+						} else {
>+							throw new ParseError.SYNTAX ("expected get, set, or construct");
>+						}
>+						if (prop.set_accessor != null) {
>+							throw new ParseError.SYNTAX ("property set accessor already defined");
>+						}
>+						Block block = null;
>+						if (!accept (TokenType.SEMICOLON)) {
>+							block = parse_block ();
>+							prop.external = false;
>+						}
>+						prop.set_accessor = new PropertyAccessor (false, writable, _construct, value_type, block, get_src (accessor_begin), comment);
>+						set_attributes (prop.set_accessor, accessor_attrs);
>+						prop.set_accessor.access = accessor_access;
> 					}
>-					prop.set_accessor = new PropertyAccessor (false, writable, _construct, value_type, block, get_src (accessor_begin), comment);
>-					set_attributes (prop.set_accessor, accessor_attrs);
>-					prop.set_accessor.access = accessor_access;
> 				}
> 			}
>+			expect (TokenType.CLOSE_BRACE);
>+		} else if (expect (TokenType.LAMBDA)) {
>+			comment = scanner.pop_comment ();
>+
>+			var accessor_begin = get_location ();
>+			var value_type = type.copy ();
>+
>+			var block = parse_expression_bodied_function (value_type);
>+			prop.external = false;
>+
>+			prop.get_accessor = new PropertyAccessor (true, false, false, value_type, block, get_src (accessor_begin), comment);
>+			prop.get_accessor.access = prop.access;
> 		}
>-		expect (TokenType.CLOSE_BRACE);
> 
> 		if (!prop.is_abstract && prop.source_type == SourceFileType.SOURCE) {
> 			bool empty_get = (prop.get_accessor != null && prop.get_accessor.body == null);
>-- 
>2.7.4
>
Comment 3 Jeeyong Um 2018-04-20 08:16:22 UTC
Created attachment 371152 [details] [review]
parser: Add support for expression-bodied function

Unlike previous patch, expression-bodied function is fully supported for properties (set/get) and methods. Test case is included.
Comment 4 Al Thomas 2018-04-20 12:47:10 UTC
Hmm, not sure about this one. "very concise methods" might be an exaggeration. From what I can tell the function body delimiters, {}, are replaced with two new characters, =>, and the 'return' keyword is dropped. So really it saves the five characters of 'return'.

Personally I'm in favour of developing the functional programming style within Vala, but that probably won't be a goal for a 1.0 release. Reviewing map, filter, reduce use in Vala might be start. I also like the idea of some form of pattern matching in Vala (https://stackoverflow.com/questions/2502354/what-is-pattern-matching-in-functional-languages).

As far as syntax style goes I wonder why F#, the functional successor to C#, uses -> instead of =>. See https://fsharpforfunandprofit.com/posts/defining-functions/
Comment 5 Jeeyong Um 2018-04-20 13:22:16 UTC
I omitted an example in test case. This syntax allows very short read-only property.

Example:
class Foo {
    private int _bar;
    public int bar => _bar;
}

I don't think all syntactic sugars in other programming languages should be in Vala, but improving readability is very important. In large project, codes are usually maintained by others who are not a writer of those lines. Not only do concise expressions decrease time to read but make it easy to understand them.
Comment 6 Rico Tzschichholz 2018-04-24 17:16:14 UTC
This is interesting, but your example is not a good one while this is currently achieved with:

class Foo {
    public int bar { get; private set; }
}


Still this makes things more consistent with the current lambda expressions where this shortened syntax is already possible.


[CCode (has_target = false)]
delegate unowned string Func ();

unowned string func () {
    return "bar";
}

void main () {
    Func foo = () => "foo";
    Func bar = () => func ();
    assert (foo () == "foo");
    assert (bar () == "bar");
}
Comment 7 Jeeyong Um 2018-04-24 18:44:33 UTC
Created attachment 371338 [details] [review]
parser-Add-support-for-expression-bodied-function

I didn't think about that case. That's a good idea. Patch already covers that usage. I only changed the test case with your example.
Comment 8 Rico Tzschichholz 2018-04-24 19:46:25 UTC
I don't think you meant to replace your tests specific to this report.

My example was just to show the current possibility without your patch and is already covered by the existing test-suite.
Comment 9 Jeeyong Um 2018-04-25 01:16:43 UTC
Created attachment 371358 [details] [review]
parser: Add support for expression-bodied function

Oh, I misunderstood your intention. I changed the test case with original one (a little modified). Thanks for your suggestions. If possible, I hope to write patches make the syntax of Vala more concise or convenient. :)
Comment 10 GNOME Infrastructure Team 2018-05-22 15:07:18 UTC
-- GitLab Migration Automatic Message --

This bug has been migrated to GNOME's GitLab instance and has been closed from further activity.

You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.gnome.org/GNOME/vala/issues/439.