I really like that you focus on the mathematical properties. That's where property-based testing really shines. Just like unit tests help us write testable code, property-based tests help us write operations with simple and useful properties.

A couple of comments:

a = a * 1 = a * 1 * 1 is idempotent. 1 is also the identity of *, which is another property.

Idempotence (function f is idempotent): f(a) = f(f(a))

a + 0 = a
a * 1 = a
usernames.append([]) = usernames
hashmap.merge({}) = hashmap
Bool1 && True = Bool1

There's also the mathematical idea of zero. a * 0 = 0. It's another cool property.

Zero (z is the zero of f): f(a, z) = z
intersect(set1, EmptySet) = EmptySet
Bool1 && False = False

I think the example you gave for associativity (with sorting and filtering) is actually an example of commutativity since it shows that order doesn't matter between sorting and filtering.

Math.max(a, b) = Math.max(b, a) (though this one is also associative :)
Bool1 && Bool2 = Bool2 && Bool1 (this one is associative too)
average(a, b) = average(b, a)

First off, I geeked out again when I saw I had a comment from you. I've loved several of your articles on Clojure!

Secondly, I think your critiques are correct. I misused idempotency slightly, and my associative example is actually commutative. I definitely appreciate the clarification and additional examples? Would you mind if I added those to the article?

Thanks again for the complements and clarifications, and keep up the good work at PurelyFunctional.tv!

Hey Jason,

Very nice article!

I really like that you focus on the mathematical properties. That's where property-based testing really shines. Just like unit tests help us write testable code, property-based tests help us write operations with simple and useful properties.

A couple of comments:

a = a * 1 = a * 1 * 1 is idempotent. 1 is also the identity of *, which is another property.

Idempotence (function f is idempotent): f(a) = f(f(a))

Examples:

Math.abs(x) = Math.abs(Math.abs(x))

hashmap.merge({a:1}) = hashmap.merge({a:1}).merge({a:1})

Identity (i is the identity of f): f(a, i) = a

Examples:

a + 0 = a

a * 1 = a

usernames.append([]) = usernames

hashmap.merge({}) = hashmap

Bool1 && True = Bool1

There's also the mathematical idea of zero. a * 0 = 0. It's another cool property.

Zero (z is the zero of f): f(a, z) = z

intersect(set1, EmptySet) = EmptySet

Bool1 && False = False

I think the example you gave for associativity (with sorting and filtering) is actually an example of commutativity since it shows that order doesn't matter between sorting and filtering.

Associativity examples:

hashmap1.merge(hashmap2.merge(hashmap3)) = (hasmap1.merge(hashmap2)).merge(hashmap3)

list1.append(list2.append(list3)) = (list1.append(list2)).append(list3)

Commutativity examples:

Math.max(a, b) = Math.max(b, a) (though this one is also associative :)

Bool1 && Bool2 = Bool2 && Bool1 (this one is associative too)

average(a, b) = average(b, a)

Again, this was an awesome article!

Rock on!

Eric

Hey Eric,

First off, I geeked out again when I saw I had a comment from you. I've loved several of your articles on Clojure!

Secondly, I think your critiques are correct. I misused idempotency slightly, and my associative example is actually commutative. I definitely appreciate the clarification and additional examples? Would you mind if I added those to the article?

Thanks again for the complements and clarifications, and keep up the good work at PurelyFunctional.tv!

Hey Jason,

Sorry I didn't reply to this sooner. Be my guest and reuse the examples.

Rock on!

Eric