gracelang / language

Design of the Grace language and its libraries
GNU General Public License v2.0
6 stars 1 forks source link

Are too many objects Patterns? #183

Open apblack opened 4 years ago

apblack commented 4 years ago

I wrote this in a test:

assert {allEven [1, 2, 3]} shouldRaise "TypeError" mentioning "does not have type List”

and got the following failure message:

wrong type: code raised exception TypeError: "argument to request of `allEven(_)` 
does not have type List. ..." instead of TypeError

This confused me: why was a TypeError not being recognized as a TypeError? It took me quite a long time to notice that I had written “TypeError” rather than TypeError, i.e, I had used a string rather than an ExceptionKind.

So, I though: I can stop that from happening again. All I need do is to annotate the assert (_) shouldRaise(_)... method in gUnit with the type ExceptionKind. I did this, and the test now failed, complaining that the wrong type of argument was provided to assert(_)shouldRaise(_)

Unfortunately, a few other tests also failed. For example:

testDictionaryRemove5: TypeError: argument 2 in request of
`assert(_)shouldRaise(_)mentioning(_)and(_)` does not have type ExceptionKind. 
It is an OrPattern.new(_,_) (defined in module standardGrace, line 88), which is missing
methods parent, raise(_), raise(_)with(_), and refine(_). 

The test in question is written like this:

        assert {evens.removeKey 5} shouldRaise (NoSuchObject | TypeError)
        // TypeError if the type parameters are checked, NoSuchObejct otherwise

Nothing wrong with that test. Obviously, this was my mistake; I need to annotate the shouldRaise parameter with Pattern, not ExceptionKind.

Unfortunately, that doesn’t help, because a string is a Pattern, and my original error is no longer caught. Indeed, all the primitive objects are patterns. I can't think of a type annotation that would catch this error.

Is this a design issue with Patterns?

kjx commented 4 years ago

Well yeah. The question is safety vs convenience.

Do we want to write

match (x) case { 3 -> "three" }

or

match (x) case { _ : pattern( 3 ) -> "three" }

or perhaps

match (x) case { # 3 -> "three" }

the big problem, I seem to remember, is if you (can) write

if ( a | b ) ...

instead of

if ( a || b )

On 10/11/2019, at 7:45AM, Andrew Black notifications@github.com wrote:

I wrote this in a test:

assert {allEven [1, 2, 3]} shouldRaise "TypeError" mentioning "does not have type List”

and got the following failure message:

wrong type: code raised exception TypeError: "argument to request of allEven(_) does not have type List. ..." instead of TypeError

This confused me: why was a TypeError not being recognized as a TypeError? It took me quite a long time to notice that I had written “TypeError” rather than TypeError, i.e, I had used a string rather than an ExceptionKind.

So, I though: I can stop that from happening again. All I need do is to annotate the assert () shouldRaise()... method in gUnit with the type ExceptionKind. I did this, and the test now failed, complaining that the wrong type of argument was provided to assert()shouldRaise()

Unfortunately, a few other tests also failed. For example:

testDictionaryRemove5: TypeError: argument 2 in request of assert(_)shouldRaise(_)mentioning(_)and(_) does not have type ExceptionKind. It is an OrPattern.new(,) (defined in module standardGrace, line 88), which is missing methods parent, raise(), raise()with(), and refine().

The test in question is written like this:

    assert {evens.removeKey 5} shouldRaise (NoSuchObject | TypeError)
    // TypeError if the type parameters are checked, NoSuchObejct otherwise

Nothing wrong with that test. Obviously, this was my mistake; I need to annotate the shouldRaise parameter with Pattern, not ExceptionKind.

Unfortunately, that doesn’t help, because a string is a Pattern, and my original error is no longer caught. Indeed, all the primitive objects are patterns. I can't think of a type annotation that would catch this error.

Is this a design issue with Patterns?

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or unsubscribe.

apblack commented 4 years ago

I quite like using # as a prefix operator on any EqualityObject, returning a pattern.

The specification, I think, is that #o is a pattern that matches any object to which o is equal:

# o -> Pattern
# o.matches(x) { o == x }

The implementation is almost as simple:

method prefix # → Pattern {
    object {
        use BasePattern
        method matches(x) { outer == x }
    }
}

This would not stop us from having objects that are Patterns in addition to something else — the example that I have in mind is Exceptions, where it is convenient to be able to use the same name in raise and catch. But Numbers, Strings and Booleans would no longer be patterns — they would need explicit conversion.

As James points out, this change would mean that a|b, where a:Boolean, would become a NoSuchMethod error, rather than it being a pattern that will cause an error later.

apblack commented 1 year ago

I've been writing some test code using patterns. One of them went like this:

method describeNumber(a:Number) {
     match (a)
            case { >30 → "{a} >30"}
            case { <30 → "{a} <30"}
            case { 30 → "{a} =30 "}
}

This works as expected. However, at first, I wrote it like this:

method describeNumber(a:Number) {
     match (a)
            case { >30 → "{a} >30"}
            case { <30 → "{a} <30"}
            case { =30 → "{a} =30 "}
} 

This gives one

NoSuchMethod: no method prefix= on number 30 (defined in module built-in library). Did you mean prefix-, prefix<, prefix> or prefix≤?

It seems to me that prefix = is exactly the operator that we have been looking for, for converting Numbers (and Strings, and Booleans) into patterns. A Number as a pattern matches exactly those numbers equal to it; the same is true for Strings and Booleans.

While writing down (in the "standard" dialect) the types for String, Number, etc, I was quite puzzled as to why String has a method isType (which answers false), as well as methods &(_) and |(_). The answer is: because Strings are patterns. I've gradually come to the conclusion that "self-matching objects" are a historical mistake. Strings, Numbers and Booleans should not be patterns, but should have a prefix operator = that converts them into patterns.

Or, maybe it should be prefix == ?

KimBruce commented 1 year ago

I like marking things that are used as patterns. I'd prefer ==30 (for consistency), but could live with a single "=".

On Sun, Feb 26, 2023 at 9:23 AM Andrew Black @.***> wrote:

I've been writing some test code using patterns. One of them went like this:

method describeNumber(a:Number) {

 match (a)

        case { >30 → "{a} >30"}

        case { <30 → "{a} <30"}

        case { 30 → "{a} =30 "}

}

This works as expected. However, at first, I wrote it like this:

method describeNumber(a:Number) {

 match (a)

        case { >30 → "{a} >30"}

        case { <30 → "{a} <30"}

        case { =30 → "{a} =30 "}

}

This gives one

NoSuchMethod: no method prefix= on number 30 (defined in module built-in library). Did you mean prefix-, prefix<, prefix> or prefix≤?

It seems to me that prefix = is exactly the operator that we have been looking for, for converting Numbers (and Strings, and Booleans) into patterns. A Number as a pattern matches exactly those numbers equal to it; the same is true for Strings and Booleans.

While writing down (in the "standard" dialect) the types for String, Number, etc, I was quite puzzled as to why String has a method isType (which answers false), as well as methods &() and |(). The answer is: because Strings are patterns. I've gradually come to the conclusion that "self-matching objects" are a historical mistake. Strings, Numbers and Booleans should not be patterns, but should have a prefix operator = that converts them into patterns.

Or, maybe it should be prefix == ?

— Reply to this email directly, view it on GitHub https://github.com/gracelang/language/issues/183#issuecomment-1445414176, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAN2D6RQQR3TRY23RFCWIS3WZOGSTANCNFSM4JLJBOYA . You are receiving this because you are subscribed to this thread.Message ID: @.***>

-- Prof. Kim Bruce Computer Science Pomona College