V: Allow interfaces to include struct fields, not just methods

Created on 28 Feb 2020  路  4Comments  路  Source: vlang/v

What and Why

At present, V's interfaces are limited like Go's in that they require unnecessary boilerplate code to be written.

The following feature request proposes to solve this problem by allowing interfaces to be defined not only in terms of methods, like

interface Namer {
    my_name() string
}

but also in terms of struct fields, like

interface Namer {
    name string  // struct field, not a method!
}

Examples

Here is a modified version of the interface example from https://vlang.io/docs#interfaces --

struct Dog { name string }
struct Cat { name string }

fn (d Dog) my_name() string {
    return d.name  // we shouldn't have to do this
}

fn (c Cat) my_name() string {
    return d.name  // ...more boilerplate...
}

interface Namer {
    my_name() string
}

fn intro(n Namer) string {
    return 'Hi, my name is $n.my_name()'
}

dog := Dog{name: 'woof'}
cat := Cat{name: 'meow'}
println(intro(dog)) // "Hi, my name is woof"
println(intro(cat)) // "Hi, my name is meow"

As in Go, we see we need to create additional boilerplate in the form of a my_name methods on Cats and Dogs. But we can do better!

Here is the solution under this proposal:

struct Dog { name string }
struct Cat { name string }

interface Namer {
    name string  // this is a struct field, not a method!
}

fn intro(n Namer) string {
    return 'Hi, my name is $n.name'
}

dog := Dog{name: 'woof'}
cat := Cat{name: 'meow'}
println(intro(dog)) // "Hi, my name is woof"
println(intro(cat)) // "Hi, my name is meow"

Plus, note how elegantly this can be combined with V's generic structs:

struct Node<T> {
    val T
    name string
    created time.Time
}

// Notice that every particular Node<T> (e.g., Node<int>,
// Node<string>, etc) will naturally implement the
// 'NameTimer' interface(!), _and_ everything else with
// a `name string` and a `created time.Time` will, too

interface NameTimer {
    name string
    created time.Time
}

struct User {
    id i64
    name string
    email string
    created time.Time
    modified time.Time
}

fn node_namer(n NameTimer) string {
    return 'I am "$n.name", created at $n.created, and of type ' + typeof(n)
}

fn main() {
    int_node := Node<int>{1, 'one', time.now()}
    str_node := Node<string>{'2', 'two', time.now()}
    user := User{id: i64(3), name: 'Alex', email: '[email protected]', created: time.now()}

    println(node_namer(int_node))  // 'I am "one", created at 2020-02-27 21:15:08, and of type Node<int>'
    println(node_namer(str_node))  // 'I am "two", created at 2020-02-27 21:15:08, and of type Node<str>'
    println(node_namer(user))  // 'I am "Alex", created at 2020-02-27 21:15:08, and of type User'
}

More Benefits

By eliminating needless boilerplate code that only complicates the writing of V code, we can make V more fun and more powerful _without_ making it significantly more complex.

I have taught Go to several people, and they all expected interfaces to support this functionality, and they don't, forcing them to write 1-line wrapper methods. Plus, adding this feature will allow us to translate from V to TypeScript much more easily, since this is basically a simplified subset of TypeScript interfaces.

Structs Only

This proposal indeed means that non-struct-based types cannot implement interfaces that contain fields. But 99% of user-specified types are struct-based anyway, and it would be odd for a user to want something like type MyInt int to implement an interface meant for completely different kinds of objects anyway.

Conclusion

Given that this proposal solves a real problem, complies with this comment from @BlackjackZ94 that @medvednikov said he agrees with the spirit of (just below that comment itself), uses familiar syntax, and given that this feature is very much in the spirit of V, I urge Alex to accept this feature request!

Thank you.

Feature Request

Most helpful comment

Yes, this will be implemented, like in TypeScript.

All 4 comments

I remember Alex giving this a while back in the discord server and also why to allow only struct? Couldn't types like int, float, byte also be added to fields? Will there be a drawback if we added types as well?

EDIT: link to the proposal by Alex.

Yes, this will be implemented, like in TypeScript.

@Delta456 the proposed feature means that the type that implements the interface will have to have all of the fields of the interface, or it will not satisfy the interface.
Primitive types like int, f32 and so on do not have fields => they will not satisfy such interfaces.
NB: the proposed feature does not restrict using the primitives in the interface fields themselves.

@spytheman Oh I meant that using the primitives in the interface fields themselves and thanks for answering, really grateful!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

radare picture radare  路  3Comments

choleraehyq picture choleraehyq  路  3Comments

jtkirkpatrick picture jtkirkpatrick  路  3Comments

elimisteve picture elimisteve  路  3Comments

arg2das picture arg2das  路  3Comments