Hello, I have some problems with generics.
I have four similar situations in which only one can succeed.
Failed case one: https://play.nim-lang.org/#ix=2r7Q
type
Must*[U: object; T: object | ref object] = object
impl*: U
data*: T
Animal[T] = object
Cat = object
template must(a, b: typed): untyped =
Must[a[b], b]
var a: must(Animal, Cat)
# failed
proc initZ[T](must: Must[Animal[T], T]) =
discard
Failed case two: https://play.nim-lang.org/#ix=2r7R
type
Must*[U: object; T: object | ref object] = object
impl*: U
data*: T
Animal[T] = object
Cat = object
template must(a, b: typed): untyped =
Must[a[b], b]
var a: must(Animal, Cat)
# failed
proc init[T](must: must(Animal, T)) =
discard
Failed case three: https://play.nim-lang.org/#ix=2r7S
type
Must*[U; T: object | ref object] = object
impl*: U
data*: T
Animal[T] = object
Cat = object
template must(a, b: typed): untyped =
Must[a[b], b]
var a: must(Animal, Cat)
# failed
proc init[T](must: must(Animal, T)) =
discard
Successive Case: https://play.nim-lang.org/#ix=2r7U
type
Must*[U; T: object | ref object] = object
impl*: U
data*: T
Animal[T] = object
Cat = object
template must(a, b: typed): untyped =
Must[a[b], b]
var a: must(Animal, Cat)
# succeded
proc initZ[T](must: Must[Animal[T], T]) =
discard
initZ[Cat](must = Must[Animal[Cat], Cat]())
What do you want to code? Can you express it in English? Perhaps using generics is not the best way to express it.
Or do you want explicitly to use and learn generics?
Here are complete codes which can succeed to compile.
import strformat
import ../src/shene/mcall
# Constructor injection
type
Animal*[T] = object
sleepImpl: proc (a: T) {.nimcall, gcsafe.}
barkImpl: proc (a: T, b: int, c: int): string {.nimcall, gcsafe.}
danceImpl: proc (a: T, b: string): string {.nimcall, gcsafe.}
Phone*[T] = object
playImpl: proc (a: T) {.nimcall, gcsafe.}
Cat* = object
cid: int
Iphone* = object
pid: int
People*[T, U] = object
id: int
pet: must(Animal, T)
phone: must(Phone, U)
proc sleep*(a: Cat) =
discard
proc bark*(a: Cat, b: int, c: int): string =
result = fmt"{a.cid + b + c}"
proc dance*(a: Cat, b: string): string =
result = fmt"{b}"
proc play*(a: Phone) =
echo "Hello, the phone is playing the music."
proc play*(a: Iphone) =
echo "Hello, the iphone is playing the music."
proc initCat*(cid: int): must(Animal, Cat) =
result.cid = cid
result.sleepImpl = sleep
result.barkImpl = bark
result.danceImpl = dance
proc initPhone*(pid: int): must(Phone, Iphone) =
result.pid = pid
result.playImpl = play
proc initPeople*[T, U](pet: Must[Animal[T], T], phone: Must[Phone[U], U]): People[T, U] =
result.id = 2333
result.pet = pet
result.phone = phone
proc prepare(p: People[Cat, Iphone]) =
doAssert p.pet.call(barkImpl, 13, 14) == "40"
p.pet.call(sleepImpl)
doAssert p.pet.call(danceImpl, "Hello") == "Hello"
doAssert p.pet.cid == 13
p.phone.call(playImpl)
doAssert p.phone.pid == 2333
let pet = initCat(13)
let phone = initPhone(2333)
let p = initPeople[Cat, Iphone](pet, phone)
prepare(p)
@xflywind in your simple example, you could use concepts like so:
type
Animal* = concept a
a.id is int
a.sleep()
a.bark(int, int) is string
a.dance(string) is string
People*[T] = object
pet: T
# User defined type
Dog* = object
id: int
# User defined procs
proc sleep*(d: Dog) =
discard
proc bark*(d: Dog, b: int, c: int): string =
result = $(d.id + b + c)
proc dance*(d: Dog, b: string): string =
result = b
proc newDog*(id: int): Dog =
result.id = 1314
let people = People[Dog](pet: newDog(12))
doAssert people.pet.bark(12, 14) == "1340"
doAssert people.pet.dance("dancing") == "dancing"
@jyapayne Thanks! I did't know generics are late binding.
This works as what I hope:
type
Animal* = concept a
a.id is int
a.sleep()
a.bark(int, int) is string
a.dance(string) is string
People*[T: Animal] = object
pet: T
# User defined type
Dog* = object
id: int
# User defined procs
proc sleep*(d: Dog) =
discard
proc bark*(d: Dog, b: int, c: int): string =
result = $(d.id + b + c)
proc dance*(d: Dog, b: string): string =
result = b
proc newDog*(id: int): Dog =
result.id = 1314
proc pay[T](p: People[T]) =
doAssert p.pet.bark(12, 14) == "1340"
doAssert p.pet.dance("dancing") == "dancing"
let d = newDog(12)
pay[Dog](People[Dog](pet: d))
What I can understand that you need the lib's user should implement their own proc implementation right?
Actually tables modules exactly does like that, when the user wants to use a specific object as the key, should implement hash and == so the object can be used as key.
It's also catch-able during compile-time.
templates are also late binding. we can achieve similar goal using it. But it is not clear to see the signature of function declaration. I think interfaces should be placed together.
template genHashImpl(key, hc: typed) =
hc = hash(key)
if hc == 0: # This almost never taken branch should be very predictable.
hc = 314159265 # Value doesn't matter; Any non-zero favorite is fine.