Jump to content

  • Log In with Google      Sign In   
  • Create Account

- - - - -

Type inference and anonymous returns

Posted by , 26 December 2010 · 157 views

It's been a productive holiday for Epoch!

In addition to adding generalized support for anonymous temporary constructors as well as overloaded constructors, I've added some nice convenience features. Specifically, it is now possible to return anonymous expressions directly from functions, thanks to some hefty upgrades to the type inference capabilities of the compiler.

For example, it used to be necessary to write code like this:

increment : (integer(value)) -> (integer(ret, value + 1))

Even worse, in previous versions of Epoch, it wasn't possible to directly compute expressions in the return value initializer, so you had the more cumbersome version:

increment : (integer(value)) -> (integer(ret, 0)) { ret = value + 1 }

This is all superfluous now. Although function parameters typically still require type annotations, much of the rest of the syntax can be elided entirely. This permits nice, compact definitions for trivial functions:

increment : (integer(value)) -> (value + 1)

Again, type inference plays heavily into this, and it is now necessary to run the compiler across the code several times to complete all inference passes. This leads to a minor increase in build times but at the benefit of much cleaner code. I may find a more intelligent way to do inference later as well, which might reduce the need for extra inference passes, or better, localize the passes so the compiler doesn't spend time recompiling code that doesn't need any more work done on it.

Pattern matching on function parameters makes this even more powerful. For instance, I can write a program like the following:

returnstring : (string(param)) -> (param)
returninteger : (integer(param)) -> (param * 2)

fib : (0) -> (1)
fib : (1) -> (1)
fib : (integer(n)) -> (integer(ret, fib(n - 1) + fib(n - 2)))

entrypoint : () -> ()
debugwritestring(cast(string, returninteger(21)))
debugwritestring(cast(string, fib(5)))

Note that I still have to explicitly provide a return value for the general-case overload of fib. This is because, in Epoch, overloads can differ by only their return types; so the type inference engine can't tell if fib returns an integer or a real or what have you.

I may be able to improve this in the future by adding a simple heuristic: if no overloads differ by return type, a recursive pattern-matched function can be inferred to return the same type as all the other overloads. This would eliminate the need to specify a return variable for the general case of fib above, making things even more compact:

fib : (integer(n)) -> (fib(n - 1) + fib(n - 2))

There are a few cases where inference will fail entirely and get stuck in an infinite loop; I need to add some safety checks to make sure the inference doesn't try to run more than a sane number of passes over the code, or at least halts if it stops making forward progress.

I have only a handful of items left to do for Release 11, and then I'll be considering when to kick it out the door. Stay tuned - this will be a big one!

August 2016 »

28 293031