mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-01 13:09:14 +00:00
I don't like it, but seems like this is correct. Concept type classes have to behave like other "named" type classes and participate in "bind once" mechanics or require some weird semantics. As a side note I'm pretty sure the `tuple` example in the manual explaining this is either wrong now or has regressed, but I don't think it matters because I doubt anyone thinks about this feature much. #25778
617 lines
13 KiB
Nim
617 lines
13 KiB
Nim
discard """
|
|
action: "run"
|
|
output: '''
|
|
B[system.int]
|
|
A[system.string]
|
|
A[array[0..0, int]]
|
|
A[seq[int]]
|
|
100
|
|
a
|
|
b
|
|
c
|
|
a
|
|
b
|
|
c
|
|
1
|
|
2
|
|
5
|
|
test
|
|
'''
|
|
"""
|
|
import conceptsv2_helper
|
|
|
|
block: # issue #24451
|
|
type
|
|
A = object
|
|
x: int
|
|
B[T] = object
|
|
b: T
|
|
AConcept = concept
|
|
proc implementation(s: var Self, p1: B[int])
|
|
|
|
proc implementation(r: var A, p1: B[int])=
|
|
echo typeof(p1)
|
|
|
|
proc accept(r: var AConcept)=
|
|
r.implementation(B[int]())
|
|
|
|
var a = A()
|
|
a.accept()
|
|
|
|
block: # typeclass
|
|
type
|
|
A[T] = object
|
|
x: int
|
|
AConcept = concept
|
|
proc implementation(s: Self)
|
|
|
|
proc implementation(r: A) =
|
|
echo typeof(r)
|
|
|
|
proc accept(r: AConcept) =
|
|
r.implementation()
|
|
|
|
var a = A[string]()
|
|
a.accept()
|
|
|
|
block:
|
|
type
|
|
SomethingLike[T] = concept
|
|
proc len(s: Self): int
|
|
proc `[]`(s: Self; index: int): T
|
|
|
|
A[T] = object
|
|
x: T
|
|
|
|
proc initA(x: SomethingLike): auto =
|
|
A[type x](x: x)
|
|
|
|
var a: array[1, int]
|
|
var s: seq[int]
|
|
echo typeof(initA(a))
|
|
echo typeof(initA(s))
|
|
|
|
block:
|
|
proc iGetShadowed(s: int)=
|
|
discard
|
|
proc spring(x: ShadowConcept)=
|
|
discard
|
|
let a = DummyFitsObj()
|
|
spring(a)
|
|
|
|
block:
|
|
type
|
|
Buffer = concept
|
|
proc put(s: Self)
|
|
ArrayBuffer[T: static int] = object
|
|
proc put(x: ArrayBuffer)=discard
|
|
proc p(a: Buffer)=discard
|
|
var buffer = ArrayBuffer[5]()
|
|
p(buffer)
|
|
|
|
block: # composite typeclass matching
|
|
type
|
|
A[T] = object
|
|
Buffer = concept
|
|
proc put(s: Self, i: A)
|
|
BufferImpl = object
|
|
WritableImpl = object
|
|
|
|
proc launch(a: var Buffer)=discard
|
|
proc put(x: BufferImpl, i: A)=discard
|
|
|
|
var a = BufferImpl()
|
|
launch(a)
|
|
|
|
block: # simple recursion
|
|
type
|
|
Buffer = concept
|
|
proc put(s: var Self, i: auto)
|
|
proc second(s: Self)
|
|
Writable = concept
|
|
proc put(w: var Buffer, s: Self)
|
|
BufferImpl[T: static int] = object
|
|
WritableImpl = object
|
|
|
|
proc launch(a: var Buffer, b: Writable)= discard
|
|
proc put[T](x: var BufferImpl, i: T)= discard
|
|
proc second(x: BufferImpl)= discard
|
|
proc put(x: var Buffer, y: WritableImpl)= discard
|
|
|
|
var a = BufferImpl[5]()
|
|
launch(a, WritableImpl())
|
|
|
|
block: # more complex recursion
|
|
type
|
|
Buffer = concept
|
|
proc put(s: var Self, i: auto)
|
|
proc second(s: Self)
|
|
Writable = concept
|
|
proc put(w: var Buffer, s: Self)
|
|
BufferImpl[T: static int] = object
|
|
WritableImpl = object
|
|
|
|
proc launch(a: var Buffer, b: Writable)= discard
|
|
proc put[T](x: var Buffer, i: T)= discard
|
|
proc put(x: var BufferImpl, i: object)= discard
|
|
proc second(x: BufferImpl)= discard
|
|
proc put(x: var Buffer, y: WritableImpl)= discard
|
|
|
|
var a = BufferImpl[5]()
|
|
launch(a, WritableImpl())
|
|
|
|
block: # co-dependent concepts
|
|
type
|
|
Writable = concept
|
|
proc w(b: var Buffer; s: Self): int
|
|
Buffer = concept
|
|
proc w(s: var Self; data: Writable): int
|
|
SizedWritable = concept
|
|
proc size(x: Self): int
|
|
proc w(b: var Buffer, x: Self): int
|
|
BufferImpl = object
|
|
|
|
proc w(x: var BufferImpl, d: int): int = return 100
|
|
proc size(d: int): int = sizeof(int)
|
|
|
|
proc p(b: var Buffer, data: SizedWritable): int =
|
|
b.w(data)
|
|
|
|
var b = BufferImpl()
|
|
echo p(b, 5)
|
|
|
|
block: # indirect concept matching
|
|
type
|
|
Sizeable = concept
|
|
proc size(s: Self): int
|
|
Buffer = concept
|
|
proc w(s: Self, data: Sizeable)
|
|
Serializable = concept
|
|
proc something(s: Self)
|
|
proc w(b: Buffer, s: Self)
|
|
BufferImpl = object
|
|
ArrayImpl = object
|
|
|
|
proc something(s: ArrayImpl)= discard
|
|
proc size(s: ArrayImpl): int= discard
|
|
|
|
proc w(x: BufferImpl, d: Sizeable)= discard
|
|
|
|
proc spring(s: Buffer, data: Serializable)=discard
|
|
|
|
spring(BufferImpl(), ArrayImpl())
|
|
|
|
block: # instantiate even when generic params are the same
|
|
type
|
|
ArrayLike[T] = concept
|
|
proc len(x: Self): int
|
|
proc `[]`(b: Self, i: int): T
|
|
proc p[T](x: ArrayLike[T])=
|
|
for k in x:
|
|
echo k
|
|
# For this test to work the second call's instantiation has to be incompatible with the first on the back end
|
|
p(['a','b','c'])
|
|
p("abc")
|
|
|
|
block: # reject improper generic variables in candidates
|
|
type
|
|
ArrayLike[T] = concept
|
|
proc len(x: Self): int
|
|
proc g(b: Self, i: int): T
|
|
FreakString = concept
|
|
proc len(x: Self): int
|
|
proc characterSize(s: Self): int
|
|
A = object
|
|
|
|
proc g[T, H](s: T, i: H): H = default(T)
|
|
proc len(s: A): int = discard
|
|
proc characterSize(s: A): int = discard
|
|
|
|
proc p(symbol: ArrayLike[char]): int = assert false
|
|
proc p(symbol: FreakString): int=discard
|
|
|
|
discard p(A())
|
|
|
|
block: # typerel disambiguation by concept subset
|
|
type
|
|
ArrayLike[T] = concept
|
|
proc len(x: Self): int
|
|
proc characterSize(s: Self): int
|
|
FreakString = concept
|
|
proc len(x: Self): int
|
|
proc characterSize(s: Self): int
|
|
proc tieBreaker(s: Self, j: int): float
|
|
A = object
|
|
|
|
proc len(s: A): int = discard
|
|
proc characterSize(s: A): int = discard
|
|
proc tieBreaker(s: A, h: int):float = 0.0
|
|
|
|
proc p(symbol: ArrayLike[char]): int = assert false
|
|
proc p(symbol: FreakString): int=discard
|
|
|
|
discard p(A())
|
|
|
|
block: # tie break via sumGeneric
|
|
type
|
|
C1 = concept
|
|
proc p1(x: Self, b: int)
|
|
proc p2(x: Self, b: float)
|
|
proc p3(x: Self, b: string)
|
|
C2 = concept
|
|
proc b1(x: Self, b: int)
|
|
proc b2(x: Self, b: float)
|
|
A = object
|
|
|
|
proc p1(x: A, b: int)=discard
|
|
proc p2(x: A, b: float)=discard
|
|
proc p3(x: A, b: string)=discard
|
|
|
|
proc b1(x: A, b: int)=discard
|
|
proc b2(x: A, b: float)=discard
|
|
|
|
proc p(symbol: C1): int = discard
|
|
proc p(symbol: C2): int = assert false
|
|
|
|
discard p(A())
|
|
|
|
block: # not type
|
|
type
|
|
C1 = concept
|
|
proc p(s: Self, a: int)
|
|
C1Impl = object
|
|
|
|
proc p(x: C1Impl, a: not float)= discard
|
|
proc spring(x: C1)= discard
|
|
|
|
spring(C1Impl())
|
|
|
|
block: # not type parameterized
|
|
type
|
|
C1[T: not int] = concept
|
|
proc p(s: Self, a: T)
|
|
C1Impl = object
|
|
|
|
proc p(x: C1Impl, a: float)= discard
|
|
proc spring(x: C1)= discard
|
|
|
|
spring(C1Impl())
|
|
|
|
block: # typedesc
|
|
type
|
|
C1 = concept
|
|
proc p(s: Self, a: typedesc[SomeInteger])
|
|
C1Impl = object
|
|
|
|
proc p(x: C1Impl, a: typedesc)= discard
|
|
proc spring(x: C1)= discard
|
|
|
|
spring(C1Impl())
|
|
|
|
|
|
block: # or
|
|
type
|
|
C1 = concept
|
|
proc p(s: Self, a: int | float)
|
|
C1Impl = object
|
|
|
|
proc p(x: C1Impl, a: int | float | string)= discard
|
|
proc spring(x: C1)= discard
|
|
|
|
spring(C1Impl())
|
|
|
|
block: # or mixed generic param
|
|
type
|
|
C1 = concept
|
|
proc p(s: Self, a: int | float)
|
|
C1Impl = object
|
|
|
|
proc p[T: string | float](x: C1Impl, a: int | T) = discard
|
|
proc spring(x: C1)= discard
|
|
|
|
spring(C1Impl())
|
|
|
|
block: # or parameterized
|
|
type
|
|
C1[T: int | float | string] = concept
|
|
proc p(s: Self, a: T)
|
|
C1Impl = object
|
|
|
|
proc p(x: C1Impl, a: int | float)= discard
|
|
proc spring(x: C1)= discard
|
|
|
|
spring(C1Impl())
|
|
|
|
block: # unconstrained param
|
|
type
|
|
A = object
|
|
C1[T] = concept
|
|
proc p(s: Self, a: T)
|
|
C1Impl = object
|
|
|
|
proc p(x: C1Impl, a: A)= discard
|
|
proc spring(x: C1)= discard
|
|
|
|
spring(C1Impl())
|
|
|
|
block: # unconstrained param sanity check
|
|
type
|
|
A = object
|
|
C1[T: auto] = concept
|
|
proc p(s: Self, a: T)
|
|
C1Impl = object
|
|
|
|
proc p(x: C1Impl, a: A)= discard
|
|
proc spring(x: C1)= discard
|
|
|
|
spring(C1Impl())
|
|
|
|
block: # exact nested concept binding
|
|
type
|
|
Sizeable = concept
|
|
proc size(s: Self): int
|
|
Buffer = concept
|
|
proc w(s: Self, data: Sizeable)
|
|
Serializable = concept
|
|
proc w(b: Buffer, s: Self)
|
|
ArrayLike = concept
|
|
proc len(s: Self): int
|
|
ArrayImpl = object
|
|
|
|
proc len(s: ArrayImpl): int = discard
|
|
proc w(x: Buffer, d: ArrayLike)=discard
|
|
|
|
proc spring(data: Serializable)=discard
|
|
spring(ArrayImpl())
|
|
|
|
block:
|
|
type
|
|
StaticallySized = concept
|
|
proc staticSize(x: typedesc[Self]): int
|
|
proc dynamicSize(x: Self): int
|
|
DynamicallySized = concept
|
|
proc dynamicSize(x: Self): int
|
|
|
|
proc dynamicSize(a: SomeInteger): int = 5
|
|
proc read[T: DynamicallySized](a: var T): int = 1
|
|
proc read[T: SomeInteger](a: var T): int = 2
|
|
|
|
var a: uint16
|
|
assert read(a) == 2
|
|
|
|
block:
|
|
type
|
|
A[X, Y] = object
|
|
x: X
|
|
y: Y
|
|
C1 = concept
|
|
proc p(z: var Self)
|
|
C2 = concept
|
|
proc g(x: var Self, y: int)
|
|
C3 = C1 and C2
|
|
C4 = concept
|
|
proc h(x: Self): C3
|
|
|
|
proc p[X, Y](z: var A[int, float]) = discard
|
|
proc g[X, Y](z: var A[X, Y], y: int) = discard
|
|
proc h[X, Y](z: A[X, Y]): A[X, Y] = discard
|
|
|
|
proc spring(x: C4) = discard
|
|
var d = A[int, float]()
|
|
d.spring()
|
|
|
|
block:
|
|
type
|
|
A[X, Y] = object
|
|
x: X
|
|
y: Y
|
|
B = object
|
|
C1 = concept
|
|
proc p(z: var Self, d: A[int, float])
|
|
|
|
proc p[X: int; Y: float](x: var B, y: A[X, Y]) = discard
|
|
proc spring(x: var C1) = discard
|
|
var d = B()
|
|
d.spring()
|
|
|
|
block:
|
|
type
|
|
A = object
|
|
C1 = concept
|
|
proc p(s: Self; x: auto)
|
|
C2[T: int] = concept
|
|
proc p(s: Self; x: T)
|
|
Impl = object
|
|
|
|
proc p(n: Impl; i: int) = discard
|
|
|
|
proc spring(x: C1): int = 1
|
|
proc spring(x: C2): int = 2
|
|
|
|
assert spring(Impl()) == 2
|
|
|
|
block:
|
|
type
|
|
C1[T] = concept
|
|
proc p(s: var Self; x: T)
|
|
FreakString = concept
|
|
proc p(w: var C1; s: Self)
|
|
proc a(x: Self)
|
|
DynArray[CT, T] = object
|
|
|
|
proc p[CT; T; W; ](w: C1[T]; o: DynArray[CT, T]): int = discard
|
|
proc spring(s: auto) = discard
|
|
proc spring(s: FreakString) = discard
|
|
|
|
spring("hi")
|
|
|
|
block:
|
|
type
|
|
RawWriter = concept
|
|
proc write(s: Self; data: pointer; length: int)
|
|
ArrayBuffer[N: static int] = object
|
|
SeqBuffer = object
|
|
CompatBuffer = ArrayBuffer | SeqBuffer
|
|
|
|
proc write[T:CompatBuffer](a: var T; data: pointer; length: int) =
|
|
discard
|
|
|
|
proc spring(r:RawWriter, i: byte)=discard
|
|
|
|
var s = ArrayBuffer[1500]()
|
|
spring(s, 8.uint8)
|
|
|
|
block:
|
|
type
|
|
Future[T] = object
|
|
SyncType = concept
|
|
proc p(s: Self)
|
|
AsyncType = concept
|
|
proc p(s: Self) : Future[void]
|
|
SyncImpl = object
|
|
AsyncImpl = object
|
|
Container[T] = object
|
|
|
|
proc p(x: SyncImpl) = discard
|
|
proc p(x: AsyncImpl): Future[void] = discard
|
|
|
|
proc p(x: Container[SyncType]) = discard
|
|
proc p(x: Container[AsyncImpl]): Future[void] = discard
|
|
|
|
assert SyncImpl is SyncType
|
|
assert SyncImpl isnot AsyncType
|
|
assert AsyncImpl isnot SyncType
|
|
assert AsyncImpl is AsyncType
|
|
assert Container[SyncImpl] is SyncType
|
|
assert Container[SyncImpl] isnot AsyncType
|
|
assert Container[AsyncImpl] isnot SyncType
|
|
assert Container[AsyncImpl] is AsyncType
|
|
|
|
block:
|
|
type
|
|
C1 = concept
|
|
proc p(x: typedesc[Self]): int
|
|
E1 = enum
|
|
One, Two
|
|
proc p[E: enum](x: typedesc[set[E]]): int = sizeof(set[E])
|
|
|
|
proc spring(x: C1) = discard
|
|
|
|
spring({One,Two})
|
|
|
|
block: # bare `range`
|
|
type
|
|
MyRange = 0..64
|
|
MyConcept = concept
|
|
proc a(x: typedesc[Self])
|
|
|
|
proc a(x: typedesc[range]) = discard
|
|
proc spring(x: typedesc[MyConcept]) = discard
|
|
spring(MyRange)
|
|
|
|
block:
|
|
type
|
|
A = object
|
|
TestConcept =
|
|
concept
|
|
proc x(x: Self)
|
|
|
|
proc x(x: not object) =
|
|
discard
|
|
|
|
assert A isnot TestConcept
|
|
|
|
# this code fails inside a block for some reason
|
|
type Indexable[T] = concept
|
|
proc `[]`(t: Self, i: int): T
|
|
proc len(t: Self): int
|
|
|
|
iterator items[T](t: Indexable[T]): T =
|
|
for i in 0 ..< t.len:
|
|
yield t[i]
|
|
|
|
type Enumerable[T] = concept
|
|
iterator items(t: Self): T
|
|
|
|
proc echoAll[T](t: Enumerable[T]) =
|
|
for item in t:
|
|
echo item
|
|
|
|
type DummyIndexable[T] = distinct seq[T]
|
|
|
|
proc `[]`[T](t: DummyIndexable[T], i: int): T =
|
|
seq[T](t)[i]
|
|
|
|
proc len[T](t: DummyIndexable[T]): int =
|
|
seq[T](t).len
|
|
|
|
|
|
let dummyIndexable = DummyIndexable(@[1, 2])
|
|
echoAll(dummyIndexable)
|
|
|
|
block:
|
|
type
|
|
C = concept
|
|
proc a(x: Self, i: int)
|
|
AObj[T] = object
|
|
x: T
|
|
ARef[T] = ref AObj[T]
|
|
|
|
proc a[T: int](x: ARef[T], i: int) =
|
|
discard
|
|
|
|
assert (ref AObj[int]) is C
|
|
|
|
block:
|
|
type
|
|
C = concept
|
|
proc a(x: Self, i: int)
|
|
AObj[T; B] = object
|
|
x: T
|
|
ARef[T; B] = ref AObj[T,B]
|
|
|
|
proc a[T: int, C: float](x: ARef[T, C], i: int) =
|
|
discard
|
|
|
|
assert (ref AObj[int, int]) isnot C
|
|
assert (ref AObj[int, float]) is C
|
|
|
|
block:
|
|
type
|
|
C = concept
|
|
proc a(x: Self, i: int)
|
|
AObj[T] = object
|
|
ARef[T] = ref AObj[T]
|
|
|
|
proc a(x: ARef, i: int) =
|
|
discard
|
|
|
|
assert (ref AObj[int]) is C
|
|
|
|
block:
|
|
type
|
|
C = concept
|
|
proc x(a:Self, x: int)
|
|
StreamObj = object of RootObj
|
|
Stream = ref StreamObj
|
|
MemMapFileStreamObj = object of Stream
|
|
MemMapFileStream = ref MemMapFileStreamObj
|
|
|
|
proc x(a: Stream, x: int) = discard
|
|
proc spring(x: C) = discard
|
|
|
|
let test = MemMapFileStream()
|
|
spring(test)
|
|
|
|
# explicit negative "bind once"
|
|
|
|
type
|
|
Dollarable = concept
|
|
proc `$`(x: Self): string
|
|
|
|
proc checkEqual2[T: Dollarable; S: Dollarable](x: T, y: S) =
|
|
echo $x
|
|
echo $y
|
|
|
|
checkEqual2(5, "test")
|