GNOME Bugzilla – Bug 727553
Dart-like syntax for very concise methods
Last modified: 2018-05-22 15:07:18 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); }
Created attachment 343622 [details] [review] Add support for expression-bodied function
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 >
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.
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/
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.
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"); }
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.
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.
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. :)
-- 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.