V: reflection

Created on 23 Mar 2019  路  23Comments  路  Source: vlang/v

will it have reflection ? if yes is it the same as go?

Feature Request Discussion

Most helpful comment

The syntax I have in mind:

fn decode<T>(data string) T {
        mut result := T{}
        for field in T.fields {
                if field.typ == 'string' {
                        result.$field = get_string(data, field.name)
                } else if field.typ == 'int' {
                        result.$field = get_int(data, field.name)
                }
        }
        return result
}

// generates to:

fn decode_User(data string) User {
        mut result := User{}
        result.name = get_string(data, 'name')
        result.age = get_int(data, 'age')
        return result
}

What do you think?

All 23 comments

I'm not sure yet.

Reflection is slow and can result in runtime errors.

I think I'll implement it with codegen, just like json decoding.

if it would be something like c#'s it would be awesome,
like just adding an attribute to a func then take it from reflection to do stuff with it, it would be time saver in making API like

[url="/api-end-point"]
fn someFunction(w http.ResponseWriter, r *http.Request) {}

I could see value in static reflection or a compiler API at some point. @medvednikov As you point out, this could generalize your json support (which some people won't want to use anyway, as more efficient serialization methods exist). But I agree that runtime reflection is at odds with V as I understand it today.

@tjpalmer Yes, compile-time reflection would be great. I've posted some thoughts on possible native V JSON decoding in a comment here.

The syntax I have in mind:

fn decode<T>(data string) T {
        mut result := T{}
        for field in T.fields {
                if field.typ == 'string' {
                        result.$field = get_string(data, field.name)
                } else if field.typ == 'int' {
                        result.$field = get_int(data, field.name)
                }
        }
        return result
}

// generates to:

fn decode_User(data string) User {
        mut result := User{}
        result.name = get_string(data, 'name')
        result.age = get_int(data, 'age')
        return result
}

What do you think?

@HedariKun about your suggestion.

vweb framework will have the following syntax:

['/post/:id']
fn (a App) show_post(id int) vweb.Result {
  post := a.posts_repo.retrieve(id) or {
     return vweb.not_found()
  }
  return vweb.view(post)
}

decode looks good, I think it needs to use something like result.$(field.name) = though. Perhaps local const values could be allowed:

        const fields = T.fields
        for field in fields {
                const id = field.name
                if field.type == 'string' {
                        result.$id = get_string(data, field.name)
                } else if field.type == 'int' {
                        result.$id = get_int(data, field.name)
                }
        }

I renamed typ to type, I assume that was a typo ;-)
For structs with fields of struct type we can recurse:

else if is_uppercase(field.type[0]) {
  const type = field.type
  result.$id = decode<$type>(data)
}

For arrays we can check field.type[0:2] == '[]' and then handle the element type like above.

We could call $identifier a string identifier. Compile-time for and if could be called inline. I think inline if shouldn't create a scope, so the body can declare variables for use after the if block - this is like D's static if. Maybe inline for and if should look different to their runtime versions.

type is a keyword, just like in Go, so typ has to be used instead.

V can be smarter with struct fields:

else {
  result.$field = decode(data)
}

Compile-time coding is tough, I want to make it as simple and light as possible.

Maybe to make this more consistent and simpler:

if field is string {
  result.$field = get_string(data, field)
} else if field is int {
  result.$field = get_int(data, field)
} else if field is struct {
  result.$field = decode(data)
}

Even better:

result.$field = match field {
  string => get_string(data, field)
  int    => get_int(data, field)
  struct => decode(data)
}

the later syntax seems better, also it is nice that the vweb will have that type of syntax but wouldn't it be good if we can make our own, like having ['something'] can also be used when creating other stuff, maybe discord bot and you specify the command name between [] or some other custom thing.

but wouldn't it be good if we can make our own

[attr] is a method attribute that can be defined by developers for any method and accessed via reflection.

oh neat~

I think requiring $if and $for for compile time code makes the code clearer for the compiler (giving better errors) and user:

$for field in T.fields {
  $if field is SomeType {

Let's define what's happening. Here I'd call field a symbol alias as it's not a value. $field means use the field name as an identifier token in code.

For ident is Type I think ident can be a variable or type. Other than with is I suggest we use type(field) to clearly get the field type, and something like field.stringof to get a string of the field name:

  result.$field = get_string(data, field.stringof)
result.$field = decode<type(field)>(data)
result.$field = $match type(field) {

See also #205 for some more compile time code generation.

field.stringof

Of course this could be field.name as in the current docs. Allowing field alone to get a string could be ambiguous if field is passed as a template argument, depending on what we allow in future to be passed as template arguments (see string template arguments in my referenced issue above).

The reason I prefer type(expr) over field.type is because expr can be a more complex expression than just an identifier, e.g. a + b. This can be useful in template code.

I agree, compile time if, for, match should be distinguishable. $if, $for, $match seems to be fine.

What I would like from meta functionality in compile time:

  • ability to enumerate source files, with their attributes
  • ability to enumerate all top level items in source files
  • ability to enumerate structure members and statements in functions, hierarchically, scope by scope
  • API which says "this item is used by these other items", like who is calling my function

This would allow me to implement e.g. this:

  1. Application specific tools to verify validity the code, e.g. each foo call has to be followed by reverse-foo inside the scope. This specific example could be perhaps handled in other way (I'd use RAII in C++), but the checks often go beyond such simple scenario.
  2. My own test runner. E.g. I would like to run only tests from recently modified source files, not everything.

Update:
typeof() has already been implemented in v2 backend. Generics are now being stabilized in the new backend and there are now discussions on Discord about implementing interface{} (aka any).

Here I propose some new compile-time features for reflection and update the docs: https://github.com/vlang/v/pull/5962

I thumbs-downed this feature request, unless someone can explain why repeating Go鈥檚 design error will not be a problem for Vlang?

Runtime reflection in Go ostensibly was a design error that continues to have egregious implications they apparently did not contemplate when it was designed into the language:

  1. The JavaScript transpiler GopherJS thus can鈥檛 safely do dead code elimination, thus bloated JavaScript output.

  2. The new generics proposal is apparently handicapped by an inability to have type parametrization in methods presumably because of reflection.

@shelby3 like @ntrel mentioned we will have compile time reflection, not runtime reflection like in Go.

Actually we already have some of it working.

Was this page helpful?
0 / 5 - 0 ratings