variant Shape:
Circle(r: float)
Rectangle(w: float, h: float)
UnitCircle
and expand into
type
ShapeE = enum
CircleE, RectangleE, UnitCircleE
Shape = object
case kind: ShapeE
of CircleE:
r: float
of RectangleE:
w: float
h: float
of UnitCircleE:
nil
proc `==`(a: Shape; b: Shape): bool =
if a.kind == b.kind:
case a.kind
of CircleE:
return a.r == b.r
of RectangleE:
return a.w == b.w and a.h == b.h
of UnitCircleE:
return true
else:
return false
proc Circle(r: float; x: float; y: float): Shape =
Shape(kind: CircleE, r: r)
proc Rectangle(w: float; h: float): Shape =
Shape(kind: RectangleE, w: w, h: h)
proc UnitCircle(side: int): Shape =
Shape(kind: UnitCircleE)
The match macro then allows (very very simple) pattern matching (regardless if the variant type is created by variant or by hand):
match makeRect(3, 4):
Circle(r: radius):
echo "it is a circle of radius ", radius
Rectangle(w: width, h: height):
echo "it is a rectangle of height ", height
UnitCircle():
echo "it is a unit circle"
Support for pattern matching is really, really basic (essentially what I described above) and a lot of features are lacking, see the README on github for more examples of what would be nice to have.
Still, I wanted to get the ball rolling, and I hope I will be able to improve it to provide decent pattern matching. Any help is appreciated!
The variant macro is simpler and is in better shape, but still has some issues with ref types and probably does not handle visibility properly. Even there, there is still some work to do.
I hope it eventually becomes something usable. Cheers, Andrea
Looks really nice!
If you want a reference for implementing missing features, you can look through the source of Hyskell, a similar library I wrote for Hy. I basically used recursion to implement nesting and some simple checks to implement catch-all patterns.
Thank you for your pointer! The only delicate thing to do nesting is to take care of the case where externally there is a match, but internally the match fails. The simple solution would be to iterate through all clauses until one matches. A better way may be to group clauses based on the external pattern and recurse on that.
Anyway, if you want to help, any contribution is very much appreciated!
datatype notZero:
if X != 0
______
X : notZero
proc div(numerator: int, denominator: int): int =
{ any --> notZero --> any }
result = numerator / denominator
let x = 2
div(4, x) # type error
# x not of type notZero
if x != 0:
div(4, x) # OK
That would be quite nontrivial to do. More in general, language with dependent types and a proof system could be able to express more complex proofs that a number is not zero.
Anyway, this would not be in the scope of these macros. My aim would be to have pattern matching in the style of ML.
There is still a lot of work to do, in particular for the algebraic data types generation
while for pattern matching there is a lot to do, in particular for nested matching and matching sequences.
Any help or comment is appreciated!
Unfortunately, not yet - there is still a lot of work to do. Anyway, when I get there, I was planning to just compare the two values with ==, assuming the caller has defined == in a suitable way.
Note that Patty also provides a macro variant to generate algebraic data types, and for these types it also generates a definition of equality that amounts to comparing the discriminator and the fields. It is explained in a little more detail on the README