I'm sure this question has been asked before but I haven't found the answer yet.
Consider this code:
type
Base = ref object of RootObj
TypedExpression = ref object of RootObj
Constant[T] = ref object of TypedExpression
value: T
LessExpression[T] = ref object of TypedExpression
term1, term2: TypedExpression
method valid(e: TypedExpression): bool {.base.} = false
# Version 1
#method valid(e: LessExpression[int]): bool = true
# Version 2
method valid[T](e: LessExpression[T]): bool = true
proc newLessExpression(v1: TypedExpression, v2: TypedExpression):
TypedExpression =
LessExpression[int](term1: v1, term2: v2)
proc newConstant[T](v: T): TypedExpression =
Constant[T](value: v)
echo newLessExpression(newConstant(1), newConstant(2)).valid()
Using version 1 of the valid method does the expected thing (program emits "true"), but version 2 does not (false). I assume that the presence of the generic doesn't force the creation of the method (and insertion into a table of available methods) until it is actually instantiated.
Is there something I'm supposed to do to actually instantiate the generic? Any easy way to ensure I don't neglect instantiations (and therefore cause subtle bugs when the wrong method is called?).
Inserting
discard valid[int]
after the generic implementation actually does the trick. Is that the preferred method of making this work?I assume that the presence of the generic doesn't force the creation of the method (and insertion into a table of available methods) until it is actually instantiated.
Exactly.
Is that the preferred method of making this work?
Yes, for now. I know it's a horrible hack. The current plan is to attach a method to a single type so that type instantiation also instantiates its associated methods. This fixes most gotchas that involve generic methods.
Thanks, that's helpful! In case anyone finds this thread, the workaround I listed has some problems when multiple versions of valid exist, as valid[T] may match multiple, and
discard valid[T]
yields an error about the expression not having a value. The fix here is to do something like:
if false:
discard newLessExpression(<some valid set of correctly typed arguments>).valid()
... for each set of types one cares about.The current plan is to attach a method to a single type so that type instantiation also instantiates its associated methods.
Does that mean that multimethods are no more, and Nim becomes a single dispatch language? Where do these plans get discussed?
Does that mean that multimethods are no more, and Nim becomes a single dispatch language?
Not necessarily. I see 3 possibilities:
I favor solution (2) but I don't think it matters much since multi dispatch is quite rare.
Where do these plans get discussed?
Here and now. ;-)
1. We replace multi dispatch by single dispatch.
I favor this one. I enjoy playing with Julia, which also has a sort of multimethods, but it's a different language, and in a language like Nim, static overloading already gives some similar benefits.
I think single dispatch is simpler, so if you're willing to take the hit (breaking language change) now we get a simpler language. IMO, multi-dispatch doesn't seem to have a good power to weight ratio as a feature of Nim.
I favor solution (2) but I don't think it matters much since multi dispatch is quite rare.
I agree that MD is rare; why do you favor (2)? Is it because it's the least amount of work and is most compatible with Nim now?
I'm curious, has any decision been made? This is a good opportunity to fix what I think is a bit of a misfeature of Nim that makes it a more complex language for little practical gain.
Is anyone using multimethods heavily?
- We replace multi dispatch by single dispatch.
- We keep the multi dispatch mechanism but attach the candidate instantiation to the first object type.
- We keep the multi dispatch mechanism but attach methods to every involved generic object type.
I'm not sure that I understand (2) - if you had all generic types, would this then be the same as single dispatch (since the first type is privileged)? Maybe I'm misunderstanding what is meant by "candidate instantiation".
Count my vote for multiple dispatch :)
Not that I think it's a universal hammer, but it definitely makes implementing rule-based logic easier, and is one of the reasons (another is having a nice macro system) that I'm considering switching to Nim from C# at all :)
Also, I completely understand having this working with generics is not trivial, especially when there's no runtime representation of them, but still...