Wiki

Ticket #280 (closed defect: invalid)

Opened 6 years ago

Last modified 6 years ago

'or require' and 'and ensure' not working as expected on overriden method

Reported by: cmitchell Owned by: Charles
Priority: major Milestone:
Component: Cobra Compiler Version: 0.8.0
Keywords: Cc:

Description

Given the following Cobra code:

@args -t:lib

namespace CobraLib

	class Person
		pro firstName from var = ''
		pro lastName from var = ''

		def speak() as String
			return 'My name is [.firstName] [.lastName]'

	class Speaker
		inherits Person

		def speak() as String is override
			or require
				.firstName is not ''
				.lastName is not ''
			and ensure
				result.startsWith('My name is')
			body
				return 'My name is not what you think it is'

When I compile it and reference it in a c# console app like this:

using System;
using CobraLib;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var p = new Speaker();

            Console.WriteLine(p.Speak());
            Console.ReadLine();
        }
    }
}

My expectation is that the code contracts will cause a runtime exception. However, the code executes without issue.

Likewise, when I add the code contracts to the Person class and use that class in the c# code, the code contracts cause an exception as expected.

Change History

Changed 6 years ago by cmitchell

Actually, when I add the properties to the Speaker class and change the "or require" and "and ensure" to just "require" and "ensure", the c# code works as expected. Sorry for the mix-up.

Changed 6 years ago by Charles

  • status changed from new to assigned
  • owner set to Charles

The first thing that stands out to me is the use of "is (not)" for comparisons as in:

.firstName is not ''
# which should be
.firstName <> ''

Use "is" and "is not" for identity/pointer/reference comparison.

Use "==" and "<>" for equality/contents comparison.

In any case, I'll give this code a try with that adjustment and see what's up.

Changed 6 years ago by Charles

  • status changed from assigned to closed
  • resolution set to invalid

So putting the string comparison issue aside, the behavior is still what you describe and is actually correct. What you're experiencing here is this:

A subclass cannot strengthen the requirement of a superclass' method. It can only weaken it.

This comes out of the very nature of a contract combined with the use of base classes in OO. For example:

peopleList = List<of Person>()
# populate the peopleList with instances of Person, Speaker, etc.
# then make them speak:
for p in peopleList
    p.speak

When you write the code "p.speak" where "p" is of type "Person", it is reasonable to expect that you know upfront what the requirements for doing so are without any later surprises. Subclasses of Person are not allowed to add more requirements which would break the contract.

Note that even in non-contract languages like Java and C# there are already things that subclasses are not allowed to do, such as remove a public property or method found in the superclass. Otherwise, code that uses the base class wouldn't make sense.

This is one of the trickier aspects of contracts to learn. It's covered by the Eiffel docs and tutorials on contracts btw.

The solutions for your specific example include at least:

* Put the strong requirement up at the Person.speak level.

* Put a requirement at the Person.speak level of ".canSpeak" and then make Person return true for that method and have Speaker override it.

HTH

If you have more questions, you can ask in the forums, here or on IRC.

Note: See TracTickets for help on using tickets.