Skip to content

Commit

Permalink
add extractGeneric: extractGeneric(Foo2[float, string], 0) is float
Browse files Browse the repository at this point in the history
  • Loading branch information
timotheecour committed Dec 4, 2018
1 parent f86b827 commit eaf691f
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 1 deletion.
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
30 changes: 30 additions & 0 deletions lib/pure/sugar.nim
Original file line number Diff line number Diff line change
Expand Up @@ -237,3 +237,33 @@ 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
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
16 changes: 16 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,18 @@ 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]
doAssert extractGeneric(Foo[float, string], 1) is string
doAssert extractGeneric(Foo2, 1) is string
# 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

0 comments on commit eaf691f

Please sign in to comment.