I'm having issues using inheritable types:
https://github.com/caioariede/pum/blob/master/response.nim#L12
I think it can be related to https://github.com/Araq/Nimrod/issues/575 but not sure.
To reproduce this problem, just change TTemplateResponse to use the inheritance.
Can anyone explain to me if it's a bug or something I'm missing?
The underlying problem seems to be that copying objects by value (whether through var x = t in issue #575 or return TTemplateResponse(...) in your code) only copies the newly defined fields, but not the inherited ones.
A workaround would be to use ref object instead of object, which may be what you want anyway if you are using inheritance.
- TResponse* {.inheritable.} = object
- client*: TSocket
- TTemplateResponse* = object of TResponse
- template_name*: string
to:
- TResponse* {.inheritable.} = object
- client*: TSocket
- TTemplateResponse* = ref object of TResponse
- template_name*: string
And it now works beautifully.
I'm not very familiar with references, so I don't know how the last code worked and the first didn't.
Could you give more explanation about this behavior?
type
TPerson {.inheritable.} = object
name*: string
age: int
TStudent = ref object of TPerson
id: int
proc sayHello(person: TPerson): string =
return "Hello " & person.name
var person = TPerson(name: "Anton", age: 5)
var student = TStudent(id: 3, name: "Anton", age: 5)
echo(person of TPerson) # true
echo(sayHello(person)) # Hello Anton
echo(student of TPerson) # true
echo(sayHello(student)) # Error: type mismatch: got (TStudent)
# but expected one of:
# pum.sayHello(person: TPerson): string
If I change "ref object" back to "object" it compiles but now gives me a runtime error:
type
TPerson {.inheritable.} = object
name*: string
age: int
TStudent = object of TPerson # Without using "ref"
id: int
proc sayHello(person: TPerson): string =
return "Hello " & person.name
var person = TPerson(name: "Anton", age: 5)
var student = TStudent(id: 3, name: "Anton", age: 5)
echo(person of TPerson) # true
echo(sayHello(person)) # Hello Anton
echo(student of TPerson) # true
echo(sayHello(student)) # Hello P --- wait, what's this strange "P" ??
And if I don't have a person variable defined before student variable:
type
TPerson {.inheritable.} = object
name*: string
age: int
TStudent = object of TPerson
id: int
proc sayHello(person: TPerson): string =
return "Hello " & person.name
var student = TStudent(id: 3, name: "Anton", age: 5)
echo(student of TPerson) # true
echo(sayHello(student)) # out of memory
What you want is roughly the following:
type
Person = ref object {.inheritable.}
name*: string
age: int
Student = ref object of Person
id: int
proc sayHello(person: Person): string =
return "Hello " & person.name
var somePerson = Person(name: "Anton", age: 5)
var someStudent = Student(id: 3, name: "Benjamin", age: 5)
echo(somePerson of Person) # true
echo(sayHello(somePerson)) # Hello Anton
echo(someStudent of Person) # true
echo(sayHello(someStudent)) # Hello Benjamin
So, what's the difference between object and ref object? Objects are value types. When you assign them, pass them as an argument to a function, or return them as the result, they are duplicated (and, as it turns out, this duplication process is currently broken if the objects inherit from some other type: inherited fields are ignored). Conversely, ref object types are reference types. When you assign them, etc. only a reference to the underlying object gets passed around, while the contents themselves aren't duplicated.
More importantly, if one ref object type inherits from another, object references of the child type can be assigned to variables of the parent type. This is not cleanly possible for object types (the assignment has to discard the additional fields of the child type).
Mixing and matching ref object and object types is of limited usefulness.