Hi folks,
Let's say I want to define a "family" of Matrix data types, indexed on their element type and their rank (row/column counts). Dependent types would be convenient here, but another approach would be to create a macro or template, e.g., 'generate_square_matrix_type(Elem: typedesc, rank: int)' that created the type and its related procedures. For example, a call to
generate_square_matrix_type(int, 4)
might produce code like:
type Matrix_Int_4 = array[4*4, int] proc `+`*(a, b: Matrix_Int_4): Matrix_Int_4 = ... proc `*`*(a, b: Matrix_Int_4): Matrix_Int_4 = ... ...
I'm open to other ideas on how to approach a problem like this. But my main question is: how would I create the name of the type within this template or macro? Specifically, given a typedesc and a numeric value as parameters, can I create an identifier of the form "Matrix_{name_of_type}_{$integer_value}"?
I see the "backtick" syntax within in template documentation, but it seems too limited to produce the naming scheme above (it expands the type names, but not the integer value). I tried playing with AST macros, but I can't figure out how to derive a name-string from a typedesc, so that I can build up the new type name.
(BTW, I'm using 0.9.3 from the git master HEAD.)
Any ideas?
Graham
Fair question. A generic type Matrix[T] would let me define the element type of the matrix, but would not let me define the width and height. If this information were captured in the type, it could simplify adding compile-time optimizations for certain array sizes, while keeping generic routines to handle irregular sizes.
I realize that, in this case, you could implement the type using multidimensional arrays, and that would capture the dimensions statically.
But I'm not actually writing a matrix library; I'm just trying to get a handle on the metaprogramming aspects of the language, and how something like depdendent types might be emulated. Having the dimensions of a matrix type available at compile-time (in the "type") is just an example.
Looking at the source of linagl's matrix type I don't see why you can't use generics and specify sizes, it seems to be doing fine with them. Maybe you can give an example of what is not possible with generics?
With regards to doing it the macro way, you can compile this and verify the macro generates the same type as normal code would. But it seems pretty painful to do it this way having generics:
import macros
dumpTree:
type Matrix_int_4 = array[4*4, int]
macro generate_square_matrix_type(typ, rank: PNimrodNode):
stmt {.immediate.} =
let tname = "Matrix_" & $typ & "_" & $intVal(rank)
result = newStmtList(
newNimNode(nnkTypeSection).add(
newNimNode(nnkTypeDef).add(
newIdentNode(tname),
newEmptyNode(),
newNimNode(nnkBracketExpr).add(
newIdentNode("array"),
infix(rank, "*", rank),
newIdentNode("int")))))
echo "We built: ", treeRepr(result)
when isMainModule:
generate_square_matrix_type(int, 4)
Oh! Ranges are expressible as types! Of course that solves the matrix issue. Thank you for pointing this out. I haven't spent much time with Nimrod (or Pascal, or Ada!) and that never occurred to me.
But especially thank you for the detailed macro. That's exactly what I was looking for.
[edit] In spite of my matrix example, I really don't have a specific problem in mind right now; I'm just learning the language. But it seems to me that the metaprogramming facilities are one of the most compelling features of this language, and I'm eager to kick the tires. :)
I think you are missing some of the Nimrod's features.
Specifying the dimensions of the Matrix as generic parameters have been available since some of the earlier versions of the compiler. You can encode them using a range type. Some examples are available here:
https://github.com/Araq/Nimrod/blob/devel/tests/matrix/tmatrix1.nim | https://github.com/Araq/Nimrod/blob/devel/tests/range/tmatrix3.nim
Admittedly, this is a bit bothersome and a lot of people have requested that we add support for the more familiar int-like generic parameters. For this and other reasons, recent versions of the compiler allow the generic types to have arbitrarily typed static arguments:
http://build.nimrod-lang.org/docs/manual.html#static-t
Unfortunately, some recent developments have introduced regressions that break the static parameters in the devel branch, but we are certainly going to fix this for the upcoming 0.9.4 release.
I think you are missing some of the Nimrod's features.
I certainly am. :) I've tried to read the manual and tutorial exhaustively, but it's a lot to take in at once. :)
Thanks for mentioning static[T]. Range types are fine for the matrix example, as you and gradha mentioned, but I can see static (and semistatic, oh my!) being useful where ranges don't make sense, and they are much better than my macro attempts.