New function type syntax
Provide a new, more natural syntax for declaring function types with support for argument names.
Haxe supports first-class functions from the beginning, using the following syntax for type-hinting variables containing functions:
Int -> String -> Void
This syntax is often found in functional languages, such as OCaml and Haskell, but much less in non-functional and hybrid languages.
There are several issues with this syntax:
- For people familiar with functional programming languages, it suggests that auto-currying and partial application are supported, but they aren't.
- For non-FP people, it looks unfamiliar and differs too much from the actual function definition syntax.
- It doesn't support argument names. While they aren't important for the type system, they are very useful for self-documenting code, IDE signature hints, callback auto-generation, etc. (see fancy examples with screenshots here)
What we could have instead is a function type syntax that follows the new arrow function syntax:
(id:Int, name:String) -> Void
The proposed syntax would looks like this:
// no arguments () -> Void // single argument (name:String) -> Void // multiple (also, optional) arguments (name:String, ?age:Int) -> Void // unnamed arguments (Int, String) -> Bool // mixed arguments, why not (a:Int, ?String) -> Void
This is a rather small parser change, adding additional rules after the
( token in
There are different approaches regarding representation of named arguments in the AST:
- Add a new
name:Typepart and use that for
CTFunctionargument list. This would be similar to
CTFunctionarguments structure to contain a list of
(name, opt, type)tuples, having empty string for names when the value comes from parsing unnamed arguments or old function type syntax.
Both options are viable and both will require changing macro data structures (which is breaking), so this is something we should discuss.
Interoperability with the old syntax
Old syntax stays in place and works like before, examples are:
Int -> Int (Int) -> Int (Int) -> Int -> Int (Int -> Int) -> Int -> Int // etc.
Mixing old and new syntax without parenthesis results in a syntax error:
(Int, Int) -> Int -> Int // syntax error: unexpected -> (a:Int) -> Int -> Int // same
If the desired behaviour is to have a functional return type, parenthesis should be used:
(Int, Int) -> (Int -> Int) (a:Int) -> (Int -> Int)
Impact on existing code
Depending on how we implement AST data structures (options are listed in the Representation section), this will potentionally break macros that work with
haxe.macro.Expr.ComplexType in one way or another, other than that it should not break anything because it's a completely new syntax.
The obvious drawback is that we'll have two function type syntaxes, which is why I think we should deprecate and eventually remove the old syntax. Removing the old syntax would be a huge change, but could still be an option for a major release, especially if we provide a migration tool.
I already tried the another approach of augmenting the current funtion type syntax with argument names:
a:Int -> b:String -> Void, but unfortunately that introduced a syntax ambiguity with macro reification and case patterns. One workaround for that would be to require parenthesis for named arguments, so it would be
(name:Type) -> Ret, but that suggests that one could do
(name:Type, name:Type) -> Ret while it's an invalid syntax (one would have to do
(name:Type) -> (name:Type) -> Ret instead).
ComplexTyperepresentation for named arguments
- Removal of old function type syntax