Feature request: type inference on anonymous functions

504 views
Skip to first unread message

Magnús Örn Gylfason

unread,
Jan 4, 2014, 3:25:42 AM1/4/14
to golan...@googlegroups.com
This has probably been requested a thousand times before, so let me be number 1001!

I would love to see some way to do type inference on anonymous functions so I would not have to type out the function signature when sending an function pointer into a function. 

For example, rather than typing:

Foo.A(func(a int) int {
  return a*2
})

Foo.B(func(a, b int) int {
  return a+b
})

we could do write: 

Foo.A({ return a*2 })

Foo.B({ return a+b })

Maybe the way to achieve this would be to create a function type and then declare A and B to receive those

type FuncA func(a int) int
type FuncB func(a,b int) int

func (f Foo) A(f FuncA) {
}

func (f Foo) B(f FuncB) {
}

Then the definitions of FuncA and FuncB could be used to define the a and b variables in the anonymous code blocks.

cheers,
mg

Dave Cheney

unread,
Jan 4, 2014, 3:28:21 AM1/4/14
to Magnús Örn Gylfason, golan...@googlegroups.com


On 4 Jan 2014, at 19:25, Magnús Örn Gylfason <magnus....@gmail.com> wrote:

This has probably been requested a thousand times before, so let me be number 1001!

I would love to see some way to do type inference on anonymous functions so I would not have to type out the function signature when sending an function pointer into a function. 

For example, rather than typing:

Foo.A(func(a int) int {
  return a*2
})

Foo.B(func(a, b int) int {
  return a+b
})

we could do write: 

Foo.A({ return a*2 })

Foo.B({ return a+b })

Maybe the way to achieve this would be to create a function type and then declare A and B to receive those

type FuncA func(a int) int
type FuncB func(a,b int) int


Give this a try in the playground, you may be presently surprised. 


func (f Foo) A(f FuncA) {
}

func (f Foo) B(f FuncB) {
}

Then the definitions of FuncA and FuncB could be used to define the a and b variables in the anonymous code blocks.

cheers,
mg

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Magnús Örn Gylfason

unread,
Jan 4, 2014, 3:39:20 AM1/4/14
to golan...@googlegroups.com, Magnús Örn Gylfason
I did, and all I got for my trouble was a syntax error: 

syntax error: unexpected {

package main

import "fmt"

type FuncA func(a,b int) int
type Foo struct { }


func (f Foo) FooFunc(fu FuncA) {
    return fu(4,5)
}

func main() {
    var f Foo
    
    fmt.Println(f.FooFunc({ return a+b }))  // <- syntax error is here
}


Should this have worked?

Dave Cheney

unread,
Jan 4, 2014, 3:43:29 AM1/4/14
to Magnús Örn Gylfason, golan...@googlegroups.com, Magnús Örn Gylfason


On 4 Jan 2014, at 19:39, Magnús Örn Gylfason <magnus....@gmail.com> wrote:

I did, and all I got for my trouble was a syntax error: 

syntax error: unexpected {

package main

import "fmt"

type FuncA func(a,b int) int

In as much as this is valid syntax, yes

type Foo struct { }


func (f Foo) FooFunc(fu FuncA) {

This has no return type. 

    return fu(4,5)
}

func main() {
    var f Foo
    
    fmt.Println(f.FooFunc({ return a+b }))  

You still need to repeat the func definition here as well I think

Kyle Lemons

unread,
Jan 4, 2014, 3:12:01 PM1/4/14
to Dave Cheney, Magnús Örn Gylfason, golan...@googlegroups.com
While proposals like this don't normally catch my attention, I think this one has some interesting bits to it.

For the purposes of my discussion, I assume something like the following would be legal:

package main

import "fmt"

type F func(y int) (x int)

func main() {
fmt.Println(F{x = y*y}(4)) // prints 16
}

This has the following rules:
- Input parameters must be named in the type for this to be usable.
- Iff the returns are named, an implicit bare "return" is assumed if the last statement is not a return or a panic

This has a number of interesting properties:
- Like most other such proposals, it dramatically reduces the amount of typing required for func literals.
- It enforces consistent naming of the parameters across all uses of the type
- Like structs when you use fields in your literals, adding a named param or return needn't cause a compile error
  and might not require updating all references to the type, depending on the semantics
- Doesn't compromise type-safety, but still allows you to change e.g. size/signedness or use a named type in the
  definition without being forced to also make the same change in dozens of places

The immediate problem with my flavor of his proposal is that statements are not allowed in composite literals syntactically, and F{} is a composite literal.  I'm sure there are a number of ways around this syntactically, like making it func F{} or F(...){} (where the "..." is actually three dots, implying that something is being inferred in the same way as an array literal).
Message has been deleted

Carl Johnson

unread,
Jan 4, 2014, 11:31:06 PM1/4/14
to golan...@googlegroups.com
Stealing from the array syntax, how about Foo(func ... { return 10 }) ? In other words, "..." means "infer from context" as it does for arrays.

Paul Hankin

unread,
Jan 5, 2014, 5:51:08 AM1/5/14
to golan...@googlegroups.com, Dave Cheney, Magnús Örn Gylfason


On Saturday, 4 January 2014 21:12:01 UTC+1, Kyle Lemons wrote:
While proposals like this don't normally catch my attention, I think this one has some interesting bits to it.

For the purposes of my discussion, I assume something like the following would be legal:

package main

import "fmt"

type F func(y int) (x int)

func main() {
fmt.Println(F{x = y*y}(4)) // prints 16
}

I think needs refining, since I don't think this can't be parsed without using a symbol table. That is, F{} or F{<expr>} could be a literal struct or an anonymous function. F{A:<expr>} could be an anonymous function with a label or a literal struct with a named field A.

-- 
Paul Hankin

Caleb Doxsey

unread,
Jan 5, 2014, 11:09:39 AM1/5/14
to Paul Hankin, golang-nuts, Dave Cheney, Magnús Örn Gylfason
C# has a lambda syntax:

x => x * x 

which is the same as:

delegate(int x) { return x * x; }

the types are inferred.

Go will already convert an anonymous function:

func(x int) { return x * x; }

into the F type above, but I think the shorter syntax is only useful if you also have generics. It lets you chain methods together: (while still keeping type safety)

var results = items
    .OrderBy(item => item.LastName)
    .ThenBy(item => item.FirstName)
    .Select(item => item.BirthDate)
    .ToArray()

And that's impossible with Go right now. You can ditch the type safety, but I don't think that's good Go code anymore.

So given that this type of programming isn't currently recommended for Go, what are the cases where saving the 10-20 characters for the shorter syntax is worth it?


--

Kevin Gillette

unread,
Jan 5, 2014, 2:29:10 PM1/5/14
to golan...@googlegroups.com, Dave Cheney, Magnús Örn Gylfason
On Saturday, January 4, 2014 1:39:20 AM UTC-7, Magnús Örn Gylfason wrote:
type FuncA func(a,b int) int
type Foo struct { }
func (f Foo) FooFunc(fu FuncA) { return fu(4,5) }
fmt.Println(f.FooFunc({ return a+b }))  // <- syntax error is here

One issue with this is that func types don't need to specify parameter names (iirc, the names are even discarded during compile from func type declarations). If the declaration had been `type FuncA func(int, int) int`, then `return a+b` wouldn't have been resolvable. As proposed, it'd never get accepted even if for no other reason than it'd be limited to working with named function types; that's the first issue you need to solve (one of the others being readability). I propose some potential solutions below.

Should this have worked?

No. What you're proposing currently does not exist in the language to any degree -- all functions must be accompanied by a full function declaration.


On Sunday, January 5, 2014 3:51:08 AM UTC-7, Paul Hankin wrote:
I think needs refining, since I don't think this can't be parsed without using a symbol table. That is, F{} or F{<expr>} could be a literal struct or an anonymous function. F{A:<expr>} could be an anonymous function with a label or a literal struct with a named field A.

I agree, though I don't mind calling `F{ x = y*y }` syntactically ambiguous. Anything where the parameter names are not defined with the function, in my opinion, sacrifices too much readability. Anything where parameter types are sacrificed, in my opinion, sacrifices readability, but may be livable. A few possibilities:

Option 1: Kyle's counter-proposal without ambiguity but with implicit parameter names
  • Implicit parameter names start at a, proceeding sequentially through z. Functions with more than 26 parameters would be disallowed, though obviously more than a few would be unreasonable in practice.
  • Function body consists of an `[ ExpressionList ]` (zero or more comma-separated expressions).
  • example: `F({ a*a })(4)` evaluates to 16
  • Behaves as if the whole of the provided function body were wrapped with:
        function(a T1, b T2 /* ... */) (T3, T4 /* ... */) { return <original body> }
    The number of parameters and their types would be filled in by the compiler.

  • This has the downside of requiring the compiler to do "deep" type inference -- functions of this kind are essentially thunks at parse time, and all validation and analysis would have to wait until the enclosing context was evaluated.

Option 2: Abbreviated Syntax
  • ex: func(x int) -> x*x
  • ex: func(x int) -> (x*x, error(nil))
  • Introduce a new syntax reminiscent of normal function declarations, with a parenthesized set of inputs, and a parenthesized set of outputs (if there is only one output, the parentheses would be optional if it did not adversely affect readability). In this case, an arrow indicates that the output types and values should inferred from a parenthesized expression list.

Option 1 (along with the OP's proposal) has the downsides that the compiler would need to develop more advanced inference capabilities than it already has, and potentially significant restructuring in support of that. Also (along with the OP's proposal), it will likely be a source of many new non-comprehensive generics proposals, in this case, constant functions: `const F = { a*a }; f1 := (func(int32) int32)(F); f2 := (func(int64) int64)(F)`

Option 2 certainly isn't as short as Option 1 but it is about twice as short as current anonymous function literals, and unlike Option 1, no "deep" inference is needed -- it's just syntactic sugar, rather than semantic sugar. Also, Option 2, unlike Option 1, is resistant to API breakages producing subtle, silent bugs.
Reply all
Reply to author
Forward
0 new messages