Functions

From Adam Colton
Jump to navigation Jump to search

Basics

A function takes some argument and may return a value (or multiple values).

Multiple Returns

Not sure about these, there's an elegance to only allowing a single return type. With the tagged unions, it's not necessary the way it is with go.

Implicit Attributes

Pure

A pure function has no side effects. A pure function invoked with static arguments can be statically replaced at compile time.

Unsafe libraries can override this. The use cases are logging and memoization.

Async

If a function can cause a wait operation it is async. This is implicit to avoid the "what color" problem.

Error Types

Assuming Gryphon allows only a single return, then the most common case would be (like Rust) a tagged union with errors. If the errors are specific, we can create a list of all the possible errors.

Curry Closures

I think closures are a bad idea - they are a good source of errors and complicate what data a function has access to.

Instead I want to use currying.

 const Add = <{a, b int}>-->(int){return a+b}
 c5 = Add::{a:5} // c5 is now type {int}-->(int)

Inline

The compiler should be able to make an informed decision about inlining function calls. We may want a compiler flag to help tweak the threshold (setting the desired balance between performance and size).

Of course, a function only called from one place should always be inlined.

Context Struct and Methods

A function really takes an anonymous struct as context and may attach a method to the namespace of the first argument as a method:

 func (f Foo) Bar(a,b int)

is like

 method Bar{f Foo; a,b int}

and returns just add more context

 func (f Foo) Bar(a,b int) (c int)

is

 method Bar{ctxIn {f Foo; a,b int}; ctxOut{c int}}

If we treat func args as a struct, then the order either becomes optional or entirely goes away with named

 square := <[x float]> -> {<- x*x}
 square[x:10]

then a method becomes

 Person.SetName := <[p *Person, newName string]> -> {p.Name = newName}

This could be extended to other types like arrays, so sum could be:

 const sum := <[]T/Numeric> -> T {
   sum : T
   for _,v := range ${
     sum += v
   }
   <- sum
 }

Operator Overloading

I'm not even sure I want operator overloading.

The problem is that I'm conflicted. I like a pure model where operator overloading is only permitted if the arguments and return are of the same type. But the place where I most want it is with vectors. And I like distinguishing vectors and points.

That said, if I do operator overloading, I want operator categories. For instance, it may be the case that + means either sum or concatenation. But you must choose.

Simple Model

I keep going in circles on some of the details, so I propose the following simple model as a starting point.

Function args are structs.

 func Person( name, role string, age int)

implicitly defines

 {name,role string, age int}

Functions can be curried with structs

 createAdmin = {role:"admin} @ Person // type func(name string, age int)