Skip to content

Commit

Permalink
Structs cannot be tested for equality with == or !=.
Browse files Browse the repository at this point in the history
  • Loading branch information
cpressey committed Feb 22, 2022
1 parent 27c0b1a commit 4f09554
Show file tree
Hide file tree
Showing 9 changed files with 64 additions and 75 deletions.
7 changes: 5 additions & 2 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ Castile 0.5
after the struct. A value of a scope struct can only be
`make`d, and the fields of such a value can only be accessed,
lexically inside one of the definitions named in the `for`.
* Nested structs can be tested for deep equality.
* Values of union type can be tested for equality.
* Structs cannot be tested for equality with the `==` and `!=`
operators. Instead the programmer should write a function
that compares structs for equality, if desired.
* Values of union type can be tested for equality, but only if
none of the types involved in the union are structs.

### Implementation

Expand Down
11 changes: 4 additions & 7 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,7 @@ TODO

### Implementation ###

call equal_tagged_value() when you find a union type when
comparing structs deeply. (C backend, probably others)

Actually the idea of deep equality of structs is suspect.
Especially if the visibility of those structs is limited.
Instead users should write their own `equal` functions
(possibly abstract, possibly only testing equivalence.)
Remove the deep struct equality implementation from backends.

Name mangling for compilers (prepend with `_` most likely.)

Expand All @@ -36,6 +30,9 @@ Other backends (Python? Java? CIL? Scheme?)

### Design ###

Disallow equality checking for union types too (maybe except where
all member types are simple.)

Don't output final value. Command-line arguments passed to `main`. (`sysmain`?)

Convenience:
Expand Down
37 changes: 37 additions & 0 deletions eg/equal-linkedlist.castile
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
struct list {
value: string;
next: list|void;
}

fun empty() {
return null as list|void
}

fun cons(v: string, l: list|void) {
make list(value:v, next:l) as list|void
}

equal_list : list|void, list|void -> boolean
fun equal_list(a: list|void, b: list|void) {
typecase a is void {
typecase b is void {
return true
}
}
typecase a is list {
typecase b is list {
return a.value == b.value and equal_list(a.next, b.next)
}
}
return false
}

main = fun() {
l1 = cons("first", cons("second", cons("third", empty())));
l2 = cons("first", cons("second", cons("third", empty())));
l3 = cons("first", cons("second", empty()));

if (equal_list(l1, l2) and not equal_list(l2, l3)) {
print("Yep, story checks out")
}
}
15 changes: 0 additions & 15 deletions eg/equality.castile

This file was deleted.

2 changes: 1 addition & 1 deletion eg/length.castile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
struct list {
value: string;
next: list|void;
} for (cons, singleton, len)
} for (cons, singleton, length)

fun cons(v: string, l: list) {
make list(value:v, next:l as list|void)
Expand Down
1 change: 1 addition & 0 deletions src/castile/backends/c.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ def compile(self, ast):
self.indent -= 1
self.write_indent('}\n\n')

# FIXME the language no longer supports this, it can be jettisoned
self.write_indent('int equal_%s(struct %s * a, struct %s * b) {\n' % (ast.value, ast.value, ast.value))

self.indent += 1
Expand Down
1 change: 1 addition & 0 deletions src/castile/backends/javascript.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def compile(self, ast):
elif ast.tag == 'Forward':
pass
elif ast.tag == 'StructDefn':
# FIXME the language no longer supports this, it can be jettisoned
field_defns = ast.children[0].children
self.write('function equal_%s(a, b) {\n' % ast.value)
for child in field_defns:
Expand Down
4 changes: 2 additions & 2 deletions src/castile/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ def type_of(self, ast):
type1 = self.type_of(ast.children[0])
type2 = self.type_of(ast.children[1])
self.assert_eq(type1, type2)
if ast.value in ('>', '>=', '<', '<=') and isinstance(type1, Struct):
raise CastileTypeError("structs cannot be compared for order")
if isinstance(type1, Struct):
raise CastileTypeError("structs cannot be compared")
ast.type = Boolean()
elif ast.tag == 'Not':
type1 = self.type_of(ast.children[0])
Expand Down
61 changes: 13 additions & 48 deletions tests/Castile.md
Original file line number Diff line number Diff line change
Expand Up @@ -769,32 +769,24 @@ Order of field initialization when making a struct doesn't matter.
| }
= 23

Structs can be tested for equality. (Since structs are immutable, it
doesn't matter if this is structural equality or identity.)
Structs cannot be tested for equality with the `==` or `!==`
operators.

| struct person { name: string; age: integer }
| main = fun() {
| j = make person(age: 23, name:"Jake");
| k = make person(name:"Jake", age: 23);
| j == k
| }
= True

| struct person { age: integer; name: string }
| main = fun() {
| j = make person(age: 23, name:"Jake");
| k = make person(age: 23, name:"John");
| j == k
| }
= False
? structs cannot be compared

| struct person { age: integer; name: string }
| main = fun() {
| j = make person(age: 23, name:"Jake");
| k = make person(age: 21, name:"Jake");
| j != k
| }
= True
? structs cannot be compared

Structs of two different types cannot be tested for equality.

Expand All @@ -807,44 +799,17 @@ Structs of two different types cannot be tested for equality.
| }
? mismatch

Deeply nested structs can be tested for equality.

| struct name { first: string; last: string }
| struct person { age: integer; name: name }
| main = fun() {
| j = make person(age: 23, name:make name(first:"Bamber", last:"Smith"));
| k = make person(age: 23, name:make name(first:"Bamber", last:"Smith"));
| j == k
| }
= True

| struct name { first: string; last: string }
| struct person { age: integer; name: name }
| main = fun() {
| j = make person(age: 23, name:make name(first:"Bamber", last:"Smith"));
| k = make person(age: 23, name:make name(first:"Amber", last:"Smith"));
| j != k
| }
= True

Deeply nested structs can be tested for equality, even when containing values
of union type.
If you really want to compare two structs for equality, you'll
have to write the equality predicate function yourself.

| struct name { first: string; last: string|integer }
| struct person { age: integer; name: name }
| main = fun() {
| j = make person(age: 23, name:make name(first:"Bamber", last:"Smith" as string|integer));
| k = make person(age: 23, name:make name(first:"Bamber", last:"Smith" as string|integer));
| j == k
| struct person { name: string; age: integer }
| equ_person = fun(a: person, b: person) {
| a.age == b.age and a.name == b.name
| }
= True

| struct name { first: string; last: string|integer }
| struct person { age: integer; name: name }
| main = fun() {
| j = make person(age: 23, name:make name(first:"Bamber", last:"Smith" as string|integer));
| k = make person(age: 23, name:make name(first:"Bamber", last:75 as string|integer));
| j != k
| j = make person(age: 23, name:"Jake");
| k = make person(name:"Jake", age: 23);
| equ_person(j, k)
| }
= True

Expand All @@ -856,7 +821,7 @@ Structs cannot be compared for ordering.
| k = make person(age: 21, name:"Jake");
| j > k
| }
? structs cannot be compared for order
? structs cannot be compared

Structs can be passed to functions.

Expand Down

0 comments on commit 4f09554

Please sign in to comment.