type
Foo[T] = object
x:T
Bar[T,R] = Foo[T]
Baz = Bar[int,float]
proc qux[T,R](x: Bar[T,R]) = discard
var b:Baz
b.qux()
fails to compile with "cannot instantiate 'R'"
a workaround is Bar[T,R] = distinct Foo[T], which is awkward for my api.
What is going on here? As I trawled through issues trying to find a similar one, I started wondering if maybe it's expected behaviour? For example this RFC about bind once vs bind many is that what's going on? I have no clue.
I dont mind if I can add this quirk to my mental model of how to navigate edge cases in Nim, but I don't know how to even think about why this doesn't work.
Even though generic aliases allow attaching type information if that information is not used they make no sense to use. type A[T] = string for instance all A[T] are the same and you cannot dispatch on A[int] or A[string] as the first one is string and the second is string. The only case that generic aliases make sense in the present type system is when the parameters are used. I do personally want a 'weak distinct' type that allows implicit conversion to the base but also is a distinct type sorta akin to
type Bar[T, R] {.borrow: `.` = distinct Foo[T])
converter toBase[T, R](bar: Bar[T, R]): lent Foo[T] = Foo[T](bar)
converter toBase[T, R](bar: var Bar[T, R]): var Foo[T] = Foo[T](bar)
i didn't know about {.borrow:'.'.} thanks for that. unfortunately it doesn't work through an alias:
type
Bar[T,R]{.borrow:`.`.} = distinct Foo[T]
Baz = Bar[int,float] ## Error: only a distinct type can borrow '.'
tbh, this is why i dont muck around with borrow, every time i try it, i hit bug/edge case almost immediately.
in my case, i've discovered i'm fine; Foo[T] is a union, so i need accessor funcs to do the right thing at compiletime, anyway.
Your explanation that you cant dispatch on extra parameters in an alias makes a lot of sense. sounds like i'll have to get a handle on distinct.
Something like this works, but i dunno how robust it is:
type
Foo[T] = object
x:T
Bar[T,R]{.borrow:`.`.} = distinct Foo[T]
template makeBar(t,r:typedesc):type =
type Internal{.borrow:`.`.} = distinct Bar[t,r]
converter toBar(b:Internal):Bar[t,r] = Bar[t,r](b)
converter toBar(b:var Internal): var Bar[t,r] = Bar[t,r](b)
Internal
type
Barr = makeBar(int,float)
Bazz = makeBar(int,float)
proc foo(f:Barr):string = "Barr"
proc foo(f:Bazz):string = "Bazz"
proc qux(f:Bar):string = $f.typeof.T & $f.typeof.R
proc plugh(f:var Bar,i:int) = f.x = i
var
b:Barr
c:Bazz
echo typeof(b),typeof(c)
#Internal'gensym0,Internal'gensym1
#which could be better i suppose
echo b.x,c.x
#0 0
echo b.qux,c.qux
#int,float int,float
echo b.foo,c.foo
#Barr Bazz
it has the limitation that you can't create a type in an internal scope (converters can only be created at top scope)
and the var converter doesn't work
type X = makeBar(int,float)
var x:X
x.plugh(5) #expression x is immutable, not var
converter sudoToBar(x:var X):var Bar[int,float] = Bar[int,float](x)
x.plugh(5) #still no
It occurs to me we can use inheritance instead of distincts here to get a desirable outcome.
type
Foo[T] = object of RootObj
x:T
Bar[T,R] = object of Foo[T]
Barr = object of Bar[int, float]
Bazz = object of Bar[int, float]
proc foo(f:Barr): string = "Barr"
proc foo(f:Bazz): string = "Bazz"
proc qux[T, R](f:Bar[T, R]): string = $T & $R
proc plugh(f:var Bar,i:int) = f.x = i
var
b:Barr
c:Bazz
echo typeof(b), " ", typeof(c)
echo b.x, " ", c.x
echo b.qux(), " ", c.qux()
echo b.foo,c.foo
#Barr BazzzR
The solution I'm using right now is to add an unused counter static int parameter to Bar, and a CacheCounter that's incremented in makeBar it makes 2 makeBar(int,float)`s distinct/dispatchable, but of course they are both `Bar so all of Bar's api works
This was straightforward only because my Bar has so many parameters already, and none get exposed to the user; and because Bar is created with a macro anyway.
ok, it does work (https://github.com/nim-lang/Nim/issues/16653) on 1.6.12:
type
Foo[T]{.pure.} = object {.inheritable.}
x:T
Bar[T,R] = object of Foo[T]
on devel
type
Foo[T]{.pure,inheritable.} = object
x:T
Bar[T,R] = object of Foo[T]
OP, have you tried this? This seems to work.
type
Foo[T] = object
x:T
Bar[T,R] = Foo[T]
Baz = Bar[int,float]
proc qux(x: Bar) = discard
var b:Baz
b.qux()