#ex1
proc test1(T : typedesc[SomeNumber] = typedesc[float]) : T =
echo "type is " & $T
return T(0)
#works
echo test1(int)
#ex2
type
SomeObject[T] = object
val : T
proc test2(T : typedesc = typedesc[float]) : SomeObject[T] =
echo "type is " & $T
return SomeObject[T](val : default(T))
#works
echo test2(int)
#ex3
proc test3(T : typedesc[SomeNumber] = typedesc[float]) : SomeObject[T] =
echo "type is " & $T
return SomeObject[T](val : default(0))
#doesn't work: " cannot instantiate: 'SomeNumber' "
echo test3(int)
Why doesn't the third example work? It looks like the compiler cannot instantiate the return type SomeObject[T] from a typedesc that's declared as T : typedesc[SomeNumber]. Otherwise, just returning T, as it's the case of the first example, works. Also, the second example shows how a fully open interface, with T : typedesc, seems to work for objects aswell.
@vitreo:
As to your use in example three, the difference (and the reason for the bug, if bug it is) may be the more complex inheritance in returning an object.
One can get the effect of specifying that "SomeObject[T]" must contain a type of "SomeNumber" by the following code, which might be considered a workaround:
type
SomeObject[T] = object
val: T
#ex3
proc test3(T: typedesc = typedesc[float]) : SomeObject[T] =
when T isnot SomeNumber: {.fatal: "type must be SomeNumber!!!".}
echo "type is ", $T
# return SomeObject[T](val : default(T)) # redundant
# works
echo test3(); echo test3(int)
# shouldn't work and doesn't
# echo test3(pointer)
Note that it would be better to use "default(T)" (not default(0)) in your return statements but that the whole return statement is redundant in the case of pure non-ref objects as the return value will already just be the default value for the type. Thanks for the answer. I tried your example and it indeed works, even though I would prefer the simpler syntax of T : typedesc[SomeNumber] = typedesc[float] to work. In any case, I also came across this other error, when trying to limit the T inside the SomeObject definition:
type
SomeObject[T : SomeNumber] = object
val: T
#ex3
proc test3(T: typedesc = typedesc[float]) : SomeObject[T] =
when T isnot SomeNumber: {.fatal: "type must be SomeNumber!!!".}
echo "type is ", $T
#doesn't work
test3()
The error is:
Error: cannot instantiate SomeObject
got: <typedesc>
but expected: <T: SomeNumber>
It appears that typedesc would not work with any T : Something in an object definition.
My take at limiting the T inside the SomeObject definition
type
SomeObject[T : SomeNumber] = object
val: T
#ex3
template test3(typ: typedesc = float) : untyped =
echo "type is ", $typ
let res = 10.typ
SomeObject[typ](val: res)
#doesn't work
echo test3(pointer)
#works
echo test3()
echo test3(int)
echo test3(float)
echo test3(byte)
@:
You can make your code trying to limit the contained type T inside your "SomeObject" container to SomeNumber successfully compile by the following, which forces the typedesc to appear to the compiler as a type; however, as shown, it doesn't accomplish what you desire in limiting the type to only numbers:
type
SomeObject[T : SomeNumber] = object
val: T
#ex3
proc test3(T: typedesc = typedesc[float]) : SomeObject[T.typeof] =
echo "type is ", $T
# works, but shouldn't
echo test3(pointer)
I think this doesn't constrain the type properly as documented in the manual on Generic Inference Restrictions as "typedesc[T] cannot be inferred in a generic instantiation. ". I think that typedesc is intended to be used more with custom templates and macros where this limitation isn't as much of a problem. Remember that a generic proc is actually automatically creating a template that instantiates the proc for the specific type used at compile time but this automatically generated template for generics is limited as specified in the manual. Due to the above restriction, the combination of regular default proc parameter behaviour and this "template" behaviour conflicts enough to interfere with the more complex type resolution in your example 3 - it's not a bug, it's a restriction.
@lucian''s approach using a template thus works, of course, because template's are processed at compile time and his template is doing something like what I suggesting in my first post in using when, which also makes changes at compile time.
So, the short answer is: use compile time restrictions as in when or by using templates or macros (if you need to step up that far) to do anything more than the basic described short cuts in specifying typedesc in proc's.
And one step further from @lucian's template, returning procs:
type
SomeObject*[T: SomeNumber] = object
val : T
#ex3
proc test3impl[T : SomeNumber]: SomeObject[T] =
# all actual code here
echo "type is " & $T
template test3*(T : typedesc[SomeNumber] = float) : untyped =
# a dispatcher: just calling the proc
test3impl[T]()
#doesn't work
#echo test3(pointer)
#works
echo test3()
echo test3(int)
echo test3(float)
echo test3(byte)