The Artima Developer Community
Sponsored Link

Weblogs Forum
What if Constant Values were also valid Types?

41 replies on 3 pages. Most recent reply: Jan 20, 2006 10:43 AM by Gregor Zeitlinger

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 41 replies on 3 pages [ « | 1 2 3 ]
Terje Slettebø

Posts: 205
Nickname: tslettebo
Registered: Jun, 2004

Re: What if Constant Values were also valid Types? Posted: Jan 20, 2006 1:00 AM
Reply to this message Reply
Advertisement
> > > > So be carefull. 42 is subtype of const int, but not
> > of int.
> > >
> > > 'const int' is a type?
> >
> > Naturally. Consider:
> >
> > const int a=123;
> >
> > a=456; // Error, "a" is const (the type of "a" is
> "const
> > int")
>
> You don't need 'const int' to be a type from the language
> definition perspective to make this work.
>
> > int b=123;
> >
> > b=456; // Ok (the type of "b" is "int")
> >
> > They definitely support different operations, which
> means
> > they are different types.
> >
> > >That seems pretty undesireable and unintuitive.
> >
> > I don't think so. What do you find strange about it?
>
> Constant is an adjective i.e. a modifier of the variable.
> I'm not saying that implementing the feature in this way
> y is incorrect but defining it this way at the language
> level seems like backwards design.

Yes, const is a "modifier"/"qualifier" of the base type. What about "long", do you find that "backwards", too? How about "unsigned"? Should they all just be considered "the same type"...?

Similarly, "*" also modifies the base type, making it into a pointer to the base type, "&" makes it a reference, and so on. All these "modifiers" build up the complete type.

By making "const" part of the type system, you can do elegant things like overloading on const (which you couldn't do if it wasn't part of the type), which is very important in modern C++. For example, it provides valuable compiler-enforced documentation of which member functions can alter an object, and which can't.

To quote Kevlin Henney (http://www.cuj.com/documents/s=7999/cujcexp1901henney/):

"In C++, const divides the novice from the experienced: on one side lies a source of confusion; on the other a means of clarification. Explicit annotation of modifier from query functions can benefit a system, and this is a concept that can be expressed in C++ using type qualifiers."

Not including "const" is one of the cases where I think Java threw the baby out with the bath water. "final" is a poor substitute, and can't be used for overloading purposes, for example (and have somewhat different semantics).

> From an 'ergonomic'
> view, good design isn't derived from ease of
> implementation.

I think the inclusion of "const" in the type system was all about good design, and not about implementation. What makes you think it's easier to make it part of the type system, than not? It seems the other way around, to me.

What I find elegant about C++ is that "anything" is a type: built-in types, classes, functions, pointers, references, and so on. Having such a "unified" type system is pretty powerful, especially coupled with traits (not the kind that has been discussed in other threads, which is something like interfaces)/metaprogramming, where you can perform compile-time program transformations, based on embedded domain knowledge and configuration information provided by the user (such as matrix libraries, where you may construct different matrix types depending on the kind you want).

> A system where there is an implicit doppleganger type for
> every declared type seems extremely inelegant to me.

It's not a "doppleganger"; it's a distinct type, with distinct operations (a subset of the base type's). Treating them as "the same type", _that_ would be confusing, and inelegant (different operations, but "same" type...).

Kresimir Cosic

Posts: 34
Nickname: kreso1
Registered: Jan, 2006

Re: What if Constant Values were also valid Types? Posted: Jan 20, 2006 1:30 AM
Reply to this message Reply
> A system where there is an implicit doppleganger type for
> every declared type seems extremely inelegant to me.

No, it's extremely powerfull. 'const' allows you to make another type from allready known type. Like in

template <typename T>
bool isGreater(const T &x,const T &y)
{
return x>y;
}

This demonstrates construction of type const T from type T, similar to how type *T is constructed from T.

> As I said before, this would only be useful if Heron
> supports dynamic dispatch.

Agreed. But instead of multiple dispatch, single dispatch is enough:

struct Int
{
bool isPrime() const { ... }
};
struct 42 : public const Int
{
bool isPrime() const { return false; }
};

Int &a=42; // ... possible
a.isPrime(); //calls 42::isPrime
42.isPrime(); //same as above

I hope I got it right.

Kresimir Cosic

Posts: 34
Nickname: kreso1
Registered: Jan, 2006

Re: What if Constant Values were also valid Types? Posted: Jan 20, 2006 1:43 AM
Reply to this message Reply
But will it break down on value semantics?

Int c=42;
c.IsPrime() // Type 42 looks useless now

In my opinion, we still have no good examples where it could be usefull.

Terje Slettebø

Posts: 205
Nickname: tslettebo
Registered: Jun, 2004

Re: What if Constant Values were also valid Types? Posted: Jan 20, 2006 2:07 AM
Reply to this message Reply
> @Terje Slettebø
>
> > I'm not sure if I understand what you mean.
> > Could you provide some example code showing what
> > it is C++ doesn't let you express?
>
> Of course, that's simple:
> class Rectangle
> {
>      public:
>      virtual float GetWidth()  const =0;
>      virtual float GetHeight() const =0;
>      virtual void  SetWidth(float w)=0;
>      virtual void  SetHeight(float h)=0;
> };
> class Square : 
>     public const Rectangle // I cant do this
> {
>      public:
>      virtual float GetWidth()  const =0;
>      virtual float GetHeight() const =0;
>      virtual float GetSize()   const =0;
>      virtual void  SetSize(float s)=0;
> };

> Instead, I have to write another class const_Rectangle.
> Ugly.

A couple of things: You shouldn't inherit a Square from a Rectangle (or the other way around). This is one of the classic errors of OO, as it violates LSP (example: Square inherits SetWidth()/SetHeight(). What should these do? Should SetWidth() also call SetHeight(), to ensure it's still a Square? There are other problems with this inheritance, as well).

Secondly: Why are you inheriting from a class that has modifying functions? If you intend the derived class to be immutable, we again have a case of violating LSP (an object of the derived class is not substitutable for an object of the base class).

You can get the effect of what you want by separating the nonmutating member functions in the base class (modifying the example, due to the Square/Rectangle problem):

class ConstRectangle
{
public:
  virtual float GetWidth()  const =0;
  virtual float GetHeight() const =0;
};
 
class Rectangle : public ConstRectangle
{
public:
  virtual void  SetWidth(float w)=0;
  virtual void  SetHeight(float h)=0;
};
However, you're right in that we can't write something like (http://www.cuj.com/documents/s=7999/cujcexp1901henney):

class const Rectangle
{
  // ...
};
 
class Rectangle : public const Rectangle
{
  // ...
};
i.e. letting "const <type>" be something you can inherit from, but the above code gives the same effect.

I agree that the example you provided was ugly, in several ways, but that's just not the way to do it...

Kresimir Cosic

Posts: 34
Nickname: kreso1
Registered: Jan, 2006

Re: What if Constant Values were also valid Types? Posted: Jan 20, 2006 2:28 AM
Reply to this message Reply
> A couple of things: You shouldn't inherit a Square from a
> Rectangle (or the other way around). This is one of the
> classic errors of OO, as it violates LSP (example: Square

Yes, I know for this example, that's why I put it. Also
note that my example does not violate LSP because Square does not inherit from Rectangle, but from const Rectangle.

> inherits SetWidth()/SetHeight(). What should these do?
> Should SetWidth() also call SetHeight(), to ensure it's
> still a Square? There are other problems with this
> inheritance, as well).

Square doesn't inherit SetWidth()/SetHeight().

> Secondly: Why are you inheriting from a class that has
> modifying functions? If you intend the derived class to be

I'm not.

> You can get the effect of what you want by separating the
> nonmutating member functions in the base class (modifying
> the example, due to the Square/Rectangle problem):

> I agree that the example you provided was ugly, in several
> ways, but that's just not the way to do it...

a) It's ugly, as you said.
b) const Rectangle allows more than const_Rectangle. When templates are in question (see my example above), const allows specifying a new type from known type. Like pointers. Like vector<T> which creates new type vector<T> from known type T. Like iterator types are specified etc....

Terje Slettebø

Posts: 205
Nickname: tslettebo
Registered: Jun, 2004

Re: What if Constant Values were also valid Types? Posted: Jan 20, 2006 5:20 AM
Reply to this message Reply
> > A couple of things: You shouldn't inherit a Square from
> a
> > Rectangle (or the other way around). This is one of the
> > classic errors of OO, as it violates LSP (example:
> Square
>
> Yes, I know for this example, that's why I put it. Also
> note that my example does not violate LSP because Square
> does not inherit from Rectangle, but from const
> Rectangle.

Ok, as I understand from your posting, now, when inheriting using "const", you only inherit the "const" member functions? It was a little hard to tell, as your example didn't describe the semantics of it.

It may matter less, as this was just an example, but Square may end up with redundant data, this way (both width and height). However, as long as you can't _change_ this data in the base class, I agree that it would work. Conceptually, I think it's bunk, though: :) You're inheriting getWidth() and getHeight(), which, for a Square, always will be the same, and you add getSize() in the derived class. In short, you get a bloated interface, so as an example of the usefulness of the proposed feature, I don't think this is a good one.

>> ways, but that's just not the way to do it...

> a) It's ugly, as you said.

Yes, but I provided an example to get the same effect, without the uglyness. Being able to "const inherit" from a class, so you only inherit the const member functions (what about any data?) smells like "Refused Bequest" (http://wiki.java.net/bin/view/People/SmellsToRefactorings) to me. What you're saying is that you're not inheriting from "Rectangle", but from "const Rectangle", but just making an object const doesn't usually _remove_ functionality (it'll give an error if you call a non-const member function, but it's still there).

In short, I don't understand the advantage of this, over mine/Kevlin's example of defining the non-mutating member functions in the base class, and inheriting from that, adding mutating member functions. That seems conceptually cleaner to me.

> b) const Rectangle allows more than const_Rectangle. When
> templates are in question (see my example above), const
> allows specifying a new type from known type. Like
> pointers. Like vector<T> which creates new type vector<T>
> from known type T. Like iterator types are specified
> etc....

You don't have to argue the usefulness of "const" to me. ;) I've already done that myeself in other postings, here. At least for the "const" as it exists in C++.

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: What if Constant Values were also valid Types? Posted: Jan 20, 2006 6:48 AM
Reply to this message Reply
> Yes, const is a "modifier"/"qualifier" of the base type.
> What about "long", do you find that "backwards", too? How
> about "unsigned"? Should they all just be considered "the
> same type"...?

No, perhaps I wasn't clear. Long is the type. Const is just a modifier on how that variable should be used. That's how I would expect it to behave.

> Similarly, "*" also modifies the base type, making it into
> a pointer to the base type, "&" makes it a reference, and
> so on. All these "modifiers" build up the complete type.

They don't define a new type, they describe how the variable relates to a type. I suppose you could think of a pointer to an int being a distinct type but it's not changing the behavior of int or subclassing it.

> By making "const" part of the type system, you can do
> elegant things like overloading on const (which you
> couldn't do if it wasn't part of the type), which is very
> important in modern C++. For example, it provides valuable
> compiler-enforced documentation of which member functions
> can alter an object, and which can't.

It seems to me that a much more elegant system exists.

> To quote Kevlin Henney
> (http://www.cuj.com/documents/s=7999/cujcexp1901henney/):
>
> "In C++, const divides the novice from the experienced: on
> one side lies a source of confusion; on the other a means
> of clarification. Explicit annotation of modifier from
> query functions can benefit a system, and this is a
> concept that can be expressed in C++ using type
> qualifiers."

'Type qualification' doesn't imply 'type creation' to me. Don't get me wrong, I understand why, it's just something that only makes sense if you've got your head in the weeds IMO.

> Not including "const" is one of the cases where I think
> Java threw the baby out with the bath water. "final" is a
> poor substitute, and can't be used for overloading
> purposes, for example (and have somewhat different
> semantics).

Technically, const is a Java keyword. Final is not meant to be the same thing as const. Adding support for const is often requested. I doubt it will be added and I also have doubts that it would add a lot of value.

> > From an 'ergonomic'
> > view, good design isn't derived from ease of
> > implementation.
>
> I think the inclusion of "const" in the type system was
> all about good design, and not about implementation. What
> makes you think it's easier to make it part of the type
> system, than not? It seems the other way around, to me.

I would think it would be easier to have a unified typuing system in the compiler and not have to write special cases for const types.

> What I find elegant about C++ is that "anything" is a
> type: built-in types, classes, functions, pointers,
> references, and so on. Having such a "unified" type system
> is pretty powerful, especially coupled with traits (not
> the kind that has been discussed in other threads, which
> is something like interfaces)/metaprogramming, where you
> can perform compile-time program transformations, based on
> embedded domain knowledge and configuration information
> provided by the user (such as matrix libraries, where you
> may construct different matrix types depending on the kind
> you want).
>
> > A system where there is an implicit doppleganger type
> for
> > every declared type seems extremely inelegant to me.
>
> It's not a "doppleganger"; it's a distinct type, with
> distinct operations (a subset of the base type's).
> Treating them as "the same type", _that_ would be
> confusing, and inelegant (different operations, but "same"
> type...).

Perhaps I should explain what I mean. It seems to me that a natural const system would have two parts, member markers and variable markers.

If I declare an Integer type:
public class Integer {
    private int value;
 
    public Integer(final int value) {
        this.value = value;
    }
 
    public void setValue(int newValue) {
        value = newValue;
    }
 
    const public int getValue() {
        return value;
    }
}


Then I can declare varables as either const or normally:

Integer i = new Integer(0);
const Integer j = new Integer(1);
 
i.setValue(2);  // OK
j.setValue(3); // Error: setValue is not const
 
i = j; // Error: cannot assign const to non-const
j = i; // Error: cannot assign non-const to const


In terms of how you can use these instances, it's pretty much equivalent to treating them as different types but there's only one Integer type.

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: What if Constant Values were also valid Types? Posted: Jan 20, 2006 6:52 AM
Reply to this message Reply
> A couple of things: You shouldn't inherit a Square from a
> Rectangle (or the other way around). This is one of the
> classic errors of OO, as it violates LSP (example: Square
> inherits SetWidth()/SetHeight().

It's a classic error because people are taught OO with examples like this. It does seem to me that an unmodifiable Square could be a subtype of an unmodifiable Rectangle, though.

Christopher Diggins

Posts: 1215
Nickname: cdiggins
Registered: Feb, 2004

Re: What if Constant Values were also valid Types? Posted: Jan 20, 2006 9:42 AM
Reply to this message Reply
> > Compare:
> >
> >

> > bool b1 = isPrime(someFxnReturningInt());
> > bool b2 = isPrime(3);
> >

> >
> > Here the specialization becomes useful.
>
> I'm not sure I get your point. Why does the
> specialization become useful? If double-dispatch isn't
> provided, then isPrime(int) still needs to check whether 3
> is Prime. So having a second isPrime for the value of
> three is only useful if I know when I am writing my
> program that a condition depends on whether 3 is prime. I
> can just write 'true' instead of calling isPrime(3).

That is what many people traditionally do in langauges like C, Fortran, and Java but it isn't scalable. Too often you end up seeing things like large hand-coded trignonmetric lookup tables. The advantatges start to reveal themselves when considering non-trivial conditional compilation scenarios (e.g. cross platform optimizations).

Kresimir Cosic

Posts: 34
Nickname: kreso1
Registered: Jan, 2006

Re: What if Constant Values were also valid Types? Posted: Jan 20, 2006 9:50 AM
Reply to this message Reply
> Ok, as I understand from your posting, now, when
> inheriting using "const", you only inherit the
> "const" member functions? It was a little hard to
> tell, as your example didn't describe the semantics of it.

Sorry, now, that I look at that example of mine, maybe it really isn't so clear. If someone doesn't get it yet, line
> public const Rectangle
is crucial. 'const' keyword here means inheriting only accessors and constructors, and makes const Rectangle supertype of Square, but doesn't make Rectangle supertype of square.

> It may matter less, as this was just an example,
> but Square may end up with redundant data, this
> way (both width and height).

You are talking about inheritance of implementation. I like to completely separate it from subtyping. My example is about subtyping, not about inheritance of implementation (which is completely different story).

> You're inheriting getWidth() and getHeight(),
> which, for a Square, always will be the same, and
> you add getSize() in the derived class. In short, you
> get a bloated interface, so as an example of the
> usefulness of the proposed feature, I don't think t
> his is a good one.

I don't see what's wrong with square having GetHeight and GetWidth. Usually, when subtyping is concerned, you get huge interfaces. This can be solved by the language by introducing renaming and namespaces for class methods.

> Yes, but I provided an example to get the same effect,
> without the uglyness.

Sorry, I wrote wrong thing. I think your example (with ConstRectangle) is ugly, and I think mine with const Rectangle is elegant.

> but just making an object const doesn't usually
> _remove_ functionality (it'll give an error if you
> call a non-const member function, but it's still there).

This is either a very strange argument, or I don't understand what you are saying.

> In short, I don't understand the advantage of this,

How's about that example with templated IsGreater()? You can't do that with ConstRectangle. I have some other advantages in mind, but they require long discussion. In short, I think that I would be able to argue that 'const inheritance' avoids code duplication.

Also, I have a feeling that you didn't understand point b). Let me try to explain this way. Using my example, you can use types Rectangle and const Rectangle. Using your example, you can use types Rectangle and ConstRectangle. So it looks to me like you are against 'const' keyword here.

Also, I noticed an error im one of my previous posts about type 42. The code should go:

const Int &a=42; //
a.isPrime(); //calls 42::isPrime
42.isPrime(); //same as above

If you look at previous post of mine containing this example, you will see that it uses 'const inheritance'.

To conclude, we simply have different opinions. I said my arguments, you said yours. If there is still something unclear, I will be pleased to clarify.

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: What if Constant Values were also valid Types? Posted: Jan 20, 2006 10:05 AM
Reply to this message Reply
> That is what many people traditionally do in langauges
> like C, Fortran, and Java but it isn't scalable. Too often
> you end up seeing things like large hand-coded
> trignonmetric lookup tables. The advantatges start to
> reveal themselves when considering non-trivial conditional
> compilation scenarios (e.g. cross platform optimizations).

Are you saying the methods would be evaluted at compile time? Sorry, I'm still not following.

Gregor Zeitlinger

Posts: 108
Nickname: gregor
Registered: Aug, 2005

Re: What if Constant Values were also valid Types? Posted: Jan 20, 2006 10:43 AM
Reply to this message Reply
> But the real question is: is this usefull at all? Who
> knows...
Well, I think restricting the possible values of a type is very useful, especially for integers and floating point types. Restricting types to just one value/instance is not enogh.

Flat View: This topic has 41 replies on 3 pages [ « | 1  2  3 ]
Topic: M Clock 2.0 Previous Topic   Next Topic Topic: Static Behavioral Subtyping and Heron Traits

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use