Re: Any chance to call Tango as Extended Standard Library

2009-01-19 Thread Rainer Deyke
aarti_pl wrote:
 first - last
 advance - retreat

My preference:
  head - rhead
  next - rnext (or advance - radvance)

The purpose of retreat and toe is to allow reverse iteration.
retreat in not the opposite of advance/next, it's the same
operation applied to the other end of the range.  So why not make this
explicit by prefixing an 'r' to these reverse iteration functions?


-- 
Rainer Deyke - rain...@eldwood.com


Re: Please vote once and for good: range operations

2009-01-29 Thread Rainer Deyke
Daniel Keep wrote:
   advanceFront  -- advance the front element by one
   retreatBack   -- retreat the back element by one

I like this.  Verb instead of noun for action, unambiguous about which
end moves in which direction, and it's symmetrical.

   nonEmpty-- as Walter one espoused: negative = bad, positive = good :D

I don't like this.  !empty reads better than nonEmpty, and empty
reads much better than !nonEmpty.


-- 
Rainer Deyke - rain...@eldwood.com


Re: If !in is inconsistent because of bool/pointer, then so is !

2009-02-06 Thread Rainer Deyke
downs wrote:
 This is NOT a reason against !in. In fact, this so-called
 inconsistency is already present in the language. If we remember,
 !pointer already transforms it into a boolean, so it would actually
 be more consistent if !in changed the return type to bool.

I agree.  'a != b' is short for '!(a == b)'.  'a !is b' is short for
'!(a in b)'.  For consistency, 'a !in b ' should be short for
'!(a in b)'.  I'd even go so far as to say that 'a !+ b' should be short
for '!(a + b)', although I can't think of a use for the '!+' operator.

a !op b == !(a op b): simple, consistent pattern.
a !op b == !(a op b), but only for op in some limited set that
doesn't include all operators with which you might want to use the
pattern: less consistent; requires memorization.


-- 
Rainer Deyke - rain...@eldwood.com


Re: If !in is inconsistent because of bool/pointer, then so is !

2009-02-06 Thread Rainer Deyke
downs wrote:
 A large part of the case for !in is that you can pronounce it a *not
 in* b. !+, on the other hand, would be .. what? a not plus b? does
 that mean a - b?  :)

It's a question of consistent patterns versus special cases.  If
'a !op b == !(a op b)', then the parser can rewrite all 'a !op b'
expressions as '!(a op b)' in a single place, without looking at what
op is.

(Of course '!=' (as the opposite of '==' as opposed to '=') is already a
special case, so perhaps defining the '!op' operators individually is
unavoidable.  'a !== b' as '!(a == b)' would work, but 'a != b' as '!(a
= b)' would be very weird and inconsistent with other languages.)

I'm not suggesting that anybody should actually /use/ the '!+' operator,
even if it was defined.  That would be horrible.


-- 
Rainer Deyke - rain...@eldwood.com


Re: If !in is inconsistent because of bool/pointer, then so is !

2009-02-06 Thread Rainer Deyke
Bill Baxter wrote:
 Note that D already has things like !.   But quoth the spec:
 For floating point comparison operators, (a !op b)  is *NOT* the same
 as !(a op b).
 [emphasis added]

I had to check the spec for the difference.  'a ! b' and '!(a  b)'
/are/ equivalent in the sense that '(a ! b) == !(a  b)' for any values
of 'a' and 'b'.  The vast majority of the time, the expressions 'a ! b'
and '!(a  b)' /are/ interchangeable.  The difference is that '!(a  b)'
sets a global exception state if either operand is NaN, while 'a ! b'
does not.

This is, in my opinion, a significant design error in the language.  The
difference between '!(a  b)' and 'a ! b' is not obvious.  There is
nothing about the operator '' that suggests that it should set a global
exception state, and there is nothing about '!' that suggests that it
should /not/ set a global exception state.  (Is global state for error
reporting ever a good idea in a high-level language?)  It also adds
awkward expressions to the language, not just in the form '!(a  b)',
but in the form '!(a ! b)'.


-- 
Rainer Deyke - rain...@eldwood.com


Re: If !in is inconsistent because of bool/pointer, then so is !

2009-02-06 Thread Rainer Deyke
Daniel Keep wrote:
 Rainer Deyke wrote:
 This is, in my opinion, a significant design error in the language.  The
 difference between '!(a  b)' and 'a ! b' is not obvious.  There is
 nothing about the operator '' that suggests that it should set a global
 exception state, and there is nothing about '!' that suggests that it
 should /not/ set a global exception state.  (Is global state for error
 reporting ever a good idea in a high-level language?)  It also adds
 awkward expressions to the language, not just in the form '!(a  b)',
 but in the form '!(a ! b)'.
 
 I believe this is, or is the result of, an aspect of IEEE floating point.

I don't have a copy of the IEEE floating point standard, but I strongly
suspect it would have allowed syntax like:
  a  b // sets global state
  a ! b // sets global state
  less_than_no_state(a, b) // does not set global state
  !less_than_no_state(a, b) // does not set global state
Or:
  a  b // does not set global state
  a ! b // does not set global state
  less_than_set_state(a, b) // sets global state
  !less_than_set_state(a, b) // sets global state
Or:
  a  b // sets global state
  a ! b // sets global state
  a [] b // does not set global state
  a [!] b // does not set global state
Or any number of other syntax choices, all less confusing than the
syntax actually used.

This is assuming we need two sets of comparison operators, one of which
uses global state to report NaN operands and one which does not.  I'm
not convinced that this is the case.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Old problem with performance

2009-02-08 Thread Rainer Deyke
Michel Fortin wrote:
 Polymorphism doesn't work very well while passing objects by value, even
 in C++. This is called the slicing problem.

I have heard about the slicing problem.  I know what it is.  But in all
my years of using C++ as my primary language, I have never actually
encountered it.  I don't believe it actually exists.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Old problem with performance

2009-02-08 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 How long have you used C++?

My first serious C++ project was started in 1997.

 This is not a tendentious question. In the recent years, the advent of
 quality smart pointers, an increased scrutiny of use of inheritance, and
 the teaching of idioms associated with reference types has greatly
 diminished slicing accidents. But in the olden days, people were
 inheriting value types left and right because it was the emperor's new
 clothes.

Slicing is caused by naive copying of polymorphic types, which is only
tangentially related to inheriting value types.

Example 1 (C++):

struct point2d {
  int x, int y;
};

struct point3d : public point2d {
  int z;
}

'point2d' is a non-polymorphic value type.  Slicing behavior exists, is
intentional, and does not invalidate any invariants.

Example 2 (C++):

class data_source {
public:
  virtual char read_byte() = 0;
};

class input_file : public data_source {
public:
  char read_byte() = 0;
};

'input_file' is a value type that implements an interface.  No slicing
problem exists because 'data_source' is an abstract type.

Example 3 (C++):

class person {
public:
  person(person const org) {
assert(typeid(org) == typeid(*this));
  }
  virtual ~person() {}
};

class avatar : public person, public deity {
};

'person' and 'avatar' are value types that can be used polymorphically.
 In any context 'person' may be either a reference to an instance of
class 'person' or a polymorphic reference to an instance of any class
that inherits from 'person'.  Unfortunately the C++ type system does not
distinguish between these two uses.  It is an error (checked at runtime)
to construct a new 'person' from a polymorphic reference, but any other
use of 'person' as a value type or a polymorphic type is valid.  No
slicing problem exists except through user error.

Example 4 (D):

class RubberChicken {
  RubberChicken dup() {
return new RubberChicken();
  }
}

class RubberChickenWithAPulleyInTheMiddle {
  private Pulley pulley;
  // Forget to override 'dup'.
}

Here 'RubberChicken' is a reference type, but due to programmer error,
the slicing problem still exists.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Old problem with performance

2009-02-08 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 The slicing problem exists in spades in this example, or better put its
 converse (your code will fire asserts when it shouldn't). The reason is
 rather subtle, so please try the code out to edify yourself.

You're referring to the automatically generated copy constructor in
class 'avatar' which calls the copy constructor in class 'person',
right?  That's a bug in my silly untested example code, but it's not the
slicing problem.

Fix 1:

class person {
  person(person const org, bool allow_slicing = false) {
assert(allow_slicing || typeid(org) == typeid(*this));
  }
};

class avatar : public person, public deity {
  avatar(avatar const org, bool allow_slicing = false)
: person(org, true)
  {
  }
};

Fix 2:

Trust the programmer to pay attention and remove the silly unnecessary
assertion from class 'person'.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Old problem with performance

2009-02-09 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 So the problem exists since you are trusting the programmer to avoid it.

The slicing problem exists in the sense that it is possible for a bad
programmer to accidentally slice an object.  However:
  - This is not a problem with the language, but an avoidable programmer
error in the language.
  - There are far more common programmer errors in C++ against which D
does not guard.  For example, dangling pointers to stack variables that
have gone out of scope.
  - D's response to this perceived problem is far too heavy-handed,
because it disallows useful correct code.
  - I would even say that the reference type classes in D lead to more
problems.  It is very easy for two objects in D to accidentally share
internal state, something that is impossible with value types.
  - Even if the slicing problem were a major issue in C++, it should be
possible to partially or totally fix it at the language level in D
without sacrificing inheritance for value types.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Old problem with performance

2009-02-12 Thread Rainer Deyke
Christopher Wright wrote:
   - I would even say that the reference type classes in D lead to more
 problems.  It is very easy for two objects in D to accidentally share
 internal state, something that is impossible with value types.
 
 struct A
 {
 int* i;
 }
 
 A a, b;
 int* i = new int;
 a.i = i;
 b.i = i;
 
 Hey look, they share internal state!

That's not accidental.  You did that on purpose.

 What mechanism for sharing state is available to by-reference objects
 but not by-value objects?

struct C {
  T t;
}

C c1;
C c2 = c1;

If T was a reference type, 'c1' and 'c2' now share state, and it's up to
the programmer to write code to prevent this.  Moreover, the D language
doesn't even give a hint as to whether T or a reference type or a value
type.  If T is a template parameter, it could be both.

What I'd really like to see is a hybrid of struct and class that has the
RAII and value semantics of a struct, but the polymorphism of classes.

polymorphic_struct A {
}

polymorphic_struct B : A {
}

{
  B b;
  A a = b; // Creates a true non-sliced copy of 'b'.
} // Destructors are called when the variables go out of scope.

At the implementation level, the objects could be placed on the heap
unless the compiler determines that this is not necessary.  The goal is
not performance, but consistent value semantics throughout the language.

The alternative is to work backwards and create a wrapper for classes to
give them value semantics:

struct value_semantics(T) {
  this(T v) {
this.value = v;
  }
  this(this) {
this.value = this.value.dup;
  }
  ~this() {
delete this.v;
  }
  T opDot() {
return this;
  }
  T value;
}


-- 
Rainer Deyke - rain...@eldwood.com


Re: Old problem with performance

2009-02-12 Thread Rainer Deyke
Christopher Wright wrote:
 Rainer Deyke wrote:
 What mechanism for sharing state is available to by-reference objects
 but not by-value objects?

 struct C {
   T t;
 }

 C c1;
 C c2 = c1;

 If T was a reference type, 'c1' and 'c2' now share state, and it's up to
 the programmer to write code to prevent this.  Moreover, the D language
 doesn't even give a hint as to whether T or a reference type or a value
 type.  If T is a template parameter, it could be both.
 
 Okay, reference types don't have value semantics. That's exactly what I
 would expect.

That sounds tautological, but many newcomers to D are surprised by
reference semantics when they expected value semantics.

 Why is it a problem?

Because reference types don't play well with RAII types.

Because unintentional object aliasing a problem orders of magnitude more
common than unintentional object slicing, and at least an order of
magnitude harder to detect and fix.

Because you can't correctly duplicate a multi-dimensional dynamic array
in D by using the built-in 'dup' property.

Because having two syntactically similar ways of creating user-defined
types with different features sets and different semantics is confusing,
 especially if you later decide that you need a struct to be a class and
all your copies turn into reference or vice versa.

 Because there is no explicit builtin
 copying mechanism for classes? If there were such, would it be a problem
 if copying were not the default?

What exactly do you have in mind?
Would it allow classes to correctly work with RAII types?
Would it prevent unintentional object aliasing?
Would it allow multi-dimensional arrays to be correctly duplicated?
Would it remove the need for both classes and structs in the language,
or at least the need to switch between the two?


-- 
Rainer Deyke - rain...@eldwood.com


Re: Old problem with performance

2009-02-12 Thread Rainer Deyke
Daniel Keep wrote:
 It's not a bug.  There are differences between value types and reference
 types.  Just like how there are differences between atomic types and
 aggregate types.  Or constant types and mutable types.

This is a bug:

struct MathematicalVector {
  this(int size) {
this.values = new T[size];
  }
  // No copy constructor.
  // Insert standard mathematical operators here.
  private T[] values; // Implementation detail.
}

I want MathematicalVector to be a value type (which is why I declared it
as a struct).  However, it doesn't behave a value type because I forgot
to write the copy constructor.  D doesn't cause the bug, but it
certainly makes it easier to accidentally write this kind of bug.

By contrast, the compiler-generated copy constructors in C++ usually do
the right thing for all but a handful of low-level resource-management
classes.  Which is not to say that C++ doesn't have problems of its own
- clearly it does, or I wouldn't be looking at D.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Old problem with performance

2009-02-12 Thread Rainer Deyke
Tom S wrote:
 Rainer Deyke wrote:
 Because you can't correctly duplicate a multi-dimensional dynamic array
 in D by using the built-in 'dup' property.
 
 Simply because shallow copies are the default. Usually you can't even
 have automatic deep copies. What if the array was supposed to be
 malloc'd or put in a contiguous memory block taken from a memory cache?
 A .dup that made a deep copy of it would introduce even a larger problem
  (since it would seem correct) than remembering that dups/struct copies
 are shallow by default and you need special code to do otherwise.

C++ has a way of specifying how deep you want your copies to be at the
declaration point:

std::vectorboost::shared_ptrstd::vectorint   v1, v2;
v2 = v1; // Shallow copy.
std::vectorstd::vectorint  v3, v4;
v4 = v3; // Deep copy.

Granted, C++ is a mess and you could do something similar in D by
wrapping the reference in a value_semantics wrapper.

value_semantics!(value_semantics!(int[])[]) v5, v6;
v6 = v5; // Deep copy due to the value_semantics templated struct.

Unfortunately this doesn't solve the RAII problem: if
value_semantics!(T) deletes the contained reference in its destructor,
then it is no longer safe to embed a value_semantics!(T) variable in
memory managed by the garbage collector.

 Would it remove the need for both classes and structs in the language,
 
 It's not a bug, it's a feature.

Having both value types and reference types in the language is
(arguably) a feature.  Having no syntactic distinction between them at
the point of use is at best a misfeature.  Forcing programmers to change
their value types into reference types when they need polymorphism or
inheritance, even though safe polymorphic value types are possible, is
clearly a misfeature.

 [...] change my scanf calls [...]

Are you serious?


-- 
Rainer Deyke - rain...@eldwood.com


Re: Old problem with performance

2009-02-13 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 C++ ctors won't do the right thing if you use pointers, which is the
 moral equivalent of using T[] inside MathematicalVector. If you refer to
 std::vector instead, then that's a carefully-defined type that does have
 the right copy constructor defined.

So where's the D equivalent of std::vector?  Phobos has nothing.  Tango
has containers, but they are also reference types.

I was under the impression that native arrays were intended to be used
directly in D, something I rarely do in C++.  Certainly neither of the
standard libraries has any hesitation about passing unadorned dynamic
arrays around.


-- 
Rainer Deyke - rain...@eldwood.com


Re: ref?

2009-02-15 Thread Rainer Deyke
Jerry Quinn wrote:
 If you want inheritance, copy semantics are an issue.  For example,
 if you have struct A : B, and an array of B, you can't put an A in
 it, since there's not enough space for an A (unless A adds 0
 storage).  If you have an array of A, inheritance isn't really buying
 you anything over having a B as the first member of A.  Any place
 where you'd want to use A as a B, you can get to the member B struct
 directly.

There are really three separate issues here: copy policy, storage
policy, and clean-up policy.

Copy policy: what are the semantics of 'a = b;'?
  Reference semantics:
'a' becomes a reference to the same object as 'b'.
  Value semantics:
The object 'b' is copied into 'a'.

Storage policy: given 'A a;', where is the object stored?
  Direct storage:
 Directly in the variable 'a'.
  Padded direct storage:
 Directly in the variable 'a', but with enough padding to also store
 an object of any subtype of 'A'.  Requires that the full set of the
 subtypes of 'A' be known at compile time.
  Heap storage:
The object is stored on the heap; the variable 'a' merely contains a
pointer.

Clean-up policy: given 'A a;', when is 'a''s destructor called?
  RAII:
Immediately when 'a' goes out of scope.  The object still exists at
this point.
  Garbage collection:
Sometime after the last reference to the object is no longer
reachable from any global or stack variable.  By the time the
destructor is called, other objects referenced by the object may
already have been destroyed.


These three policies are mostly conceptually orthogonal, although
garbage collection requires heap storage.  Interesting combinations include:
  Reference semantics, heap storage, garbage collection:
Classes in D.
  Value semantics, direct storage, RAII:
Structs in D.
  Value semantics, padded direct storage, RAII:
One possible approach to value types with inheritance but without
slicing.
  Value semantics, heap storage, RAII:
Another possible approach to value types with inheritance but
without slicing.
  Reference semantics, heap storage, RAII (with reference counting):
Reference types with destructors that actually work.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Beginning with D

2009-02-25 Thread Rainer Deyke
Prestidigitator wrote:
 Is D as good at game programming as C++? Also, would it be better to
 use 1.0 or 2.0?

My opinion: D 1.0 is, on the whole, worse than C++.  D 2.0 is shaping up
 to be, on the whole, better than C++.  However, D 2.0 is unstable to
the point of being unusable at the moment.  Stick to C++ for now, but
come back in a year or two when D 2.0 has had time to stabilize.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Beginning with D

2009-02-26 Thread Rainer Deyke
grauzone wrote:
 Going back from D to C++ also feels like stepping back, because C++
 doesn't natively support garbage collection. While I admit that
 reference counting is better for heavy resources (like file handles),
 for small memory objects tracing garbage collection is actually more
 efficient and less problematic. For example, reference counting can't
 deal with cycles of garbage.

Between cycle-breaking and support for heavy resources, I'd take the
latter over the former any day.  IME heavy resources are very common,
and cycles are relatively rare.  This is especially true for game
programming.

I have many options for handling cycles in C++.  I have no reasonable
options for handling heavy resource in D1.

 And what's so great about wrapping every pointer into a smartpointer?

Consistent syntax?  Having a choice of smart pointers?

Honestly, I can't see a way to avoid smart pointers even in D2.  A
language like Python may not need them, but then Python has reference
counting and destructors that actually work in addition to garbage
collection.  And weak references.


-- 
Rainer Deyke - rain...@eldwood.com


Re: First class lazy Interval

2009-02-27 Thread Rainer Deyke
Bill Baxter wrote:
 4..$
 4u..$
 etc?

Aside from the inconsistent meaning of $, you still can't have an
inclusive range [a, b], where 'a' and 'b' are not only known at runtime,
without treating 'b == int.max' as a special case.

-- 
Rainer Deyke - rain...@eldwood.com


Re: std.locale

2009-03-02 Thread Rainer Deyke
Georg Wrede wrote:
 Let's split this into two separate issues, the console and the GUI.
 
 The GUI is aware of your preferences.
 You don't use writefln with the GUI.
 You use the GUI API for any I/O, right?

There's a third faction: graphical apps that don't use the underlying
GUI API.  Most games fall in this category.

When writing cross-platform apps (whether gui, non-gui-but-graphical, or
console), you need some layer of abstraction over the underlying
platform localization API.  This abstraction can be provided by the
programming language, or a third-party library.

 A proper internationalisation would mean that the Chinese could use the
 console, and all character mode apps in Chinese. Problem is, there
 simply aren't enough pixels on many consoles to render the Chinese
 character set.

I have Windows configured to use a Japanese text encoding for command
windows.  I can and do run Japanese console applications, but console
applications that assume CP437 or Latin-1 don't work for me.


-- 
Rainer Deyke - rain...@eldwood.com


Re: std.locale

2009-03-02 Thread Rainer Deyke
Sergey Gromov wrote:
 To actually solve this problem the default exception handler must be
 fixed to convert any UTF-8 into the current OEM code page before
 printing.  It would also help if default stdin and stdout performed such
 a conversion.

No, stdin/stdout *must* perform this conversion.  It is a serious bug if
they don't.

The conversion cannot be performed at any other level.  D uses unicode
internally.  The console uses a specific encoding.  Therefore all data
passing between D and the console must be encoded/decoded.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Null references (oh no, not again!)

2009-03-03 Thread Rainer Deyke
Daniel Keep wrote:
 The point was that these were identified as being responsible for the
 majority of the bugs in a large, real-world code base.  Clearly #2 and
 #3 are common enough and cause enough issues to have made the list.

A sample size of one doesn't mean much.  In my experience, none of those
four factors account for a significant amount of bugs, since all of them
(except integer overflow) can be caught without too much effort through
the copious use of assertions.

I'd still prefer non-nullable references to be the default though.
Writing an assertion for every non-nullable reference argument for every
function is tedious.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Null references (oh no, not again!)

2009-03-04 Thread Rainer Deyke
Walter Bright wrote:
 It's also quite unnecessary. The hardware will do it for you, and the
 debugger will tell you where it is.

By the same reasoning, unit tests are unnecessary.  The end user tells
you that there's a bug, and the manually stepping through the whole
program with a debugger tells you where.

I use assertions because they make my life easier.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Null references (oh no, not again!)

2009-03-04 Thread Rainer Deyke
Walter Bright wrote:
 Rainer Deyke wrote:
 I use assertions because they make my life easier.
 
 Yeah, but you also said they were tedious g.

Tedious, but better than the same code without the assertions.
Non-nullable types would remove the need for manual assertions.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Null references (oh no, not again!)

2009-03-04 Thread Rainer Deyke
Georg Wrede wrote:
 Rainer Deyke wrote:
 A sample size of one doesn't mean much.  In my experience, none of those
 four factors account for a significant amount of bugs, since all of them
 (except integer overflow) can be caught without too much effort through
 the copious use of assertions.
 
 Well, you got it backwards.
 
 Sample size one is your experience.
 Sample size N is Unreal huge source, which has been written by N guys.

It's one anecdote versus another anecdote.  Neither has a significant
sample size.  (And you're not examining the whole body of work created
by those N programmers.  You're examining one project.)


-- 
Rainer Deyke - rain...@eldwood.com


Re: Null references (oh no, not again!)

2009-03-05 Thread Rainer Deyke
Ary Borenszweig wrote:
 I think nullable types can't be optional. How do you implement a linked
 list without them? You use a dummy value for the no next node? Naaah...

A nullable type is, conceptually, a container that can contain either
zero or one element.  If there was no language or library support for
nullable types, you could always use a dynamic array or some othet
container instead.


-- 
Rainer Deyke - rain...@eldwood.com


Re: new D2.0 + C++ language

2009-03-20 Thread Rainer Deyke
Christopher Wright wrote:
 Games have strict performance requirements that a stop-the-world type of
 garbage collector violates. Specifically, a full collection would cause
 an undue delay of hundreds of milliseconds on occasion. If this happens
 once every ten seconds, your game has performance problems. This is not
 true of pretty much any other type of application.

If you spend hundreds of milliseconds on garbage collection every ten
second, you spend multiple percent of your total execution time on
garbage collection.  I wouldn't consider that acceptable anywhere.


-- 
Rainer Deyke - rain...@eldwood.com


Re: new D2.0 + C++ language

2009-03-21 Thread Rainer Deyke
Christopher Wright wrote:
 I was pulling numbers out of my ass.

That's what I assumed.  I'm a game developer.  I use GC.

 0.1 seconds out of every ten is a small amount to pay for the benefits
 of garbage collection in most situations.

GC is useless for resource management.  RAII solves the resource
management problem, in C++ and D2.  GC is a performance optimization on
top of that.  If the GC isn't faster than simple reference counting,
then it serves no purpose, because you could use RAII with reference
counting for the same effect.

(No, I don't consider circular references a problem worth discussing.)


-- 
Rainer Deyke - rain...@eldwood.com


Re: new D2.0 + C++ language

2009-03-21 Thread Rainer Deyke
Sergey Gromov wrote:
 I think this is an overstatement.  It's only abstract write buffers
 where GC really doesn't work, like std.stream.BufferedFile.  In any
 other resource management case I can think of GC works fine.

OpenGL objects (textures/shader programs/display lists).
SDL surfaces.
Hardware sound buffers.
Mutex locks.
File handles.
Any object with a non-trivial destructor.
Any object that contains or manages one of the above.

Many of the above need to be released in a timely manner. For example,
it is a serious error to free a SDL surface after closing the SDL video
subsystem, and closing the SDL video subsystem is the only way to close
the application window under SDL.  Non-deterministic garbage collection
cannot work.

Others don't strictly need to be released immediately after use, but
should still be released as soon as reasonably possible to prevent
resource hogging.  The GC triggers when the program is low on system
memory, not when the program is low on texture memory.

By my estimate, in my current project (rewritten in C++ after abandoning
D due to its poor resource management), about half of the classes manage
resources (directly or indirectly) that need to be released in a timely
manner.  The other 50% does not need RAII, but also wouldn't benefit
from GC in any area other than performance.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Benchmark of try/catch

2009-03-24 Thread Rainer Deyke
Daniel Keep wrote:
 int? value1 = atoi(0);   // 0
 int? value2 = atoi(#!$); // null
 
 Not quite; that assumes null isn't a valid value.  What if the function
 was getInstance?

T?? value = getInstance();

 Here's how I understand it to work.  Functions may return a Maybe!(T),
 which is either a T or None (note that None is not null), and is
 implicitly castable to T.

'T??' (aka 'Maybe!(Maybe!(T))') must be a valid type.

In Haskell, a 'Maybe Maybe Integer' can be 'Nothing', 'Just Nothing', or
'Just Just 5'.

In C++, given 'boost::optionalboost::optionalint  o', you can have
'!o', '!*o', or '**o == 5'.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Four things

2009-03-31 Thread Rainer Deyke
bearophile wrote:
 C# has arrays of arrays as D, plus it has built-in multidimensional
 arrays too: 
 http://msdn.microsoft.com/en-us/library/aa288453(VS.71).aspx They
 have some advantages: - On them the compiler can use some extra
 optimizations, common in all Fortran compilers. D seems a language
 fit for matrix processing, etc. So this may be useful. - Such
 matrixes can be reshaped on the fly, they just keep their line length
 as an extra parameter. - Being made of a single block of memory, the
 memory allocator wastes less memory, sometimes much less. You can try
 this yourself with a single stc.gc.malloc compared to the newing of a
 2D matrix made of arrays of arrays. I am ignorant about this topic,
 but maybe Lucarella may say something.

I use multi-dimensional arrays all the time.  Arrays of arrays are
totally inadequate as a substitute.  However, I'm not sure what
advantage a built-in type would provide over a library type.

 The GC used by dotnet C# is quite more efficient than the GC used by
 C# mono. This page is about the development of a future better mono
 GC: http://www.mono-project.com/Compacting_GC It also reminds me that
 a (single) good GC may manage two kinds of objects: safe objects,
 that may be moved to allow heap compaction, and pinned objects,
 coming from or referenced by unsafe modules. If most modules in a D
 project are going to be safe, then most objects may be unpinned,
 allowing the GC to move them. This in theory may lead to a D GC that
 is often as efficient as ones like HotSpot ones.

Better GC is always nice, but I'd rather see the language stabilize
before worrying about such implementation details.

  It says:
 Data types and behaviors of objects are described by classes and
 traits. Class abstractions are extended by subclassing and by a
 flexible mixin-based composition mechanism to avoid the problems of
 multiple inheritance.
 So it seems they have partially refused how classes are managed in D.
 I'd like to know if this is actually (a bit) better than the way OOP
 is done in D. If such things are better, D2/D3 may eventually copy
 something.

Disclaimer: I haven't looked at Scala.

If mixins are such a good replacement for multiple inheritance, then
they are also a good replacement for single inheritance.  Having single
inheritance without multiple inheritance is /wrong/.  Based on the above
paragraph, it sounds like this criticism applies as much to Scala as it
does to D and Java.


-- 
Rainer Deyke - rain...@eldwood.com


Re: why Unix?

2009-04-06 Thread Rainer Deyke
Christopher Wright wrote:
 If you need to use the command line, you really should use Unix, if it's
 reasonable. The Windows filesystem structure is a source of pain, and
 it's hard to work around. It's hard to get information about interacting
 with the OS from the command line. Windows and Cygwin just isn't that
 great a fit.

I have both Linux and OS X readily available, but I still end up using
the Windows command line (without cygwin) most of the time.  I won't
deny that the Unix-like systems have some nice utilities, but in my
day-to-day activities, cmd.exe, Python, and my collection of custom
utilities are all I need.


-- 
Rainer Deyke - rain...@eldwood.com


Re: AAs

2009-04-08 Thread Rainer Deyke
bearophile wrote:
 Immutable associative arrays may even define a toHash (computed only
 once, the first time it's needed), so they can be used as keys for
 other AAs/sets too.

How would this work?

Hash value calculated on conversion to immutable?  Messy special case,
plus the hash value may never be needed.

Hash value calculated on first access and stored in the AA?  Can't do,
AA is immutable.

Hash value calculated on first access and stored in a global table?  The
global table would prevent the AA from being garbage collected.

I would like to see this happen, but I don't think D allows it.


-- 
Rainer Deyke - rain...@eldwood.com


Re: AAs

2009-04-09 Thread Rainer Deyke
bearophile wrote:
 You are right, there's a problem here, even once you have added an
 .idup to AAs. A hash value isn't data, it's metadata, so you may
 have a lazily computed mutable metadata of immutable data. Once
 computed the hash value essentially becomes an immutable.

This sounds like the difference between logical const and physical
const.  I use the logical const features of C++ (along with the
'mutable' keyword) in C++ all the time for just this purpose.  For
better or worse, D has gone the physical const route.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Is typedef an alien?

2009-09-24 Thread Rainer Deyke
Aenigmatic wrote:
 Is typedef (in D) a C/C++ legacy or is the dear orphan now adopted as
 a first-class citizen in the US of D?

typedef in D is a new feature not found in C or C++.

The typedef from C/C++ has been renamed to alias and extended to
non-types in D.


-- 
Rainer Deyke - rain...@eldwood.com


Re: override(T)

2009-09-24 Thread Rainer Deyke
Lionello Lunesu wrote:
 Be careful with that reasoning. What about attributes? Properties? What
 about C# var vs. D auto? C# using vs. D import? In fact, what
 about the standard library?

I wouldn't wind at all if D copied more from C#, and I don't even like
C#.  A natively compiled C# with extra features would be much more
useful (and much easier to sell) than yet another incompatible
object-oriented C variant.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Pure dynamic casts?

2009-09-24 Thread Rainer Deyke
language_fan wrote:
 The cost of e.g. doubling computing power depends on the domain. If you 
 are building desktop end user applications, they usually should scale 
 from single core atoms to 8-core high-end enthusiastic game computers. So 
 the cpu requirements shouldn't usually be too large. Usually even most of 
 the 1-3 previous generations' hardware runs them just nicely.
 
 Now doubling the cpu power of a low-end current generation PC does not 
 cost $1000, but maybe $20-50.

You also need to consider how widely distributed your application is.
If you force a million desktop PC users to upgrade their CPUs, you just
wasted twenty to fifty million dollars of your customers' money (or,
more likely, lost a million customers).


-- 
Rainer Deyke - rain...@eldwood.com


Re: Is typedef an alien?

2009-09-25 Thread Rainer Deyke
Chad J wrote:
 typedef uint ColorFormat;
 enum : ColorFormat
 {
   RGB,
   RGBA,
   HSV,
   CMYK,
   // etc
 }

I always do the opposite in C++:

namespace color_formats {
  enum ColorFormat {
RGB,
RGBA,
HSV,
CMYK,
// ...
  };
}
using color_formats::ColorFormat;

This way I don't pollute my outer namespaces with enum symbols.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Pure dynamic casts?

2009-09-25 Thread Rainer Deyke
language_fan wrote:
 I do not believe the market works this way. According to that logic 
 popularity correlates with expenses. So if you have e.g. 1 billion users, 
 even $1 in per-user hardware costs causes a billion dollar losses to 
 customers. Is that unacceptable? On the other hand a program with a 
 userbase of 10 might require a $100 hw upgrade and it is still ok?

If you manage to sell a billion copies of your program, you can
certainly afford to spend some extra time on optimizations and reach
maybe another million users (which would be only 0.1% of your total user
base, but a huge amount of money in absolute sales).  If you're serving
a vertical market of only 10, then your program is probably so expensive
that another $1000 for hardware upgrades is peanuts.  So yes, that's the
way it works.

 A used 2.5 GHz Athlon XP with 1GB of RAM and 100GB of disk costs about 
 $100. Anything below that is obsolete these days. Good luck selling 
 anything to people who use older computers, they are probably broke 
 anyways. Otherwise I just see it cheaper to build your apps slower and 
 require hardware updates. Just imagine - a highly optimized $400 program 
 is way too expensive for most users, a $50 program + $200 hw upgrade 
 sounds just fine.

If you just pull numbers of your ass, you can prove anything.

Software is priced to optimize total income, which is net income per
unit times number of units sold.  Production costs are not factored in
at all.  So the real question is if your $50 software package sells
enough additional units to make up for the increase in production costs.

Competent programmers write reasonably efficient code from the start,
and can take maybe another 10% of the development time to optimize the
code to the point of diminishing returns.  If you have competent
programmers, your 8x price increase almost an order of magnitude off.

Incompetent programmers don't just write slow code, they write buggy,
non-reusable code.  If you're using incompetent programmers, you're
probably wasting more money on management than it would have cost to
hire competent programmers in the first place.


-- 
Rainer Deyke - rain...@eldwood.com


Re: The Non-Virtual Interface idiom in D

2009-09-25 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 interface Cloneable(T) if (is(T == class))
 {
 private T doClone(); // must implement but can't call
 T clone()// this is what everybody can call
 {
 auto result = doClone();
 assert(typeof(result) == typeof(this));
 assert(this.equals(result));
 return result;
 }
 }

This sounds like a case for contract inheritance rather than two layers
of functions.

 interface ComparableForEquality(T)
 {
 protected bool doEquals(T);
 final bool equals(T rhs)
 {
 auto result = doEquals(rhs);
 assert(rhs.equals(cast(T) this) == result);
 return result;
 }
 }

This, on the other hand, does require two layers of functions if you
want to remove the infinite recursion by replacing the 'equals' in the
assertion with 'doEquals'.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Null references redux

2009-09-27 Thread Rainer Deyke
Walter Bright wrote:
   void bar(bool foo) {
 int a = void;
 if (foo) {
   a = 1;
 } else {
   a = 2; // Reuse variable.
 }
 int c = 3;
   }

 You now only have two variables, but both of them coexist at the end of
 the function.  Unless the compiler applies a clever optimization, the
 compiler is now forced to allocate space for two variables on the stack.
 
 Not necessarily. The optimizer uses a technique called live range
 analysis to determine if two variables have non-overlapping ranges. It
 uses this for register assignment, but it could just as well be used for
 minimizing stack usage.

That's the optimization I was referring to.  It works for ints, but not
for RAII types.  It also doesn't (necessarily) work if you reorder the
function:

   void bar(bool foo) {
 int a = void;
 int c = 3;
 if (foo) {
   a = 1;
 } else {
   a = 2; // Reuse variable.
 }
   }

Of course, a good optimizer can still reorder the declarations in this
case, or even eliminate the whole function body (since it doesn't do
anything).


-- 
Rainer Deyke - rain...@eldwood.com


Re: Null references redux

2009-09-27 Thread Rainer Deyke
Jeremie Pelletier wrote:
 Walter Bright wrote:
 They are completely independent variables. One may get assigned to a
 register, and not the other.
 
 Ok, that's what I thought, so the good old C way of declaring variables
 at the top is not a bad thing yet :)

Strange how you can look at the evidence and arrive at exactly the wrong
conclusion.  Declaring variables as close as possible to where they are
used can reduce stack usage, and never increases it.

-- 
Rainer Deyke - rain...@eldwood.com


Re: Null references redux

2009-09-27 Thread Rainer Deyke
Jesse Phillips wrote:
 The thing is that memory safety is the only safety with code.

That is such bullshit.  For example, this:

  class A {
  }

  class B {
  }

  A x = new B;

No memory access violation (yet).  Clearly incorrect.  Detecting this at
compile time is clearly a safety feature, and a good one.

You could argue that assigned a 'B' to a variable that is declared to
hold an 'A' is already a memory safety violation.  If so, then the exact
argument also applies to assigning 'null' to the same variable.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Null references redux

2009-09-28 Thread Rainer Deyke
Jesse Phillips wrote:
 Yeah, it was brought to my attention that type safety by a friend
 could be another form. bearophile also brings up a good example.

snip

 I think that is what Walter is getting at, you're not dealing with
 memory that is correct, when this happens the program should halt and
 be dealt with from outside the program.

Type errors and null pointer errors both belong to the same class of
errors, namely variables containing bogus contents.  Some languages like
Python detect both at runtime.  That's fine for those languages.
However, I prefer to detect as many errors as possible at compile time,
especially for larger projects.

Nullable types turn compile time errors into runtime errors which may or
may not be detected during testing.  In the worst case, nullable types
lead to silent data corruption.  Consider what happens when a bogus null
field is serialized.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Interesting GCC extensions

2009-09-28 Thread Rainer Deyke
Adam D. Ruppe wrote:
 Trying it, I see it doesn't actually work in initialization, but you
 can do it after the fact easily enough:
 
   int[101] a;
   a[0..9] = 1;
   a[10..99] = 2;
   a[100] = 3;

This leaves elements 9 and 99 uninitialized.  I assume the gcc version
does not.


-- 
Rainer Deyke - rain...@eldwood.com


Half-open versus closed ranges, was Re: Interesting GCC extensions

2009-09-28 Thread Rainer Deyke
Adam D. Ruppe wrote:
 I think this makes D's syntax superior. We can say a[0..$] where
 gcc would have to use the more annoying a[0...$-1].
 (pretending the $ worked of course)

Although I generally prefer half-open ranges over closed ranges, both
have their advantages and disadvantages.

Advantages of half-open ranges:
  - Empty ranges are valid: [0 .. 0]
  - Easy for subranges to go to the end of the original: [x .. $]
  - Easy to split ranges: [0 .. x] and [x .. $]

Advantages of closed ranges:
  - Symmetry.
  - Arguably easier to read.
  - ['a' ... 'z'] does not require awkward '+ 1' after 'z'.
  - [0 ... uint.max] is possible.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Rich Hickey's slides from jvm lang summit - worth a read?

2009-09-29 Thread Rainer Deyke
Chris Nicholson-Sauls wrote:
 My personal experience with Scala, at least, has been that it doesn't
 hurt anything.

Scala uses the Java garbage collector, which can take all kinds of abuse
without flinching.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Null references redux

2009-09-29 Thread Rainer Deyke
Jeremie Pelletier wrote:
 struct NonNull(C) if(is(C == class)) {
 C ref;
 invariant() { assert(ref !is null); }
 T opDot() { return ref; }
 }

This only catches null errors at runtime.  The whole point of a non-null
type is to catch null errors at compile time.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Null references redux

2009-09-29 Thread Rainer Deyke
Jeremie Pelletier wrote:
 Rainer Deyke wrote:
 This only catches null errors at runtime.  The whole point of a non-null
 type is to catch null errors at compile time.

 
 Thats what flow analysis is for, since these are mostly uninitialized
 variables rather than null ones.

Nitpick: there are no uninitialized variables in D (unless you
especially request them).  There are explicitly initialized variables
and default-initialized variables.

I can see the argument for disabling default initialization and
requiring explicit initialization.  You don't even need flow analysis
for that.  However, that doesn't address the problem that non-null
references are intended to solve.  It's still possible to explicitly
store a null values in non-null references without the problem being
detected at compile time.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Multiple subtyping with alias this and nested classes

2009-10-03 Thread Rainer Deyke
Max Samukha wrote:
 The above sucks because we can't specify which nameCollision gets
 implemented by which mixin. In current D, nameCollision of both
 interfaces is implemented by Flipper.

I don't think that being able to define multiple separate functions with
the same name and the same signature is a necessary feature of multiple
inheritance, or even a particularly well thought-out one.  Python is an
example of a language without this feature, and multiple inheritance
is very common in Python.


-- 
Rainer Deyke - rain...@eldwood.com


Re: What does Coverity/clang static analysis actually do?

2009-10-03 Thread Rainer Deyke
There is a simple and elegant solution to the problem of false positives
in static analysis.  Simple provide a way for the programmer to say, I
know this looks dangerous, but I know what I'm doing, so trust me on
this.  This could be done by adding some new syntax, either a new
keyword or a reused old keyword, or by changing the semantics of void
initialization.

For example, this would be an error:
  T t;
  if (i  10) {
t = new T;
  }
  if (i  10) {
t.f();
  }

This would be legal:
  T t = unchecked;
  if (i  10) {
t = new T;
  }
  if (i  10) {
t.f();
  }

But this would be an error:
  T t = unchecked;
  t.f();

As would this:
  T t = unchecked;
  f(t);

'unchecked' has the following properties:
  - It is an error to use the value of a variable that is initialized as
'unchecked'.
  - 'unchecked' tells the static analysis that the programmer knows what
he's doing.  The static analysis should therefore be liberal rather than
conservative.
  - At runtime, 'unchecked' is equivalent to 'null' for non-nullable
reference types.  This increases the chance that the error will be
detected at runtime.

On the other hand, variables without any explicit initializer at all
will have the following properties:
  - It is an error to use the value of the variable.
  - The static analysis is conservative by default.  If it thinks the
variable could be used uninitialized, an error is reported.
  - Since the static analysis guarantees that the variable will not be
used uninitialized, there is no need to initialize the variable at runtime.


-- 
Rainer Deyke - rain...@eldwood.com


Re: null references redux + Looney Tunes

2009-10-05 Thread Rainer Deyke
bearophile wrote:
 Scala has a powerful type system that allows to implement such things
 in a good enough way:
 
 http://www.michaelnygard.com/blog/2009/05/units_of_measure_in_scala.html

Either I'm missing something, or this system only checks units at
runtime (which would make it both slow and unsafe).

Boost.Units (C++) checks units at compile time.  There is no reason why
D could not use the same approach.


-- 
Rainer Deyke - rain...@eldwood.com


Re: null references redux + Looney Tunes

2009-10-05 Thread Rainer Deyke
Nick Sabalausky wrote:
 I've been thinking it might be nice to have both. Compile-time for obvious 
 reasons but then also run-time ones that could do conversions:
 
 auto velocity = convert(meter/second)(distance / time); // Actual 
 runtime-conversion

I'm pretty sure Boost.Units already does this, although in general it's
probably better to stick with SI units in your code and only perform
conversions on input/output.


-- 
Rainer Deyke - rain...@eldwood.com


unit libraries, was Re: null references redux + Looney Tunes

2009-10-05 Thread Rainer Deyke
language_fan wrote:
 The only problem with these was that there was no way to signal the 
 location of the type error in the client code, it always reported the 
 location of the (static) assert in the library, which is pretty much 
 useless.

That's a compiler problem, no?  I don't think this is a big deal either
way.  Unit errors should be exceedingly rare.  The purpose of a unit
library not to track down unit errors, but to formally prove that
correct code is correct.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Use of first person in a book

2009-10-07 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 So I thought I'd ask a candid question in here. How do you feel about
 moderate use of the first person in a technical book? Do you find it
 comfortable, neutral, or cringeworthy?

Occasionally a technical author finds that a personal anecdote or
opinion is useful for illustrating a concept.  The author then has these
choices:
  1. Leave it out.
  2. Treat it as a universal fact.
  3. Anonymize it.
  4. Talk about himself in the third person.
  5. Use first person.

The first three options are clearly bad because information is lost.  I
prefer option 5 over option 4, but both are acceptable to me.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Rationals Lib?

2009-10-10 Thread Rainer Deyke
dsimcha wrote:
 I guess I could have implemented some of these suggestions, but the idea was 
 for
 this lib to be very simple (it's only about 300 lines of code so far) and 
 agnostic
 to the implementation of the integers it's working on top of, with the caveat
 that, if you use something that's not arbitrary precision, the onus is on you 
 to
 make sure nothing overflows.  If anyone, for example, made a wrapper to the 
 GNU
 multiprecision lib that looked like a D struct w/ operator overloading, it 
 would
 be able to plug right into this library.  If std.bigint improves, this library
 will automatically benefit.

FWIW I use boost::rational quite a bit, and I've never felt the need to
use bigints.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Communicating between in and out contracts

2009-10-14 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 Eiffel offers the old keyword that refers to the old object in a
 postcondition. But it seems quite wasteful to clone the object just to
 have a contract look at a little portion of the old object.

You don't need to clone the whole object.  You just need to cache the
properties that are used with 'old'.  In other words, this functionality:

void push(T value);
in {
   auto oldLength = length();
}
out {
   assert(value == top());
   assert(length == oldLength + 1);
}

...can be expressed with this syntax:

   void push(T value);
   out {
  assert(value == top());
  assert(length == old length + 1);
   }

The syntax with 'old' is more concise, easier to read, and does the same
thing.  What's wrong with it (other than adding yet another keyword to
the language)?


-- 
Rainer Deyke - rain...@eldwood.com


Re: T[new] misgivings

2009-10-15 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 int[new] a;
 
 a = [1, 2, 3];
 
 What should that do?

This question can be rephrased as, should 'int[new]' be a reference
type or a value type (or something else)?

If 'int[new]' is a reference type, then it must rebind, because that's
what assignment does for reference types.  If 'int[new]' is a value
type, then it must modify the array in place, because that's all it can
do.  If 'int[new]' is neither a reference type nor a value type, then
we're back to (some of) the problems with slices.

To answer the rephrased question: 'int[new]' should be a value type.

 W: Nobody complained about it with slices.

FWIW, I found arrays in D1 so completely broken that I didn't it worth
the effort to complain about every little detail.  Everything about them
was wrong.  I consider them a textbook example of what not to do.


-- 
Rainer Deyke - rain...@eldwood.com


Re: T[new] misgivings

2009-10-15 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 Rainer Deyke wrote:
 To answer the rephrased question: 'int[new]' should be a value type.
 
 Well Walter and I agreed they should be pass-by-reference. That doesn't
 mean they must be references, and the fact that the simplest syntax has
 the worst efficiency reminds me of iostreams.

Given that D already has both value types and reference types, the
addition of types that are passed by reference but otherwise act as
value types actually seems reasonable.  It make the language more
orthogonal.

Classes have one set of attributes.  Structs have another.  If the
language absolutely needs to support both sets of attributes, I should
at least be able to mix and match between them.

So, what's the syntax for user-defined value types that are passed by
reference going to be?  ref struct?  opPass?

 FWIW, I found arrays in D1 so completely broken that I didn't it worth
 the effort to complain about every little detail.  Everything about them
 was wrong.  I consider them a textbook example of what not to do.
 
 My perception is that you're in a minority. Anyway, if there's something
 that T[new] can help with, let us know.

I was under the impression that arrays were generally considered broken,
which is why 'T[new]' is now being introduced.


-- 
Rainer Deyke - rain...@eldwood.com


Re: T[new] misgivings

2009-10-15 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 Then your argument building on similarity between the two is weakened.
 
 T[new] a;
 T[] b;
 
 a = [1, 2, 3];
 b = [1, 2, 3];
 
 Central to your argument was that the two must do the same thing. Since
 now literals are in a whole new league (they aren't slices because
 slices can't be assigned to arrays), the cornerstone of your argument
 goes away.

Actually [1, 2, 3] looks more like an array than a slice to me.  Arrays
can be assigned to slices, no?


-- 
Rainer Deyke - rain...@eldwood.com


Re: Communicating between in and out contracts

2009-10-16 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 class A {
 int fun() { ... }
 int gun(int) { ... }
 
 int foo()
 in {
 }
 out(result) {
 if (old.fun())
assert(old.gun(5));
 else
assert(old.fun() + old.gun(6));
 foreach (i; 1 .. old.fun())
assert(gun(i * i));
 }
 ...
 }
 
 Now please tell what's cached and in what order.

The following are cached, in this order:
  fun()
  gun(5)
  gun(6)
Old values are calculated in the order in which they appear in the
function, but only once each.

However, I strongly prefer the following syntax:

  class A {
  int fun() { ... }
  int gun(int) { ... }

  int foo()
  in {
  }
  out(result) {
  if (old(fun()))
 assert(old(gun(5)));
  else
 assert(old(fun()) + old(gun(6)));
  foreach (i; 1 .. old(fun()))
 assert(gun(i * i));
  }
  ...
  }

This lets you distinguish between the following cases:
  old(f().g())
  old(f()).g()

It also lets you cache non-members:
  old(arg);
  old(global_var);

For example:

  void increment_global_counter() out {
global_counter = old(global_counter) + 1;
  }


-- 
Rainer Deyke - rain...@eldwood.com


Re: Communicating between in and out contracts

2009-10-16 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 Rats, I meant assert(old.gun(i * i)). That's what compounds the
 difficulty of the example.

That wouldn't be allowed.  More specifically 'old(gun(i * i))' wouldn't
be allowed.  'old(this).gun(i * i)' would be allowed, but probably
wouldn't do what you want it to do.  'old(this.clone()).gun(i * i)'
would be allowed and would work, assuming that the 'clone' method is
defined and has the right semantics.

The general rule is that for any 'old(expr)', 'expr' only has access to
variables that are accessible in the 'in' block.  Preferably const access.

 I honestly believe the whole old thing can't be made to work. Shall we
 move on to other possibilities instead of expending every effort on
 making this bear dance?

It definitely /can/ be made to work, for some value of work.  It
sacrifices the natural order of evaluation to gain a concise and
intuitive syntax.  I don't think it should be dismissed out of hand.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Communicating between in and out contracts

2009-10-17 Thread Rainer Deyke
Rainer Deyke wrote:
 Andrei Alexandrescu wrote:
 I honestly believe the whole old thing can't be made to work. Shall we
 move on to other possibilities instead of expending every effort on
 making this bear dance?
 
 It definitely /can/ be made to work, for some value of work.  It
 sacrifices the natural order of evaluation to gain a concise and
 intuitive syntax.  I don't think it should be dismissed out of hand.

Also, from the Eiffel docs
(http://archive.eiffel.com/doc/online/eiffel50/intro/language/invitation-07.html):
  The notation 'old  expression' is only valid in a routine
postcondition. It denotes the value the expression had on routine entry.

It seems that Eiffel had 'old' semantics that I've proposed all along.
Any significant problems with this approach would have been discovered
by the Eiffel community by now.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Communicating between in and out contracts

2009-10-17 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 Rainer Deyke wrote:
 Also, from the Eiffel docs
 (http://archive.eiffel.com/doc/online/eiffel50/intro/language/invitation-07.html):

   The notation 'old  expression' is only valid in a routine
 postcondition. It denotes the value the expression had on routine entry.

 It seems that Eiffel had 'old' semantics that I've proposed all along.
 
 Great. Others brought it up too, inspired from Eiffel.
 
 Any significant problems with this approach would have been discovered
 by the Eiffel community by now.
 
 There is no problem if a copy of the object is made upon entry in the
 procedure. That's what I think Eiffel does. I was hoping to avoid that
 by allowing the out contract to see the definitions in the in contract.

Copying the object would be completely broken, so I'm sure that that's
*not* how Eiffel does it.  It denotes the value the expression had on
routine entry.  In other words, the expression is evaluated once, on
routine entry, and the result is cached for use in the postcondition.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Communicating between in and out contracts

2009-10-17 Thread Rainer Deyke
Christopher Wright wrote:
 Rainer Deyke wrote:
 It seems that Eiffel had 'old' semantics that I've proposed all along.
 Any significant problems with this approach would have been discovered
 by the Eiffel community by now.
 
 It requires duplicating the object. If the object is mutable, this
 requires duplicating it and recursively duplicating everything it
 references. If the object is immutable, this is free.

There is no the object.

  void f(string fname) out {
file_size(fname) = old(file_size(fname));
  }


-- 
Rainer Deyke - rain...@eldwood.com


Re: Communicating between in and out contracts

2009-10-17 Thread Rainer Deyke
Leandro Lucarella wrote:
 Rainer Deyke, el 17 de octubre a las 14:24 me escribiste:
 There is no the object.
 There is an object if you have this:
 
 void f(SomeObjectWithLotsOfReferences obj) out {
   assert(old(obj).some_check());
 }

If 'obj' is a reference type and the reference itself wasn't modified,
then 'old(obj)' is the same as 'obj'.  Objects are only copied if you
explicitly copy them.  'old(x)' means the cached value of evaluating
'x' at the beginning of the routine.  No more, no less.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Communicating between in and out contracts

2009-10-17 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 Rainer Deyke wrote:
 Copying the object would be completely broken, so I'm sure that that's
 *not* how Eiffel does it.  It denotes the value the expression had on
 routine entry.  In other words, the expression is evaluated once, on
 routine entry, and the result is cached for use in the postcondition.
 
 What if the expression is conditioned by the new state of the object?

Not allowed.  Since 'old(x)' is the value of 'x' evaluated at the
beginning of the function, it must be possible to evaluate 'x' at the
beginning of the function.  Either rewrite the assertion or drop it.  I
have a feeling that this will only rarely be an issue.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Communicating between in and out contracts

2009-10-17 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 If x is a complex expression and part of a complex control flow, it
 becomes highly difficult what it means at the beginning of the
 function. It also becomes difficult to find a way to distinguish good
 cases from bad cases without being overly conservative.

It looks like a more or less straightforward AST transformation to me.

  in {
  } body {
F();
  } out {
G(old(x));
  }

=

 {
   auto old_x = x;
   try {
 F();
   } finally {
 G(old_x);
   }
 }

Repeat for each instance of 'old', in order of appearance.  OK, it's not
entirely trivial, but it's not prohibitively difficult either.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Communicating between in and out contracts

2009-10-17 Thread Rainer Deyke
Rainer Deyke wrote:
  {
auto old_x = x;
try {
  F();
} finally {
  G(old_x);
}
  }

Not 'finally', unless postconditions are checked when the function
terminates with an exception.  This is closer to correct:

 {
   auto old_x = x;
   // Preconditions go here.
   F(); // -- function body
   G(old_x); // -- postcondition
 }


-- 
Rainer Deyke - rain...@eldwood.com


Re: Communicating between in and out contracts

2009-10-17 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 It is if x is an _arbitrarily complex_ expression, and if that
 expression is part of a _complex control flow_. The language definition
 would have to decide exactly where complex is too complex in the
 expression or the control flow. That complicates the language. Also, by
 necessity the feature will be limited and will not give people enough
 freedom. It's lose-lose.

The complexity of the expression is irrelevant, since the expression is
simply moved to the beginning of the function as a unit.  Complex
expressions are just simple expressions applied recursively.  The
compiler already knows how to deal with complex expressions.

Control flow is also irrelevant because it is simply ignored.  The AST
transformation sees this:
  blah blah old(x) blah old(y) blah
..and turns it into this:
  blah blah cached_value_0 blah cached_value_1 blah

Yes, this means that the expressions 'x' and 'y' cannot use any local
variables from the postcondition block.  If you want to cache values in
the precondition block for use in the postcondition block, you can't
wait until you reach the postcondition block before deciding what to
cache.  This limitation exists whether the caching is manual or automated.

If you insist on arguing about this, please direct your complaints to
the Eiffel community and tell *them* how the language feature they've
been using for years is broken.  If there really is something wrong with
Eiffel-style 'old' expressions, I'm sure the Eiffel community would know
about it.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Communicating between in and out contracts

2009-10-18 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 Rainer Deyke wrote:
 The expression may mutate stuff.

It shouldn't.  It's an error if it does, just like it's an error for an
assertion or post/precondition to have any side effects.

It would be nice if the compiler could catch this error, but failing
that, 'old' expressions are still no worse than assertions in this respect.

 If you know about Eiffel's old, I'd appreciate if you explained how it
 works. I am arguing exactly because I don't know. My understanding is
 that neither of us currently knows how it works, so I don't think it's
 ok to refer me to Eiffel.

My suggestion to ask the Eiffel community directly was meant seriously.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Eliminate new for class object creation?

2009-10-19 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 I hereby suggest we get rid of new for class object creation. What do
 you guys think?

*applause*

'X(x)' and 'new X(x)' have distinct meanings in C++.  In Java/C#/D, the
'new' is just line noise.


-- 
Rainer Deyke - rain...@eldwood.com


Re: LRU cache for ~=

2009-10-19 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 One surprising (but safe) behavior that remains with slices is this:
 
 void fun(int[] a) {
a[0] = 0;
a ~= 42;
a[0] = 42;
 }
 
 The caller may or may not see 42 in the first slot after the call.

Your definition of safe is clearly not aligned with mine.


-- 
Rainer Deyke - rain...@eldwood.com


Re: LRU cache for ~=

2009-10-19 Thread Rainer Deyke
Denis Koroskin wrote:
 On Tue, 20 Oct 2009 03:00:57 +0400, Rainer Deyke rain...@eldwood.com
 wrote:
 Andrei Alexandrescu wrote:
 One surprising (but safe) behavior that remains with slices is this:

 void fun(int[] a) {
a[0] = 0;
a ~= 42;
a[0] = 42;
 }

 The caller may or may not see 42 in the first slot after the call.

 Your definition of safe is clearly not aligned with mine.
 
 Safe as in SafeD (i.e. no memory corruption) :)

If the caller wasn't expecting the array to be modified, then that's a
textbook case of memory corruption.  If the caller /was/ expecting the
array to be modified, then it's the opposite, which isn't much better.


-- 
Rainer Deyke - rain...@eldwood.com


Re: LRU cache for ~=

2009-10-19 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 Rainer Deyke wrote:
 Your definition of safe is clearly not aligned with mine.
 
 What's yours?

Safety is not an absolute, but a question of degree.  The harder it is
to write incorrect code, the safer the language.

One key element of this is deterministic behavior.  If you rely on the
whim of the runtime to determine if two slices refer to the same data,
then it becomes much harder to reason about or test the code.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Eliminate new for class object creation?

2009-10-19 Thread Rainer Deyke
Bill Baxter wrote:
 On Mon, Oct 19, 2009 at 4:00 PM, Rainer Deyke rain...@eldwood.com wrote:
 'X(x)' and 'new X(x)' have distinct meanings in C++.  In Java/C#/D, the
 'new' is just line noise.
 
 Well, I think new Foo is how you create a struct on the heap in D.
 So it's not exactly line noise.

I should have specified, for classes.


-- 
Rainer Deyke - rain...@eldwood.com


Re: LRU cache for ~=

2009-10-19 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 Rainer Deyke wrote:
 If the caller wasn't expecting the array to be modified, then that's a
 textbook case of memory corruption.
 
 [citation needed]

I guess we need to define memory corruption first.  Memory corruption
is when a piece of code erroneously overwrites memory.  That applies
here.  Do you have a better definition?


-- 
Rainer Deyke - rain...@eldwood.com


Re: LRU cache for ~=

2009-10-19 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 Rainer Deyke wrote:
 I guess we need to define memory corruption first.  Memory corruption
 is when a piece of code erroneously overwrites memory.
 
 Where's that quote from?

It's my own attempt to define memory corruption.  It's equivalent to the
definition in Wikipedia: Memory corruption happens when the contents of
a memory location are unintentionally modified due to programming errors.

 [...] Do you have a better definition?
 
 I do,

Then post it.

 but since you mentioned a textbook case of memory corruption, I
 was curious which textbook that wold come from.

My use of the word textbook was idiomatic, not literal.

From http://www.answers.com/topic/textbook:

text·book   (tĕkst'bʊk')

[...]

adj.

Being a characteristic example of its kind; classic: a textbook case of
schizophrenia.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Eliminate new for class object creation?

2009-10-20 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 Lionello Lunesu wrote:
 Also, somebody mentioned using 'new' to allocate structs on the heap;
 I've never actually done that, but it sounds like using 'new' would be
 the perfect way to do just that.
 
 Yah, I guess I'll drop it.

Consistency with structs demands that for a class type 'X', 'new X'
allocates a *reference*, not an instance, on the heap.


-- 
Rainer Deyke - rain...@eldwood.com


Re: this() not executing code on structs

2009-10-21 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 Today, structs can't write their own this(). There aren't very solid
 reasons for that except that it makes language implementation more
 difficult.
 
 I wonder how much of a problem that could be in practice. I realized
 today that the Counted example - a classic C++ primer example
 featuring a struct that counts its own instances - cannot be implemented
 in D.
 
 In C++ the counted example looks like this:
 
 struct Counted {
static unsigned count;
unsigned myCount;
Counted() { myCount = count++; }
Counted(const Counted rhs) { myCount = count++; }
Counted operator=(const Counted rhs) {
   // no writing to myCount
   return *this;
}
~Counted() {
   --count;
}
 }
 
 In D there's no chance to write Counted because you can always create
 Counted objects without executing any code.
 
 struct Counted {
static uint count;
uint myCount;
this() { myCount = count++; }   // ERROR
this(this) { myCount = count++; }
ref Counted opAssign(Counted rhs) {
   // no writing to myCount
   return this;
}
~this() {
   --count;
}
 }
 
 This being a toy example, I wonder whether there are much more serious
 examples that would be impossible to implement within D.

Any struct that uses dynamic memory allocation internally.

Any struct that registers its instances in some dort of global registry.

'ValueType!T', which turns reference type 'T' into a value type.

A 'ScopedLock' variant that uses a single global mutex.

RAII wrappers over global initialization/deinitialization functions.

A 'UniqueId' struct that initializes to a value that is guaranteed to be
distinct from the vale of any other 'UniqueId' used by the program.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Semicolons: mostly unnecessary?

2009-10-21 Thread Rainer Deyke
Christopher Wright wrote:
 This only leaves the possibility of removing semicolons entirely. This
 would mandate newlines as separators instead. Then you'd need a
 different character to negate newlines. The C/C++ crowd would leave at
 this point, since that's just silly. (Though less typing.)

Trivial solution: use indentation.  If a line is more deeply indented
than the previous line, it's a continuation of the previous statement.

(Of course the people who complain about significant newlines are only
going to scream louder about significant indentation, but you can't
please everybody.)


-- 
Rainer Deyke - rain...@eldwood.com


Re: Array, AA Implementations

2009-10-21 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 IMHO any container must offer at the very least (I'll use stylized
 signatures):
 
 2. Iterate using at least a one-pass range
 
 OnePassRange!E opSlice();

Requiring this to be O(1) seems silly, since the actual iteration will
never be O(1) and can't even be guaranteed to be O(n).

 3. Remove some element from the container and give it to me
 
 E removeAny();

IIRC, C++ had a good reason (related to exception safety) for separating
retrieval and removal operations.

 Note that even though length() is not part of the interface (as is in
 Gobo's library), it can be computed through iteration with a generic
 algorithm. The idea is to not force containers to write that themselves
 or ungainly cache the length.

If the length is often needed, caching is much more efficient than
recalculating from scratch.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Semicolons: mostly unnecessary?

2009-10-21 Thread Rainer Deyke
Simen Kjaeraas wrote:
 On Thu, 22 Oct 2009 05:39:10 +0200, Rainer Deyke rain...@eldwood.com
 wrote:
 Trivial solution: use indentation.  If a line is more deeply indented
 than the previous line, it's a continuation of the previous statement.
 
 All spaces, I presume? :p

Tabs wouldn't be a problem so long as they are used consistently (one
tab per indentation level).  Given two whitespace sequences A and B,
there are four possibilities:
  - They are identical, so they represent the same indentation.
  - A is a prefix of B, so B represents the deeper indentation.
  - B is a prefix of A, so A represents the deeper indentation.
  - Neither is a prefix of the other, which would be a syntax error.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Private enum members + Descent

2009-10-24 Thread Rainer Deyke
Denis Koroskin wrote:
 On Sun, 25 Oct 2009 02:22:51 +0300, Michel Fortin
 michel.for...@michelf.com wrote:
 Sounds like you want enum inheritance: one enum being a superset of
 another.  :-)

I was about to post the same thing.

 It was previously proposed, but it was rejected because it works
 backwards: downcasting is allowed while upcasting is prohibited.

Just like contravariant functions.  Only less so.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Restricting ++ and --

2009-10-25 Thread Rainer Deyke
bearophile wrote:
 Removing those operators from D, as Python, may look excessive. So a
 possible compromise can be: - Deprecate the pre versions:  --x  and
 ++x

There is nothing wrong with the prefix versions.  '--x' is the
equivalent of 'x -= 1', but with less excessive typing.  Any confusing
expression using prefix ++/-- will be just as confusing if rewritten to
use +=/-=.

On the other hand, there are two things wrong with the post versions:
  - They use postfix notation, unlike all other unary operators in D.
  - They have confusion semantics.

I don't use postfix ++/--.  I wouldn't mind if they were removed
entirely.  However, the prefix versions should be kept, and allowed in
compound expressions.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Permitted locations of a version condition

2009-10-30 Thread Rainer Deyke
Phil Deets wrote:
 mixin(qENUM
 enum Tag
 {
A, B,
 ENUM~(Version!(symbol)?qENUM
C, D,
 ENUM:)~qENUM
E,
 }
 ENUM);
 
 That's not pretty, but it's good enough for me; so I'll probably not do
 any compiler hacking.

Not pretty is putting it very lightly.

What's happening in the D community is the same thing that's already
happened in the C++ community:
  - The language is missing some useful features.
  - The language is also missing an elegant powerful macro system that
could be used to add those features.
  - However, the language has other features that can be abused to
provide the functionality in a syntactically ugly way.
  - D programmers use these language features to write powerful but ugly
code.
  - The language develops a reputation for being overly complex and
difficult to read.

What makes this case particularly bad is that Walter deliberate chose to
limit the power of the 'version' construct in order to prevent overly
complex read-only code.


-- 
Rainer Deyke - rain...@eldwood.com


Re: module hijacking

2009-11-01 Thread Rainer Deyke
hasenj wrote:
 This is also possible in python.
 
 Has this ever caused a real problem? or is it just a theoretical problem?

In Python, this has caused problems for me.  It's less likely to cause a
problem in D because D makes better use of packages.  Python just dumps
everything into the root package; D has 'std'.


-- 
Rainer Deyke - rain...@eldwood.com


Re: safety model in D

2009-11-04 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 Rainer Deyke wrote:
 '-safe' turns on runtime safety checks, which can be and should be
 mostly orthogonal to the module safety level.
 
 Runtime vs. compile-time is immaterial.

The price of compile-time checks is that you are restricted to a subset
of the language, which may or may not allow you to do what you need to do.

The price of runtime checks is runtime performance.

Safety is always good.  To me, the question is never if I want safety,
but if I can afford it.  If I can't afford to pay the price of runtime
checks, I may still want the compile-time checks.  If I can't afford to
pay the price of compile-time checks, I may still want the runtime
checks.  Thus, to me, the concepts of runtime and compile-time checks
are orthogonal.

A module either passes the compile-time checks or it does not.  It makes
no sense make the compile-time checks optional for some modules.  If the
module is written to pass the compile-time checks (i.e. uses the safe
subset of the language), then the compile-time checks should always be
performed for that module.


-- 
Rainer Deyke - rain...@eldwood.com


Re: safety model in D

2009-11-05 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 First off: _all_ languages except C, C++, and assembler are or at least
 claim to be safe. All. I mean ALL. Did I mention all? If that was some
 ideology that is not realistic, is extremely difficult to achieve, and
 ends up too painful to use, then such theories would be difficult to
 corroborate with ALL. Walter and I are in agreement that safety is not
 difficult to achieve in D and that it would allow a great many good
 programs to be written.

You're forgetting about all other system programming languages.  Also,
many of these claims to safety are demonstrably false.

 The text is very approachable and informative, and I suggest anyone
 interested to read through page 5 at least. I think it's a must for
 anyone participating in this to read the whole thing. Cardelli
 distinguishes between programs with trapped errors versus programs
 with untrapped errors. Yesterday Walter and I have had a long
 discussion, followed by an email communication between Cardelli and
 myself, which confirmed that these three notions are equivalent:
 
 a) memory safety (notion we used so far)
 b) no undefined behavior (C++ definition, suggested by Walter)
 c) no untrapped errors (suggested by Cardelli)


They are clearly not equivalent.  ++x + ++x has nothing to do with
memory safety.  Conversely, machine language has no concept of undefined
behavior but is clearly not memory safe.  Also, you haven't formally
defined any of these concepts, so you're basically just hand-waving.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Safety, undefined behavior, @safe, @trusted

2009-11-05 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 Oh how cool. So it turns out that SafeD can be 100% implemented on a
 safe VM.

This is also true of regular unsafe C.


-- 
Rainer Deyke - rain...@eldwood.com


Re: safety model in D

2009-11-05 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 Rainer Deyke wrote:
 You're forgetting about all other system programming languages.

Delphi.

  Also,
 many of these claims to safety are demonstrably false.
 
 Which?

I can get Python to segfault.

 Memory safety is defined formally in Pierce's book.

Do you mean Types and programming languages by Benjamin C. Pierce?
According to Google books, it does not contain the phrase memory
safety.  It does contain a section of language safety, which says
that a safe language is one that protects its own abstractions.  By
that definition, machine language is safe, because it has no
abstractions to protect.

Another quote: Language safety can be achieved by static checking, but
also by run-time checks that trap nonsensical operations just at the
moment when they are attempted and stop the program or raise an
exception.  In other words, Pierce sees runtime checks and compile-time
checks as orthogonal methods for providing the same safety.

 Undefined behavior
 is defined by the C++ standard.

Undefined behavior is a simple concept: the language specification does
not define what will happen when the program invokes undefined behavior.
 Undefined behavior can be trivially eliminated from the language by
replacing it with defined behavior.  If a language construct is defined
to trash the process memory space, then it is not undefined behavior.

 Cardelli defines trapped and untrapped
 errors.

Untrapped error: An execution error that does not immediately result in
a fault.

I can't find his definition of execution error, which makes this
definition useless to me.


-- 
Rainer Deyke - rain...@eldwood.com


Re: D array expansion and non-deterministic re-allocation

2009-11-16 Thread Rainer Deyke
Walter Bright wrote:
 It's deterministic in the sense that if you run the program again with
 the same inputs, you will get the same result. This is a highly useful
 attribute for testing and debugging.

On the same platform, with the same compiler, compiler settings, and
standard library implementation.  That makes it harder to test, not
easier.  You now have to test with multiple compilers.

 It's safe as in memory safe. This is as opposed to undefined-behavior,
 which is not memory safe. A buffer overflow is an example of
 undefined-behavior.

The current behavior is unsafe in that you can accidentally have two
variables pointing at the same buffer.  Let's say one buffer holds
network input and the other holds some bytecode to execute.  Boom - a
bug that can be exploited to execute malicious (byte-)code.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Short list with things to finish for D2

2009-11-18 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 I am thinking that representing operators by their exact token
 representation is a principled approach because it allows for
 unambiguous mapping, testing with if and static if, and also allows
 saving source code by using only one string mixin. It would take more
 than just a statement that it's hackish to convince me it's hackish. I
 currently don't see the hackishness of the approach, and I consider it a
 vast improvement over the current state of affairs.

Isn't opBinary just a reduced-functionality version of opUnknownMethod
(or whatever that is/was going to be called)?

T opBinary(string op)(T rhs) {
static if (op == +) return data + rhs.data;
else static if (op == -) return data - rhs.data;
...
else static assert(0, Operator ~op~ not implemented);
}

T opUnknownMethod(string op)(T rhs) {
static if (op == opAdd) return data + rhs.data;
else static if (op == opSub) return data - rhs.data;
...
else static assert(0, Method ~op~ not implemented);
}

I'd much rather have opUnknownMethod than opBinary.  If if I have
opUnknownMethod, then opBinary becomes redundant.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Conspiracy Theory #1

2009-11-20 Thread Rainer Deyke
dsimcha wrote:
 == Quote from Denis Koroskin (2kor...@gmail.com)'s article
 It would be negligible.  The idea is that unions of reference and 
 non-reference
 types are such a corner case that they could be handled conservatively as a
 special case, and then it's possible, at least in principle, to deal with the
 other 99.9% of cases precisely and being conservative in 0.1% of 
 cases is
 really of no practical significance.

Yes, but a moving GC needs to be 100% precise, not 99.9%.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Conspiracy Theory #1

2009-11-20 Thread Rainer Deyke
dsimcha wrote:
 == Quote from Rainer Deyke (rain...@eldwood.com)'s article
 Yes, but a moving GC needs to be 100% precise, not 99.9%.
 
 Not if you allow pinning, which we'd need anyhow for untyped, conservatively
 scanned memory blocks.

If you allow pinning then you no longer get the full benefits of a
moving gc.  It would be nice to be able to trade untyped, conservatively
scanned memory blocks for a better gc.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Pure, Nothrow in Generic Programming

2009-11-27 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 Denis Koroskin wrote:
 Points 2 and 3 introduce undefined behavior, which is not allowed in
 SafeD :p
 
 s/undefined/implementation-defined/

Behavior is only implementation-defined if the implementation actually
defines it.  When targeting a specific implementation of a language,
there is no difference between implementation-defined behavior and just
plain defined behavior.

I think the term you are looking for is non-deterministic, which
describes an operation which is defined to have one of several possible
effects, but which of these effects actually takes place is not defined.

Incidentally, it is sometimes possible to send output to a physical
device by merely reading from a memory-mapped I/O address.  Therefore
the set of possible results of memcmp includes sending garbage output to
a physical device.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Phobos packages a bit confusing

2009-12-01 Thread Rainer Deyke
Pelle Månsson wrote:
 File looks like a constructor. You are not constructing a file you open
 for reading.

open by itself is ambiguous.  What are you opening?  A window?  A
network port?  I think the word file needs to be in there somewhere to
disambiguate.


-- 
Rainer Deyke - rain...@eldwood.com


Re: Phobos packages a bit confusing

2009-12-02 Thread Rainer Deyke
Pelle Månsson wrote:
 Rainer Deyke wrote:
 open by itself is ambiguous.  What are you opening?  A window?  A
 network port?  I think the word file needs to be in there somewhere to
 disambiguate.


 Something like new BufferedReader(new FileReader(foo.txt))? It's quite
 unambiguous.

No, like 'file(foo.txt)' or 'fopen(foo.txt)'.  There's always a
trade-off between verbosity and clarity, but names that are both
reasonably short and reasonably unambiguous exist.


-- 
Rainer Deyke - rain...@eldwood.com


Re: should postconditions be evaluated even if Exception is thrown?

2009-12-03 Thread Rainer Deyke
Andrei Alexandrescu wrote:
 If a function throws a class inheriting Error but not Exception (i.e. an
 unrecoverable error), then the postcondition doesn't need to be satisfied.

If an 'Error' is truly unrecoverable, then there's no point in treating
it as an exception.  Just dump the core and get out.  The very existence
of 'Error' suggests that recovery is possible, so the same rules should
apply to 'Error' and 'Exception'.

 I just realized that postconditions, however, must be satisfied if the
 function throws an Exception-derived object. There is no more return
 value, but the function must leave everything in a consistent state. For
 example, a function reading text from a file may have the postcondition
 that it closes the file, even though it may throw a malformed file
 exception.

Throwing an exception usually indicates a failure to reach the normal
postcondition, but functions often have another postcondition that
applies when the function terminates from an exception.  Common
postconditions in the case of an exception include:
  - The object is in a undetermined but valid state.  (The basic
exception guarantee in C++ lingo.)
  - The function has no side effects, i.e. all objects are in the same
state as they were before the function was called.  (The strong
exception guarantee.)


-- 
Rainer Deyke - rain...@eldwood.com


Re: switch case for constants-only?

2009-12-05 Thread Rainer Deyke
Nick Sabalausky wrote:
 As I mentioned earlier, that should be semantically equivilent to:
 
 int x = 1, y = 1;
 
 if(z == x)
 { ... }
 else if(z == y)
 { ... }
 
 In fact, it's already semantically equivilent to that, except that x and y 
 are currently required to be known at compile-time.

I assume the same rule applies to 'goto case'?

int i = 0, j = 0;
switch (0) {
case i:
  goto case j; // Oops, infinite loop.
case j:
  // Never reached.
}

I'm basically in favor of this change - it increases the expressive
power and uniformity of the language at little cost - but corner cases
like this bother me.


-- 
Rainer Deyke - rain...@eldwood.com


  1   2   3   4   >