I would like to have a way to group certain types together, for example, for the usage of interface in other languages.
type ICanFly = ref object of RootRef
type Bird = ref object of ICanFly
type Fly = ref object of ICanFly
var flies :seq[ICanFly] = @[ Bird(), Fly()]
but in this case, ICanFly can be instantiated. (final pragma didn't seem to work!)
flies.add ICanFly()
a plus would be to avoid the inheritance mechanism:
type
ICanFly = concept flier
flier.numWings is int
Bird = object
numWings: int
Unfortunately concepts instantiation cannot be stored in the same heterogenous container at the moment, which was the plan 3.5 years ago: https://github.com/nim-lang/Nim/blame/devel/doc/manual_experimental.rst#L847.
That said there is a number of third-party libs for interfaces, the most promising nowadays likely being https://github.com/yglukhov/iface
Thank you for the replies, yes, basically concepts that are available at runtime and heterogeneous containers plus a form of nominal typing will fulfill what I wanted. sometimes you want to limit and have compiler support for the limitation, it helps as a design tool. Here for example, I would like to specify Bird matches ICanFly even in the case that ICanFly has no fields or procs. basically, add an ICanFly tag to Bird. this is very obvious to java/c# programmers.
I have tried your suggestion of iface lib. it comes close, but doesn't support a seq[ISomeInterface].
Is it possible to prevent instantiation of a type? based on the docs, I figured the final pragma shuold, but it doesn't work. at least instantiation of the default constructor SomeType()
it comes close, but doesn't support a seq[ISomeInterface].
Could you please clarify how it doesn't support it? In iface an interface is a valid type, it can be stored anywhere, in a seq or anything else. The following code works:
import iface
iface Animal:
proc say()
type Dog = ref object of RootRef
proc say(d: Dog) = echo "bark"
var s: seq[Animal]
s.add(Dog())
Thanks for iface, it loos really cool!
I have a couple of questions:
Thanks
Always welcome)
Do you plan on publishing it in Nimble?
Sure, just not sure if I like the erm... interface of it). iface name is awkward, and definition syntax doesn't look as natural nim as I want it to. And there's a nim issue which makes ifaces close to unusable unless you compile in a while loop :D
Would it be possible to have closures (esp. async ones) in interfaces?
Interfaces don't have "fields" if that's what you're asking, but fields can always be replaced with accessors. They might gain support for fields in the future, but that would be just a syntactic sugar to myField() and myField=() accessors, which you can already do manually.
As for async, I don't see how it is not supported currently. Async procs are just those that return Future[T], interfaces don't care.
As for async, I don't see how it is not supported currently. Async procs are just those that return Future[T], interfaces don't care.
Of course you're right, something's got in my head! Thanks :)
And there's a nim issue which makes ifaces close to unusable unless you compile in a while loop :D
Interesting, didn't know about this one
I also tried to port the manual methods example (https://nim-lang.org/docs/manual.html#methods) to iface but this fails:
import iface
iface Expression:
proc eval(): int
type
Literal = ref object of RootRef
x: int
PlusExpr = ref object of RootRef
a, b: Expression
proc eval(e: Literal): int = e.x
proc eval(e: PlusExpr): int =
result = eval(e.a) + eval(e.b)
proc newLit(x: int): Literal =
Literal(x: x)
proc newPlus(a, b: Expression): PlusExpr =
PlusExpr(a: a, b: b)
echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
Did I do something wrong here?
Interesting. This seems to fix it:
echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4).to(Expression)))
Here's a reduced sample for you to submit to somewhere... ;)
type
Expression = ref object
Literal = ref object
PlusExpr = ref object
converter toExpression[T](v: ref T): Expression = nil
proc newLit(x: int): Literal = discard
proc newPlus(a: Expression; b: Expression): PlusExpr = discard
discard newPlus(newPlus(newLit(1), newLit(2)), newLit(4).Expression) # Works
discard newPlus(newPlus(newLit(1), newLit(2)), newLit(4)) # Doesn't work
I'm not on a dev environment right now, but earlier something like this didn't seem to work:
import iface
iface Animal:
proc say()
type Dog = ref object of RootRef
type Dog2 = ref object of RootRef
proc say(d: Dog) = echo "bark"
proc say(d: Dog2) = echo "bark"
var s: seq[Animal] = @[ Dog(), Dog2() ]
That's because:
echo type(@[Dog(), Dog2()])
is seq[RootRef], and RootRef doesn't implement Animal. Similar case here. You can hint nim by coercing first element in the literal to correct type:
var s: seq[Animal] = @[ Animal(Dog()), Dog2() ]