breaking change: 'concept' is now a keyword and used instead of 'generic'

This commit is contained in:
Araq 2015-03-23 00:51:04 +01:00
commit f6ff01572e
15 changed files with 462 additions and 475 deletions

View file

@ -52,11 +52,11 @@ identOrLiteral = generalizedLit | symbol | literal
tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')'
arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']'
primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
| doBlocks
| '.' optInd symbol generalizedLit?
| '[' optInd indexExprList optPar ']'
| '{' optInd indexExprList optPar '}'
| &( '`'|IDENT|literal|'cast') expr # command syntax
| doBlocks
| '.' optInd symbol generalizedLit?
| '[' optInd indexExprList optPar ']'
| '{' optInd indexExprList optPar '}'
| &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax
condExpr = expr colcom expr optInd
('elif' expr colcom expr optInd)*
'else' colcom expr
@ -94,7 +94,7 @@ primary = typeKeyw typeDescK
/ 'bind' primary
typeDesc = simpleExpr
typeDefAux = simpleExpr
| 'generic' typeClass
| 'concept' typeClass
macroColon = ':' stmt? ( IND{=} 'of' exprList ':' stmt
| IND{=} 'elif' expr ':' stmt
| IND{=} 'except' exprList ':' stmt

View file

@ -1,6 +1,6 @@
addr and as asm atomic
bind block break
case cast const continue converter
case cast concept const continue converter
defer discard distinct div do
elif else end enum except export
finally for from func

View file

@ -60,7 +60,7 @@ Is operator
-----------
The ``is`` operator checks for type equivalence at compile time. It is
therefore very useful for type specialization within generic code:
therefore very useful for type specialization within generic code:
.. code-block:: nim
type
@ -75,7 +75,7 @@ Type operator
-------------
The ``type`` (in many other languages called `typeof`:idx:) operator can
be used to get the type of an expression:
be used to get the type of an expression:
.. code-block:: nim
var x = 0
@ -88,7 +88,7 @@ other interpretations:
.. code-block:: nim
import strutils
# strutils contains both a ``split`` proc and iterator, but since an
# an iterator is the preferred interpretation, `y` has the type ``string``:
var y: type("a b c".split)
@ -98,7 +98,7 @@ Type Classes
------------
A type class is a special pseudo-type that can be used to match against
types in the context of overload resolution or the ``is`` operator.
types in the context of overload resolution or the ``is`` operator.
Nim supports the following built-in type classes:
================== ===================================================
@ -116,7 +116,7 @@ type class matches
``array`` any array type
``set`` any set type
``seq`` any seq type
``auto`` any type
``auto`` any type
================== ===================================================
Furthermore, every generic type automatically creates a type class of the same
@ -134,7 +134,7 @@ more complex type classes:
echo key, " = ", value
Procedures utilizing type classes in such manner are considered to be
`implicitly generic`:idx:. They will be instantiated once for each unique
`implicitly generic`:idx:. They will be instantiated once for each unique
combination of param types used within the program.
Nim also allows for type classes and regular types to be specified
@ -142,7 +142,7 @@ as `type constraints`:idx: of the generic type parameter:
.. code-block:: nim
proc onlyIntOrString[T: int|string](x, y: T) = discard
onlyIntOrString(450, 616) # valid
onlyIntOrString(5.0, 0.0) # type mismatch
onlyIntOrString("xy", 50) # invalid as 'T' cannot be both at the same time
@ -152,7 +152,7 @@ exactly one concrete type. Here is an example taken directly from the system
module to illustrate this:
.. code-block:: nim
proc `==`*(x, y: tuple): bool =
proc `==`*(x, y: tuple): bool =
## requires `x` and `y` to be of the same tuple type
## generic ``==`` operator for tuples that is lifted from the components
## of `x` and `y`.
@ -160,8 +160,8 @@ module to illustrate this:
for a, b in fields(x, y):
if a != b: result = false
Alternatively, the ``distinct`` type modifier can be applied to the type class
to allow each param matching the type class to bind to a different type.
Alternatively, the ``distinct`` type modifier can be applied to the type class
to allow each param matching the type class to bind to a different type.
If a proc param doesn't have a type specified, Nim will use the
``distinct auto`` type class (also known as ``any``):
@ -178,7 +178,7 @@ the dot syntax:
type Matrix[T, Rows, Columns] = object
...
proc `[]`(m: Matrix, row, col: int): Matrix.T =
proc `[]`(m: Matrix, row, col: int): Matrix.T =
m.data[col * high(Matrix.Columns) + row]
Alternatively, the `type` operator can be used over the proc params for similar
@ -189,7 +189,7 @@ type, this results in another more specific type class:
.. code-block:: nim
seq[ref object] # Any sequence storing references to any object type
type T1 = auto
proc foo(s: seq[T1], e: T1)
# seq[T1] is the same as just `seq`, but T1 will be allowed to bind
@ -203,34 +203,34 @@ be inferred to have the equivalent of the `any` type class and thus they will
match anything without discrimination.
User defined type classes
-------------------------
Concepts
--------
**Note**: User defined type classes are still in development.
**Note**: Concepts are still in development.
The user-defined type classes are available in two flavours - declarative and
imperative. Both are used to specify an arbitrary set of requirements that the
matched type must satisfy.
Concepts, also known as "user-defined type classes", are available in two
flavours - declarative and imperative. Both are used to specify an arbitrary
set of requirements that the matched type must satisfy.
Declarative type classes are written in the following form:
.. code-block:: nim
type
Comparable = generic x, y
Comparable = concept x, y
(x < y) is bool
Container[T] = generic c
Container[T] = concept c
c.len is ordinal
items(c) is iterator
for value in c:
type(value) is T
The type class will be matched if:
The concept will be matched if:
a) all of the expressions within the body can be compiled for the tested type
b) all statically evaluatable boolean expressions in the body must be true
The identifiers following the `generic` keyword represent instances of the
The identifiers following the ``concept`` keyword represent instances of the
currently matched type. These instances can act both as variables of the type,
when used in contexts where a value is expected, and as the type itself when
used in contexts where a type is expected.
@ -240,18 +240,18 @@ type signatures of the required operations, but since type inference and
default parameters are still applied in the provided block, it's also possible
to encode usage protocols that do not reveal implementation details.
As a special rule providing further convenience when writing type classes, any
As a special rule providing further convenience when writing concepts, any
type value appearing in a callable expression will be treated as a variable of
the designated type for overload resolution purposes, unless the type value was
passed in its explicit ``typedesc[T]`` form:
.. code-block:: nim
type
OutputStream = generic S
OutputStream = concept S
write(var S, string)
Much like generics, the user defined type classes will be instantiated exactly
once for each tested type and any static code included within them will also be
Much like generics, concepts are instantiated exactly
once for each tested type and any static code included within them is also
executed once.
@ -269,7 +269,7 @@ The return type will be treated as an additional generic param and can be
explicitly specified at call sites as any other generic param.
Future versions of Nim may also support overloading based on the return type
of the overloads. In such settings, the expected result type at call sites may
of the overloads. In such settings, the expected result type at call sites may
also influence the inferred return type.
..
@ -293,12 +293,12 @@ at definition and the context at instantiation are considered:
.. code-block:: nim
type
Index = distinct int
proc `==` (a, b: Index): bool {.borrow.}
var a = (0, 0.Index)
var b = (0, 0.Index)
echo a == b # works!
In the example the generic ``==`` for tuples (as defined in the system module)
@ -307,7 +307,7 @@ the ``Index`` type is defined *after* the ``==`` for tuples; yet the example
compiles as the instantiation takes the currently defined symbols into account
too.
A symbol can be forced to be open by a `mixin`:idx: declaration:
A symbol can be forced to be open by a `mixin`:idx: declaration:
.. code-block:: nim
proc create*[T](): ref T =
@ -321,16 +321,16 @@ A symbol can be forced to be open by a `mixin`:idx: declaration:
Bind statement
--------------
The ``bind`` statement is the counterpart to the ``mixin`` statement. It
The ``bind`` statement is the counterpart to the ``mixin`` statement. It
can be used to explicitly declare identifiers that should be bound early (i.e.
the identifiers should be looked up in the scope of the template/generic
definition):
.. code-block:: nim
# Module A
var
var
lastId = 0
template genId*: expr =
bind lastId
inc(lastId)
@ -339,7 +339,7 @@ definition):
.. code-block:: nim
# Module B
import A
echo genId()
But a ``bind`` is rarely useful because symbol binding from the definition

View file

@ -6,7 +6,7 @@ static[T]
**Note**: static[T] is still in development.
As their name suggests, static params must be known at compile-time:
As their name suggests, static parameters must be known at compile-time:
.. code-block:: nim
@ -23,23 +23,7 @@ As their name suggests, static params must be known at compile-time:
For the purposes of code generation, all static params are treated as
generic params - the proc will be compiled separately for each unique
supplied value (or combination of values).
Furthermore, the system module defines a `semistatic[T]` type that can be
used to declare procs accepting both static and run-time values, which can
optimize their body according to the supplied param using the `isStatic(p)`
predicate:
.. code-block:: nim
# The following proc will be compiled once for each unique static
# value and also once for the case handling all run-time values:
proc re(pattern: semistatic[string]): RegEx =
when isStatic(pattern):
result = precompiledRegex(pattern)
else:
result = compile(pattern)
supplied value (or combination of values).
Static params can also appear in the signatures of generic types:
@ -61,7 +45,7 @@ typedesc
--------
`typedesc` is a special type allowing one to treat types as compile-time values
(i.e. if types are compile-time values and all values have a type, then
(i.e. if types are compile-time values and all values have a type, then
typedesc must be their type).
When used as a regular proc param, typedesc acts as a type class. The proc
@ -100,7 +84,7 @@ When used with macros and .compileTime. procs on the other hand, the compiler
does not need to instantiate the code multiple times, because types then can be
manipulated using the unified internal symbol representation. In such context
typedesc acts as any other type. One can create variables, store typedesc
values inside containers and so on. For example, here is how one can create
values inside containers and so on. For example, here is how one can create
a type-safe wrapper for the unsafe `printf` function from C:
.. code-block:: nim
@ -114,7 +98,7 @@ a type-safe wrapper for the unsafe `printf` function from C:
of 's': string
of 'p': pointer
else: EOutOfRange
var actualType = args[i].getType
inc i
@ -123,7 +107,7 @@ a type-safe wrapper for the unsafe `printf` function from C:
elif expectedType != actualType:
error "type mismatch for argument ", i, ". expected type: ",
expectedType.name, ", actual type: ", actualType.name
# keep the original callsite, but use cprintf instead
result = callsite()
result[0] = newIdentNode(!"cprintf")