Constructors with the same name

I’m trying to understand if there’s a convenient way to use constructors
with the same name but different types.

For instance:

datatype CONSOV =
| acons of int
| acons of (CONSOV,CONSOV)
| bcons of int

val a1 = acons(5)
val a2 = acons(acons(1),acons(2))

val tst = case a1 of
| acons _ => print(“acons\n”)
| bcons X => print(“bcons\n”)
// | acons (,) => print(“acons2\n”) //This clause is redundant

The compiler warns us that not all cases are taken into account above, but
errors if the last line is uncommented.

Oddly, to me, “case a1” will fail with a run time error, but “case a2” will
run ok with the above code.

What I was hoping was that there would be a way to group several
constructors together (i.e. by name) to save on making many alternative
case expressions.

If we use named variables everything is fine:
val tst = case a1 of
| acons (x) => print(“acons\n”)
| bcons X => print(“bcons\n”)
| acons (x,y) => print(“acons2\n”) //This clause is redundant

Sorry, acons(_) is now treated as ‘acons _’. Try acons(x).

I fixed this issue. Now () and _ are different patterns. You could write
acons(
) if you want to.

Note in the second code block, the clause is not redundant according to the
compiler; I forgot to remove teh comment.

If
| acons of (CONSOV,CONSOV)
is defined after
| acons of int
then the expansion mentioned above still seems to occur in this case:

| acons (,) =>> print(“acons2\n”)
| acons (_) =>> print(“acons\n”) * *//This clause is redundant

Note that ‘acons ’ and 'acons ()’ are different; the former expands to
’acons (_, …, _)’ depending on the arity of acons.
In the above case, ‘acons ’ expands to 'acons (, _)’ because the acons of
arity 2 is defined after the acons of arity 1.

Right, thanks. On a related note, is there currently a way to match
multiple patterns for the same code block if

  1. all the patterns for a particular block of code in a case expression
    have the same variables of the same types, e.g.
    | acons(x,) | acons(x,) | acons(x) => x^2

  2. The constructors have the same name but do not bind any values:
    acons => print("this is acons() or acons (,_)

It seems like this kind of method might be convenient to alleviate code
duplication occurring in case expression code blocks, but maybe there is a
better way.

For instance, in the example I posted a few days ago, there were lots of
fold@ statements occurring in case code blocks. This brings up another
question, as I’m not sure how to pass an unfolded variable to a function to
reduce code duplication (if it is even possible).

Under the circumstances, say you have something that looks like the
following, with many variations of this pattern but all requiring the same
code:

| GRconj (!e1, !e2) => (case+ (!e1, !e2) of
| (GRdisj (!x), GRdisj (!z)) => x where {
prval () = fold@ !e1 and () = fold@ !e2
val x = dCd(!e1,!e2)
prval () = fold@ e0 }
| (GRdisj (!x,!y), GRdisj (!z)) => x where {
prval () = fold@ !e1 and () = fold@ !e2
val x = dCd(!e1,!e2)
prval () = fold@ e0 }

This isn’t particularly bad, but it might be nice to say something like:
| (GRdisj (!x), GRdisj (!z)) | (GRdisj (!x,!y), GRdisj (!z)) =>
foo(e1,e2)

I guess the type system could potentially make some issues of this sort
tricky, so I do not know if it is a hard issue to tackle, just curious.

This issue can be tackled with the macro system of ATS.

macdef foo (e1, e2) = …

–Hongwei

PS: I do plan to dedicate a large chunk of my time on documentation once I
can get ATS2 released.