Closed tulayang closed 3 years ago
The code you showed can be easily achieved by generics, so I guess all you want is some syntax sugar to make it a little less verbose?
And for generic ptr
we already have pointer
The "Motivation" is lacking and unfortunately I don't understand the purpose of this RFC.
For example:
type
TaskBase = object of RootObj
Task[T] = object
data: T
run: proc (task: ref TaskBase)
Queue = object
data: seq[ref TaskBase]
proc add(queue: Queue, task: ref TaskBase) =
queue.data.add(task)
proc exec(queue: Queue) =
for d in queue.data:
d.run(d)
Works in today's Nim and is common too. Type erasure is easy enough to do in Nim.
type
Task[T] = object
data: T
run: proc (task: ref Task[?])
Queue = object
data: seq[ref Task[?]]
proc add(queue: Queue, task: ref Task[?]) =
queue.data.add(task)
proc exec(queue: Queue) =
for d in queue.data:
d.run(d)
can be written
type
Task[T, U] = object
data: T
run: proc (task: ref Task[U])
Queue[U] = object
data: seq[ref Task[U]]
proc add(queue: Queue, task: ref Task) =
queue.data.add(task)
proc exec(queue: Queue) =
for d in queue.data:
d.run(d)
@Araq
Yeah. I know this solution. And in my code, this compromise is now adopted. I hope to achieve this goal without object of
. I think object of
has two limitations:
TaskBase
and Task
The solution provided by @mratsim looks nice, but it cannot be compiled:
type
Task[T] = object
data: T
run: proc (task: ref Task[T])
Queue[T] = object
data: seq[ref Task[T]]
proc initQueue(): Queue =
# Error: cannot instantiate: 'Queue[T]'; Maybe generic arguments are missing?
result.data = @[]
var queue = initQueue()
Motivation: if the elements in seq
is ref T
type, then T
can be ignored when seq[ref T]
is instantiated. Like this:
type
Task[T] = object
data: T
run: proc (task: ref Task[T])
Queue = object
data: seq[ref Task[?]]
proc initQueue(): Queue =
result.data = @[]
var queue = initQueue()
var a: seq[ref ?] = @[]
var a: seq[ptr ?] = @[]
var a: seq[ref Task[?]] = @[]
var a: seq[ptr Task[?]] = @[]
# similar to pointer
var a: seq[pointer] = @[]
The error is pretty clear though, you're missing a generic argument.
type
Task[T] = object
data: T
run: proc (task: ref Task[T])
Queue[T] = object
data: seq[ref Task[T]]
proc initQueue(T: typedesc): Queue[T] =
discard
var queue = initQueue(int)
echo queue.data.len
@mratsim
This (initQueue(int)
) only allows integers (ref Task[int]
). However, I assume that ref Task[int]
, ref Task[string]
, ref Task[A]
and ref Task[B]
are allowed to be stored in a same queue.
Task.data
is a user data, the Queue
has nothing to do with it and does'nt care its specific value.
If we extend the example slightly we can see some problems:
type
Task[T] = object
data: T
run: proc (task: ref Task[?])
Queue = object
data: seq[ref Task[?]]
proc add(queue: Queue, task: ref Task[?]) =
queue.data.add(task)
proc pop(q: Queue): ref Task = discard "..."
let x = Task[string](q.pop()) # how to check if this conversion is valid?
# it requires a runtime check. How we can perform this check? Well with
# runtime type information. Now if only we inherited from some base that
# would be available...
# Or if queue wouldn't have lost type information we could statically check that...
doesn't allow multiple inheritance
So what, type erase remains possible, it's good enough.
adds redundant abstraction: TaskBase and Task
"Redundant" abstractions are better than adhoc rules that break the type system in unforeseen ways. There is nothing "redundant" here anyway, what you really mean is inconvenient.
Task.data is a user data, the Queue has nothing to do with it and doesn't care its specific value.
The type system cares. Nim is not Java where every single type is Boxed and derives from a Root object.
Ideally this would be possible with the introduction of VTable for concept and creating a concept called Any
. Or use what Araq's suggested.
If it's too cumbersome you can write a template to automate that in a one-liner.
Sorry but this has a chance of 0% of being adopted.
Motivation
When creating a container or collection, the specific type of a
ref
or aptr
element does not need to be specified.Description
In java, the question mark (
?
) is a wildcard to represent an unknown type. You can use a wildcard to relax the restrictions on a variable.This can be very useful especially when creating a container or collection.
Examples
For example, define a queue for dispatching tasks: