Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API: Attr type for HasAttrs implementations #405

Closed
emcfarlane opened this issue Mar 7, 2022 · 1 comment
Closed

API: Attr type for HasAttrs implementations #405

emcfarlane opened this issue Mar 7, 2022 · 1 comment

Comments

@emcfarlane
Copy link
Contributor

Types that implement HasAttrs need a way to map strings to values or methods that implement Callable. The simple way to do this is with a switch:

func (benchmark) AttrNames() []string { return []string{"n", "restart", "start", "stop"} }
func (b benchmark) Attr(name string) (starlark.Value, error) {
switch name {
case "n":
return starlark.MakeInt(b.b.N), nil
case "restart":
return benchmarkRestart.BindReceiver(b), nil
case "start":
return benchmarkStart.BindReceiver(b), nil
case "stop":
return benchmarkStop.BindReceiver(b), nil
}
return nil, nil
}

The main issue with switch cases is the need to ensure all switch cases are covered by AttrNames() and vice versa.

If Attrs just link to methods the implementation can create a map of *Builtins, like how the internal types map to methods:

setMethods = map[string]*Builtin{
"union": NewBuiltin("union", set_union),
}

This creates a value to a mapping of type:
fn func(thread *Thread, fn *Builtin, args Tuple, kwargs []Tuple) (Value, error)

// https://github.com/google/starlark-go/blob/master/doc/spec.md#set·union.
func set_union(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
var iterable Iterable
if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0, &iterable); err != nil {
return nil, err
}
iter := iterable.Iterate()
defer iter.Done()
union, err := b.Receiver().(*Set).Union(iter)
if err != nil {
return nil, nameErr(b, err)
}
return union, nil
}

Maybe a new attribute type could be added to better support builtin methods and values? It would also be able to support golang native methods by handling the type casting withing the map functions.

type Attr func(v Value) Value

type Attrs map[string]Attr

// Keys returns a new sorted slice of d's keys.
func (d Attrs) Keys() []string {
	names := make([]string, 0, len(d))
	for name := range d {
		names = append(names, name)
	}
	sort.Strings(names)
	return names
}

func (d Attrs) Attr(name) (Value, error) {
	if a := d[name]; a != nil {
		return a(d), nil
	}
	return nil, nil // no such method
}

// A new type Method would need to be added to support the new function signature.
type Method struct {
    recv Value
    name string
    fn func(*Thread, string, Tuple, []Tuple) (Value, error)
}

The method signature would be changed, dropping the *Builtin arg and replacing it with fnname string like the first arg in the unpack functions. Could avoid the type casting by implementing custom Attr funcs like func(s *Set) Value but then would need to implement custom Attr() and AttrNames() methods.

As an example Set attrs could then be implemented like:

var setAttrs = Attrs{
   "union": func(v Value) Value { return NewMethod(v, "union", v.(*Set).union) },
}

 func (s *Set) union(_ *Thread, fnname string, args Tuple, kwargs []Tuple) (Value, error) { 
 	var iterable Iterable 
 	if err := UnpackPositionalArgs(fnname, args, kwargs, 0, &iterable); err != nil { 
 		return nil, err 
 	} 
 	iter := iterable.Iterate() 
 	defer iter.Done() 
 	union, err := s.Union(iter) 
 	if err != nil { 
 		return nil, nameErr(b, err) 
 	} 
 	return union, nil 
 } 
@emcfarlane emcfarlane changed the title API: improved support for builtin attribute methods and values API: Attr type for HasAttrs implementations Mar 7, 2022
@adonovan
Copy link
Collaborator

adonovan commented Mar 7, 2022

The API exposed by the starlark package is definitely oriented around the needs of an efficient interpreter implementation, rather than convenience of the application that embeds the interpreter. However, it should be possible to define convenience helpers (e.g. using reflection) outside the repository, as the https://github.com/starlight-go/starlight project does, for example. I suggest you start by experimenting with a helper function that meets your needs in your application's repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants