(This is not a complaint. I know that I can switch to pure "Generics" at any time, or even "Templates". I'm just relating my own experience with "Concepts".)
I have had tons of problems with "concepts". In the simplest case, they seem wonderful. But as soon as I try to do something complicated, I get inscrutable errors. I am never even sure whether what I want to do is actually possible.
For example, how do I constrain an "iterator" for a "concept"? And how can I learn the generic type that the iterator returns?
Another example: https://github.com/nim-lang/Nim/issues/6509
What is the "correct" way to do that?
Basically, I want a "Graph" type where "Node" and "Key" are generic. Maybe "Edge" too. Then, for weighted Graph traversal algorithms, I want to use subtypes with their own inline "weight" functions, rather than passing a point-to-weight-function. Here is my current version, a WIP, in case anybody would like to offer suggestions:
This is quite frustrating because the whole reason I'm using Nim is for high productivity. With Concepts, my productivity has been abysmal. Generics and Templates are easy, and I can always use those. I was just hoping to learn the "recommended" way to do things.
As far as I can tell this is a bad error message but MyGraph.Node is not valid. The tests contain
import math
type
Node* = concept n
`==`(n, n) is bool
Graph1* = concept g
type N = Node
distance(g, N, N) is float
Graph2 = concept g
distance(g, Node, Node) is float
Graph3 = concept g
var x: Node
distance(g, x, x) is float
XY* = tuple[x, y: int]
MyGraph* = object
points: seq[XY]
static:
assert XY is Node
proc distance*( g: MyGraph, a, b: XY): float =
sqrt( pow(float(a.x - b.x), 2) + pow(float(a.y - b.y), 2) )
static:
assert MyGraph is Graph1
assert MyGraph is Graph2
assert MyGraph is Graph3
as a starting point for how to model graphs. Not sure if that's helpful.