Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[superseded] new macro for generic reflection: extractGeneric(Foo2[float, string], 0) is float #8554

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion doc/manual.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4437,14 +4437,15 @@ type classes are called `bind many`:idx: types.

Procs written with the implicitly generic style will often need to refer to the
type parameters of the matched generic type. They can be easily accessed using
the dot syntax:
``sugar.extractGeneric`` or the dot syntax:

.. code-block:: nim
type Matrix[T, Rows, Columns] = object
...

proc `[]`(m: Matrix, row, col: int): Matrix.T =
m.data[col * high(Matrix.Columns) + row]
# we could've also used ``extractGeneric(Matrix, 0)``

Alternatively, the `type` operator can be used over the proc params for similar
effect when anonymous or distinct type classes are used.
Expand Down
31 changes: 31 additions & 0 deletions lib/pure/sugar.nim
Original file line number Diff line number Diff line change
Expand Up @@ -237,3 +237,34 @@ macro distinctBase*(T: typedesc): untyped =
while typeSym.typeKind == ntyDistinct:
typeSym = getTypeImpl(typeSym)[0]
typeSym.freshIdentNodes

macro extractGeneric*(T: typedesc, index:static[int]): untyped =
## extract generic type numbered ``index`` used to construct ``T``. Note:
## ``-1`` returns ``Foo`` in ``Foo[T]``
runnableExamples:
type Foo[T1, T2]=object
doAssert extractGeneric(Foo[float, string], 0) is float
doAssert extractGeneric(Foo[float, string], 1) is string
# pending https://github.com/nim-lang/Nim/issues/9855
# doAssert extractGeneric(Foo[float, string], -1) is Foo

var impl = getTypeImpl(T)
expectKind(impl, nnkBracketExpr)
impl = impl[1]
while true:
case impl.kind
of nnkSym:
impl = impl.getImpl
continue
of nnkTypeDef:
impl = impl[2]
continue
of nnkBracketExpr:
if index == -1:
impl=impl[0] #return `Foo` in `Foo[T]`
else:
impl=impl[1+index] #return `T` in `Foo[T]` (when index = 0)
break
else:
error "internal error: impl.kind: " & $impl.kind
impl
21 changes: 21 additions & 0 deletions tests/stdlib/tsugar.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ discard """
file: "tsugar.nim"
output: ""
"""

import sugar
import macros

Expand All @@ -27,3 +28,23 @@ block distinctBase:
Uint[bits: static[int]] = distinct uintImpl(bits)

doAssert Uint[128].distinctBase is UintImpl[uint64]

block extractGeneric:
type Foo[T1, T2]=object
type Foo2=Foo[float, string]
# pending https://github.com/nim-lang/Nim/issues/9855
# doAssert extractGeneric(Foo2, -1) is Foo
doAssert extractGeneric(Foo2, 0) is float
doAssert extractGeneric(Foo2, 1) is string
doAssert extractGeneric(Foo[float, string], 1) is string
# pending https://github.com/nim-lang/Nim/issues/9855
# doAssert extractGeneric(Foo[float, string], -1) is Foo
# workaround for seq[int].T not working,
# see https://github.com/nim-lang/Nim/issues/8433
doAssert extractGeneric(seq[int], 0) is int
doAssert extractGeneric(seq[seq[string]], 0) is seq[string]
doAssert not compiles(extractGeneric(seq[int], 1))
doAssert extractGeneric(seq[int], -1) is seq

type Foo3[T] = T
doAssert extractGeneric(Foo3[int], 0) is int