uqbar-project / wollok

Wollok Programming Language
GNU General Public License v3.0
61 stars 16 forks source link

Unexpected behaviour when assert.throwsException(aBlock) is invoked with no closure argument #961

Open lgassman opened 8 years ago

lgassman commented 8 years ago

If no closure is passed as argument, the assert object throws and catchs an Exception. Then, the test doesn't fail.

Example: You suppose this test fails when is executed:

test "pepita no puede volar si no tiene energia" {
    pepita.energia(0)
    assert.throwsException({=>pepita.volar(100)})
}

but, if an student forgot to use a closure, this test will end ok.

test "pepita no puede volar si no tiene energia" {
    pepita.energia(0)
    assert.throwsException(pepita.volar(100))
}
javierfernandes commented 8 years ago

This is the most annoying type of error on "non-statically typed+checked" languages. I wonder if our idea delaying as much as possible types is even right. In this particular case (and many others) you end up explaining that he was sending an incorrect type of object as param, he should be sending a closure instead of the return of volar().

So, eventhough the "Closure" class is not written there anywhere you still teach about types. And the way to quickly fix this is to add a check on the assert method to check the type of the param object and throw an exception like "Expecting a closure but got ...". Unless you just rethrow the "... does not understand apply()" (which is the closure's execute message).

So, regarding pedagogical experience, I would say that you don't want the user to write types, or be concerned about types on their own code. But still he needs to be aware of the types of parameters when using wolllok (or 3rd party) code. This is really difficult to resolve

On Wed, Aug 31, 2016 at 5:05 PM, Leo Gassman notifications@github.com wrote:

If no closure is passed as argument, the assert object throws and catchs an Exception. Then, the test doesn't fail.

Example: You suppose this test fails when is executed:

test "pepita no puede volar si no tiene energia" { pepita.energia(0) assert.throwsException({=>pepita.volar(100)}) }

but, if an student forgot to use a closure, this test will end ok.

test "pepita no puede volar si no tiene energia" { pepita.energia(0) assert.throwsException(pepita.volar(100)) }

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/uqbar-project/wollok/issues/961, or mute the thread https://github.com/notifications/unsubscribe-auth/AEORWI3sF4sRjccwSALa4x2rB73KrHDBks5qld57gaJpZM4JyAeb .

lgassman commented 8 years ago

I think this is difficult because the assertions are messages over an assert object. If you extends the "testing DSL" to avoid to send a message to the assert object, you will be able to avoid the closure argument when you write a test. Let it use the object-message paradigm only for student code.

One possible Example:

test "my test" {

 assert myBusinessObject.isFoo()
 assert myBusinessObject.getBar() equals "bar"
 assert exception myBusinessObject.doSomething()

}

On Wed, Aug 31, 2016 at 5:32 PM, javierfernandes notifications@github.com wrote:

This is the most annoying type of error on "non-statically typed+checked" languages. I wonder if our idea delaying as much as possible types is even right. In this particular case (and many others) you end up explaining that he was sending an incorrect type of object as param, he should be sending a closure instead of the return of volar().

So, eventhough the "Closure" class is not written there anywhere you still teach about types. And the way to quickly fix this is to add a check on the assert method to check the type of the param object and throw an exception like "Expecting a closure but got ...". Unless you just rethrow the "... does not understand apply()" (which is the closure's execute message).

So, regarding pedagogical experience, I would say that you don't want the user to write types, or be concerned about types on their own code. But still he needs to be aware of the types of parameters when using wolllok (or 3rd party) code. This is really difficult to resolve

javierfernandes commented 8 years ago

Yes, I also agree with that. I also would like to extends the testing DSL. Today it is really really basic. Too basic for me. It doesn't express the concepts of all tests like prepare / act / assert ( http://c2.com/cgi/wiki?ArrangeActAssert) We are also missing the before. And I also like the nested scopes that test have in NodeJS. Given the fact that we don't need / want to declare tests as classes having special syntax of all this concepts would really be awesome (also with autocomplete and templates).

On Wed, Aug 31, 2016 at 6:07 PM, Leo Gassman notifications@github.com wrote:

I think this is difficult because the assertions are message over an assert object. If you extends the "testing DSL" to avoid to send a message to the assert object, you will be able to avoid the closure argument when you write a test. Let it use the object-message paradigm only for student code.

One possible Example:

test "my test" {

assert myBusinessObject.isFoo() assert myBusinessObject.getBar() equals "bar" assert exception myBusinessObject.doSomething()

}

On Wed, Aug 31, 2016 at 5:32 PM, javierfernandes <notifications@github.com

wrote:

This is the most annoying type of error on "non-statically typed+checked" languages. I wonder if our idea delaying as much as possible types is even right. In this particular case (and many others) you end up explaining that he was sending an incorrect type of object as param, he should be sending a closure instead of the return of volar().

So, eventhough the "Closure" class is not written there anywhere you still teach about types. And the way to quickly fix this is to add a check on the assert method to check the type of the param object and throw an exception like "Expecting a closure but got ...". Unless you just rethrow the "... does not understand apply()" (which is the closure's execute message).

So, regarding pedagogical experience, I would say that you don't want the user to write types, or be concerned about types on their own code. But still he needs to be aware of the types of parameters when using wolllok (or 3rd party) code. This is really difficult to resolve

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/uqbar-project/wollok/issues/961#issuecomment-243902177, or mute the thread https://github.com/notifications/unsubscribe-auth/AEORWDfiJjDR4CmEX57mn-VSZ9PzIRaDks5qle0dgaJpZM4JyAeb .

npasserini commented 8 years ago

Ok, several stuff here 1) I do not understand why the test works, if the method expects to receive a block, it should evaluate the block... and then you will be in trouble because you passed a wrong object there.

1.1) In any case, I think we could change the assertException method and provide a clear error message.

2) As we already discussed, I think that introducing a DSL will add an unnecessary break to the object/message pattern. I think that pedagogically, the pattern is very important. I do not say we couldn't break it, we have done so in several points in the language (if, try/catch, new, ...) but I think that this breakings should be as less as possible and have a good motivation.

I think that the value added by the DSL is not so big to justify another exception. Instead it would be nicer to work on static checking of the examples or even nice runtime messages.

3) As an addition to the language, I think that the nicest (already discussed few days ago) would be "doclets like" statically-checked type annotations.

So ideally we should change method definition like this:

/**

(It is just a quick idea, could be completely different)

So for the unexperienced programmer looks like just documentation, doesn't have to know about typing concepts, doesn't have to provide it for his own methods, but is statically (and optionally) checked.

4) About the typing itself... I have a few ideas but also a few questions, that maybe have to be responded only by experience, building a type system and checking how it works in practice.

The easy definitions are that we need type checking... and that the student is required to have some kind of typing rules. But typing rules for objects are really difficult: I strongly doubt that a first-year student will ever be able to write a parametric type. So his understanding of typing is intuitive, i.e. a type is a set of messages that in some context an object is expected to be able to respond to. So we do talk about typing in our subjects, and students have a basic understanding of it.

But if you write

method assertException(block: ()=>void)

you will blow up their minds, and none of them will follow.

Otherwise... you have to build a simpler type system, that will rule out several programs (or allow them to run and produce runtime errors, as you prefere).

I think that is kind of the easy part. The more difficult part is: what happens if your typing system detects a typing error in student's code, will Wollok IDE be able to explain the error to that student with intuitive comprehension of typing? Will the teacher be able to explain it or will be demanded to provide a magical "do this without asking" solution? Will this situation arise frequently? Wouldn't it better to allow the student to run the code and then in a live environment show him that an object does not understand the desired message? Couldn't also happen that statically we have a potential typing error but in program run it does not arise?

These are all questions that require field experience to be answered.

In the meantime, my nearest experience is with Haskell. Haskell is a strongly, statically, very strict typed language, with inference. We should think if my previous concerns arise in working with Haskell (I think they do) and try to extrapolate how would it work with an even more complicated type system.

On Wed, Aug 31, 2016 at 11:27 PM, javierfernandes notifications@github.com wrote:

Yes, I also agree with that. I also would like to extends the testing DSL. Today it is really really basic. Too basic for me. It doesn't express the concepts of all tests like prepare / act / assert ( http://c2.com/cgi/wiki?ArrangeActAssert) We are also missing the before. And I also like the nested scopes that test have in NodeJS. Given the fact that we don't need / want to declare tests as classes having special syntax of all this concepts would really be awesome (also with autocomplete and templates).

On Wed, Aug 31, 2016 at 6:07 PM, Leo Gassman notifications@github.com wrote:

I think this is difficult because the assertions are message over an assert object. If you extends the "testing DSL" to avoid to send a message to the assert object, you will be able to avoid the closure argument when you write a test. Let it use the object-message paradigm only for student code.

One possible Example:

test "my test" {

assert myBusinessObject.isFoo() assert myBusinessObject.getBar() equals "bar" assert exception myBusinessObject.doSomething()

}

On Wed, Aug 31, 2016 at 5:32 PM, javierfernandes < notifications@github.com

wrote:

This is the most annoying type of error on "non-statically typed+checked" languages. I wonder if our idea delaying as much as possible types is even right. In this particular case (and many others) you end up explaining that he was sending an incorrect type of object as param, he should be sending a closure instead of the return of volar().

So, eventhough the "Closure" class is not written there anywhere you still teach about types. And the way to quickly fix this is to add a check on the assert method to check the type of the param object and throw an exception like "Expecting a closure but got ...". Unless you just rethrow the "... does not understand apply()" (which is the closure's execute message).

So, regarding pedagogical experience, I would say that you don't want the user to write types, or be concerned about types on their own code. But still he needs to be aware of the types of parameters when using wolllok (or 3rd party) code. This is really difficult to resolve

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/uqbar-project/wollok/issues/961# issuecomment-243902177, or mute the thread https://github.com/notifications/unsubscribe- auth/AEORWDfiJjDR4CmEX57mn-VSZ9PzIRaDks5qle0dgaJpZM4JyAeb .

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/uqbar-project/wollok/issues/961#issuecomment-243907835, or mute the thread https://github.com/notifications/unsubscribe-auth/AEa1OXgO30Eh7WhvbCVhQk2Pw6FT0FbFks5qlfHBgaJpZM4JyAeb .

javierfernandes commented 8 years ago

Not sure about Haskell, but in Smalltalk the "avoid types" simplifications just moves the problem somewhere else. As in this particular case the error won't be "The method expected a Closure but you passed a ..." it will be ".... doesn't understand the message apply()". Because somewhere the method implementation should be executing the block.

(BTW, I think this is not happening, because that method may be native. Anyway, it should be happening, no matter if it is native, but just a thought).

The thing is that, I've seen this situation, and eventhough the error message doesn't contain any "weird typing message", not even a "type", the students don't understand it. Because they think "I'm not sending that apply() message. I don't understand why it say so. Who is sending that message ? I don't care.. This is confusing". They don't realise that the problem has been delayed (is like sending a message wrong message in javascript the error gets shift to something like "cannot execute undefined" -because it didn't resolve the method). In this case the error is WITHIN the assertion method, but BECAUSE we previously used it wrong passing a wrong object. If the assertion method calls another method, and that one another.. etc.. before using the parameter then the error gets even worse.

Personally I think that this is breaking encapsulation. As there is no contract on the method signature, when it fails, you need to mentally break encapsulation, go there and try to understand the method implementation to understand how to use it. I think that sucks. And it is the worst side-effect of a non-statically checked type system.

Anyway, this is probably not for Nico, as he already knows this :) But could be like a way to stop, and rethink what we already know.

Type Annotations seems like a safe path. I would like to think if there's no other way, like unexplored.

For example you example I agree with your example of block types. But that case can be relaxed if we have type aliases replacing type literals with nominal types

type VoidBlock = ()=>void

method assertException(block : VoidBlock)

What if we can build type definitions in a more human readable form ?

method assertException(block : Block that returns void and accepts no-args)

It is just syntax sugar.

Eventually there's a type Block with named type parameters like Block[accepts*, returns]

Ok... got a lot verbose. Could be even more like this:

method assertException(block is a Block that returns void and accepts no-args)

If the editor should that bold part like in greyed, or like "less important" than the parameter name. Could avoid distraction.

This could also apply for collections

method assertContains(collection is a Collection of E, element is a E)

The "of" could be like the default syntax for single-type-param. Or for any number like Comparator of Person and Dogs

Does typing gets so cryptical for newcomers if we introduce such a "natural language" ? I know that experienced programmer would hate this, because it is so much verbose than the <> or [] syntax. But well..

On Thu, Sep 1, 2016 at 7:06 AM, Nico Passerini notifications@github.com wrote:

Ok, several stuff here 1) I do not understand why the test works, if the method expects to receive a block, it should evaluate the block... and then you will be in trouble because you passed a wrong object there.

1.1) In any case, I think we could change the assertException method and provide a clear error message.

2) As we already discussed, I think that introducing a DSL will add an unnecessary break to the object/message pattern. I think that pedagogically, the pattern is very important. I do not say we couldn't break it, we have done so in several points in the language (if, try/catch, new, ...) but I think that this breakings should be as less as possible and have a good motivation.

I think that the value added by the DSL is not so big to justify another exception. Instead it would be nicer to work on static checking of the examples or even nice runtime messages.

3) As an addition to the language, I think that the nicest (already discussed few days ago) would be "doclets like" statically-checked type annotations.

So ideally we should change method definition like this:

/**

  • Params:
  • - block: Block(arguments: none, result: doesn't care). */ method assertException(block)

(It is just a quick idea, could be completely different)

So for the unexperienced programmer looks like just documentation, doesn't have to know about typing concepts, doesn't have to provide it for his own methods, but is statically (and optionally) checked.

4) About the typing itself... I have a few ideas but also a few questions, that maybe have to be responded only by experience, building a type system and checking how it works in practice.

The easy definitions are that we need type checking... and that the student is required to have some kind of typing rules. But typing rules for objects are really difficult: I strongly doubt that a first-year student will ever be able to write a parametric type. So his understanding of typing is intuitive, i.e. a type is a set of messages that in some context an object is expected to be able to respond to. So we do talk about typing in our subjects, and students have a basic understanding of it.

But if you write

method assertException(block: ()=>void)

you will blow up their minds, and none of them will follow.

Otherwise... you have to build a simpler type system, that will rule out several programs (or allow them to run and produce runtime errors, as you prefere).

I think that is kind of the easy part. The more difficult part is: what happens if your typing system detects a typing error in student's code, will Wollok IDE be able to explain the error to that student with intuitive comprehension of typing? Will the teacher be able to explain it or will be demanded to provide a magical "do this without asking" solution? Will this situation arise frequently? Wouldn't it better to allow the student to run the code and then in a live environment show him that an object does not understand the desired message? Couldn't also happen that statically we have a potential typing error but in program run it does not arise?

These are all questions that require field experience to be answered.

In the meantime, my nearest experience is with Haskell. Haskell is a strongly, statically, very strict typed language, with inference. We should think if my previous concerns arise in working with Haskell (I think they do) and try to extrapolate how would it work with an even more complicated type system.

On Wed, Aug 31, 2016 at 11:27 PM, javierfernandes < notifications@github.com> wrote:

Yes, I also agree with that. I also would like to extends the testing DSL. Today it is really really basic. Too basic for me. It doesn't express the concepts of all tests like prepare / act / assert ( http://c2.com/cgi/wiki?ArrangeActAssert) We are also missing the before. And I also like the nested scopes that test have in NodeJS. Given the fact that we don't need / want to declare tests as classes having special syntax of all this concepts would really be awesome (also with autocomplete and templates).

On Wed, Aug 31, 2016 at 6:07 PM, Leo Gassman notifications@github.com wrote:

I think this is difficult because the assertions are message over an assert object. If you extends the "testing DSL" to avoid to send a message to the assert object, you will be able to avoid the closure argument when you write a test. Let it use the object-message paradigm only for student code.

One possible Example:

test "my test" {

assert myBusinessObject.isFoo() assert myBusinessObject.getBar() equals "bar" assert exception myBusinessObject.doSomething()

}

On Wed, Aug 31, 2016 at 5:32 PM, javierfernandes < notifications@github.com

wrote:

This is the most annoying type of error on "non-statically typed+checked" languages. I wonder if our idea delaying as much as possible types is even right. In this particular case (and many others) you end up explaining that he was sending an incorrect type of object as param, he should be sending a closure instead of the return of volar().

So, eventhough the "Closure" class is not written there anywhere you still teach about types. And the way to quickly fix this is to add a check on the assert method to check the type of the param object and throw an exception like "Expecting a closure but got ...". Unless you just rethrow the "... does not understand apply()" (which is the closure's execute message).

So, regarding pedagogical experience, I would say that you don't want the user to write types, or be concerned about types on their own code. But still he needs to be aware of the types of parameters when using wolllok (or 3rd party) code. This is really difficult to resolve

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/uqbar-project/wollok/issues/961# issuecomment-243902177, or mute the thread https://github.com/notifications/unsubscribe- auth/AEORWDfiJjDR4CmEX57mn-VSZ9PzIRaDks5qle0dgaJpZM4JyAeb .

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/uqbar-project/wollok/issues/961# issuecomment-243907835, or mute the thread https://github.com/notifications/unsubscribe-auth/ AEa1OXgO30Eh7WhvbCVhQk2Pw6FT0FbFks5qlfHBgaJpZM4JyAeb .

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/uqbar-project/wollok/issues/961#issuecomment-244034864, or mute the thread https://github.com/notifications/unsubscribe-auth/AEORWAg3uMxV82oxzQOalle28O4MgwnKks5qlqOhgaJpZM4JyAeb .

npasserini commented 8 years ago

Well, I think that this goes in the same line as my proposal:

/**

The comment is meant to be parsed. The only trick is that putting the "parseable natural language" in the comment, it looks as just documentation for the initial student.

Regarding the other issue ("the problem is delayed"), I wonder if it would make sense to have dynamic (manual) type checks where the inference is not smart enough, just to provide a fail-fast mechanism and better error messages. This is not for the students, of course, but maybe we could take advantage of such a feature in some specific places.

But still, for whatever decision we make in this area I would consider it temporary until we get real experience with real students. For example, what should be the error message here?

npasserini commented 7 years ago

So, there has been a while without advances in this thread. I am putting this in the backlog, with the objective of providing a minimal solution: if you call #throwsException with a non-closure you should have a red test. That is for sure, and it is easy.

Then we come to the issue of deciding how to inform the problem, I do not have a definitive answer to that problem.

We've been considering the possibility of including a "dynamic manual type testing mechanism" intto the language. But this is a bit scary, so I will defer it for now. We could work around it implementing in Java the methods requiring such features.

fdodino commented 5 years ago

Some news from today:

assert.throwsException(pepita.volar(100))

throws an error if method is void. But we still have the same problem when message returns a value. We should fix it.