Re: The CAPI Manifesto

2011-10-28 Thread Christophe Travert
Walter Bright , dans le message (digitalmars.D:146786), a écrit :
> If you want to add a layer on top of the C API, that would be fine. std.zlib 
> is 
> an example of that.
> 
> But the idea of CAPI is NOT to add a layer. Not fix, extend, refactor, 
> improve, etc.

I definitely agree with that, no attempt should be made to fix anything.

However, if you want only direct translation, the only way I see is to 
treat all defines as mixins. That mean all macro will become very 
tedious to use. Even there, there are choices to make (how do you 
translate multi-parameter macros ?). So I think a minimum of adaptation 
should be provided.

Here is an example of how this could be made:

#define square(x) ((x)*(x))

=>

// direct translation
mixin template!(string x)square
{
enum square = '((' ~ x ~ ')*(' ~ x ~ '))';
}

// adapted version
T square(T)(T x) { return x*x; }


Which version should be added ?
Both do not do the same thing ! As you know, In the first one, if x is a 
function call, it is called twice, and it the other one, it is called 
only once.
So if I follow the rule: no fix, no extend, etc, I must include only the 
direct translation. But the CAPI library will not be as usable as the c 
one. Then I miss the purpose of CAPI: make d as usable as c when using 
c libraries.

So which version should be added: I think it is both:

// direct translation
mixin template!(string x)M_square
{
enum M_square = '((' ~ x ~ ')*(' ~ x ~ '))';
}

// adapted version
T square(T)(T x) { return mixin M_square!q{x}; }


So this is what I propose:
Direct translation have to be provided. Adapted version can be provided 
alongside the direct translation of the d header. Adaptaded version must 
be a direct forward call to the direct version (note here that the 
adapted. Rules will have to be defined to avoid name clashes (for 
example, here, I used a the direct name for the usable version, and M_ 
prefix for the mixin version, but we could decide other rules).


Macros are a big issue.

And I think abvious translating, such as const char* to string, or 
pointer-length pairs to dynamic arrays is about the same problem.


double array_sum(double* a, size_t length);

=>
double array_sum(double* a, size_t length);
// AND
double array_sum(double[] a) { return array_sum(a.ptr, a.length); }


That is very little work. The direct translation is mandatory, and the 
adapted translation is not. But when the translation is obvious, there 
is no reason for everyone to make it on it's corner. Make it in the 
public header and share it!

I order to remain consistent, adaptation will have to obey to very 
precise rules, that have to be set. No fix, no extend, no refactor, no 
improve, etc. Just a forward call, to have, in addition to the C API, 
an API that use D's power: using enums, inline functions, for defines 
instead of the direct mixin translation. Using D's arrays instead of C 
arrays, etc. could be nice too.

What translation should be provided ? What rules to translate defines ? 
Is translation of pointer+length pair to array worth doing ? What about 
stringz and strings ? Where to draw the line ?


-- 
Christophe



Re: Why the hell doesn't foreach decode strings

2011-10-28 Thread Christophe Travert
Walter Bright , dans le message (digitalmars.D:147161), a écrit :
> On 10/20/2011 9:06 PM, Jonathan M Davis wrote:
>> It's this very problem that leads some people to argue that string should be
>> its own type which holds an array of code units (which can be accessed when
>> needed) rather than doing what we do now where we try and treat a string as
>> both an array of chars and a range of dchars. The result is schizophrenic.
> 
> Making such a string type would be terribly inefficient. It would make D 
> completely uncompetitive for processing strings.

I definitely agree with you, but I have a piece of news for you :
The whole phobos alreaday treats strings as a dchar ranges, and IS 
inefficient for processing strings.

The fact is: char[] is not char[] in phobos. It is Range!dchar. This is 
aweful and schizophrenic, and also inefficient. The purpose was to allow 
people to use strings without knowing anything about unicode. That is 
why Jonathan proposed having a specific string structure to manipulate 
strings without having to worry about unicode, just like how strings are 
currently manipulated in phobos, and letting char[] be char[], and be as 
efficient as they should be.

I was not there when it was decided to treat strings as dchar ranges, 
but now it is done. The only thing I can do is use ubyte[] instead of 
char[] so that phobos treat them as proper arrays, and propose optimized 
overloads for various phobos algorithm to make them as efficient as they 
should be (which I didn't find the time to do yet).

-- 
Christophe


Re: Deterministic life-time storage type

2012-04-21 Thread Christophe Travert
Michel Fortin , dans le message (digitalmars.D:164824), a écrit :
> So with your system, how do you write the swap function?

I've thought about that.
The scope(label) is the key.

void T swap(T)(scope T a, scope(a) T b)
{
  scope(a) tmp = a;
  a = b;
  b = tmp;
}

scope(inout) would also do the trick, since it is implicitely shared 
between parameters and return values.

-- 
Christophe


Re: Constraints

2012-05-11 Thread Christophe Travert
"Ibrahim Gokhan YANIKLAR" , dans le message (digitalmars.D:166850), a
 écrit :

I would even have:

concept CInputRange(E)
{
alias E ElementType;
static assert (isDefinable!typeof(this));
static assert (isRange!typeof(this));
@property bool empty();
@property void popFront();
@property ElementType front();
}

Concepts allows to state your intent when you create a type. It self 
documents the code more nicely that a static assert. It would be 
much easier for newcomers to understand concepts. Finally, by creating a 
hierarchy, concepts allows to determine which is the more specialized 
template. Even if it is not a priority to add this to the langage, I 
don't think it is just syntactic sugar.


Several problems should be solved though:

concept CInfiniteRange(E) : CInputRange(E)
{
// empty was declared to be a property, here it is an enum.
  enum bool empty = false;
// or:
  final bool empty() { return false; }
}

struct SimpleCounter(E) : CInfiniteRange(E)
{
// front was declared as a property, here it is a member variable
E front = 0;
void popFront() { ++front; }
//or:
E front_ = 0;
@property E front() const { return front_; } // now front is const
@property void popFront() { ++front_; }
}

It will not be obvious to define what is allowed and what is not so that 
the flexibility of current template constraints is reached, while 
keeping the definition natural.

-- 
Christophe


Re: deprecating std.stream, std.cstream, std.socketstream

2012-05-16 Thread Christophe Travert
"Steven Schveighoffer" , dans le message (digitalmars.D:167548), a
> My new design supports this.  I have a function called readUntil:
> 
> https://github.com/schveiguy/phobos/blob/new-io2/std/io.d#L832
> 
> Essentially, it reads into its buffer until the condition is satisfied.   
> Therefore, you are not double buffering.  The return value is a slice of  
> the buffer.
> 
> There is a way to opt-out of reading any data if you determine you cannot  
> do a full read.  Just return 0 from the delegate.

Maybe I already told this some time ago, but I am not very comfortable 
with this design. The process delegate has to maintain an internal 
state, if you want to avoid reading everything again. It will be 
difficult to implement those process delegates. Do you have an example 
of moderately complicated reading process to show us it is not too 
complicated?

To avoid this issue, the design could be reversed: A method that would 
like to read a certain amount of character could take a delegate from 
the stream, which provides additionnal bytes of data.

Example:
// create a T by reading from stream. returns true if the T was 
// successfully created, and false otherwise.
bool readFrom(const(ubyte)[] delegate(size_t consumed) stream, out T t);

The stream delegate returns a buffer of data to read from when called 
with consumed==0. It must return additionnal data when called 
repeatedly. When it is called with a consumed != 0, the corresponding 
amount of consumed bytes can be discared from the buffer.

This "stream" delegate (if should have a better name) should not be more 
difficult to implement than readUntil, but makes it more easy to use by 
the client. Did I miss some important information ?

-- 
Christophe


Re: The more interesting question

2012-05-18 Thread Christophe Travert
"Steven Schveighoffer" , dans le message (digitalmars.D:167556), a
> toStringz can allocate a new block in order to ensure 0 gets added.  This  
> is ludicrous!
> 
> You are trying to tell me that any time I want to call a C function with a  
> string literal, I have to first heap-allocate it, even though I *know*  
> it's safe.

How about "mystring\0".ptr ?




Re: stream interfaces - with ranges

2012-05-21 Thread Christophe Travert

I don't have time to read the whole discussion right now, but I've 
thought since our exchange here about buffered stream. I've imagined 
something close to, but quite different from you buffered stream, where 
the length of the buffer chunk can be adapted, and the buffer be poped 
by an arbitrary amount of bytes:

I reuse the name front, popFront and empty, but it may not be such a 
good idea.

struct BufferedStream(T)
{
  T[] buf;
  size_t cursor;
  size_t decoded;
  InputStream input;

  // returns a slice to the n next elements of the input stream.
  // this slice is valid until next call to front only.
  T[] front(size_t n)
  {
if (n <= decoded - cursor) return buf[cursor..cursor+n];
if (n <= buffer.length)
  {
   ... // move data to the front of the buffer and read new data to 
   // fill the buffer.
return buf[0..n];
  }
if (n > buf.length)
 {
   ... // resize buffer and read new data to fill the buffer
   return buf[0..n];
 }
  }
  // pop the next n elements from the buffer.
  void popFront(size_t n) { cursor += n; }
  void empty() { return input.eof && cursor == buf.length; }
}

This kind of buffered stream enable you read data by varying chunk size, 
but always read data by an amount that is convenient for the input 
stream. (and front could be made to return a buffer with the size that 
is most adequate for the stream when called with size_t.max as n).

More importantly, it allows to peak at an arbitrary amount of data, use 
it, and decide how many items you want to consume. For example, if 
allows to write stuff like "ReadAWord" without double buffering: you 
get enough characters from the buffer until you find a space, and then 
you consume only the characters that are the space.

"Steven Schveighoffer" , dans le message (digitalmars.D:167733), a
 écrit :
> OK, so I had a couple partially written replies on the 'deprecating
> std.stream etc' thread, then I had to go home.
> 
> But I thought about this a lot last night, and some of the things Andrei
> and others are saying is starting to make sense (I know!).  Now I've
> scrapped those replies and am thinking about redesigning my i/o package
> (most of the code can stay intact).
> 
> I'm a little undecided on some of the details, but here is what I think
> makes sense:
> 
> 1. We need a buffering input stream type.  This must have additional
> methods besides the range primitives, because doing one-at-a-time byte
> reads is not going to cut it.
> 2. I realized, buffering input stream of type T is actually an input range
> of type T[].  Observe:
> 
> struct /*or class*/ buffer(T)
> {
>   T[] buf;
>   InputStream input;
>   ...
>   @property T[] front() { return buf; }
>   void popFront() {input.read(buf);} // flush existing buffer, read  
> next.
>   @property bool empty() { return buf.length == 0;}
> }
> 
> Roughly speaking, not all the details are handled, but this makes a
> feasible input range that will perform quite nicely for things like
> std.algorithm.copy.  I haven't checked, but copy should be able to handle
> transferring a range of type T[] to an output range with element type T,
> if it's not able to, it should be made to work. 

Or with joiner(buffer);

> I know at least, an
> output stream with element type T supports putting T or T[].  What I think
> really makes sense is to support:
> 
> buffer!ubyte b;
> outputStream o;
> 
> o.put(b); // uses range primitives to put all the data to o, one element
> (i.e. ubyte[]) of b at a time

Of course, output stream should not have a consistent interface with 
input stream.

> 3. An ultimate goal of the i/o streaming package should be to be able to
> do this:
> 
> auto x = new XmlParser("");
> 
> or at least
> 
> auto x = new XmlParser(buffered(""));
> 
> So I think arrays need to be able to be treated as a buffering streams.  I
> tried really hard to think of some way to make this work with my existing
> system, but I don't think it will without unnecessary baggage, and losing
> interoperability with existing range functions.

A simple string stream can be built on top of a string, with no 
other member than the string itself, can't it ?
With my definition of buffered stream, at least, it can, and any array 
could support:
T[] front(size_t i) { return this[0..min(i, $)]; }
void popFront(size_t i) { this = this[i..$]; }

-- 
Christophe


Re: The more interesting question

2012-05-22 Thread Christophe Travert
"Jonathan M Davis" , dans le message (digitalmars.D:167901), a écrit :
> On Friday, May 18, 2012 11:18:46 Steven Schveighoffer wrote:
>> On Fri, 18 May 2012 11:05:21 -0400, Christophe Travert
>> 
>>  wrote:
>> > "Steven Schveighoffer" , dans le message (digitalmars.D:167556), a
>> > 
>> >> toStringz can allocate a new block in order to ensure 0 gets added.
>> >> This
>> >> is ludicrous!
>> >> 
>> >> You are trying to tell me that any time I want to call a C function
>> >> with a
>> >> string literal, I have to first heap-allocate it, even though I *know*
>> >> it's safe.
>> > 
>> > How about "mystring\0".ptr ?
>> 
>> AKA "mystring" :)
>> 
>> I'm sorry, I don't see the reason to require this. All for the sake of
>> making "" a null slice. I find the net gain quite trivial.
> 
> And I find the net gain to be negative, since the fact that "" is non-null is 
> _useful_.
> 
> - Jonathan M Davis

I'm not saying "" should point to null. I'm saying people claiming that 
they have to heap-allocate (via toStringz) each time they call a 
c-function are just wrong. I tend to accept the point that making 
strings automatically zero terminated for making calls to c-function 
easier is not such a good idea, but I have no problem with [] !is "". 
Empty strings should be tested with empty.

-- 
Christophe


Re: AliasTuples, rather than Records, to return multiple values

2012-05-22 Thread Christophe Travert
"Dario Schiavon" , dans le message (digitalmars.D:167822), a écrit :
> void main() {
>alias TypeTuple!(1, 2) a;
>alias TypeTuple!(a, 3) b;  // appends 3 to a
>writeln(b);  // prints "123"
> }
> 
> Appending items to tuples actually covers 99% of my needs of 
> single-item tuples in Python. Can anyone find other needs for 
> single-item tuples? Or for empty tuples?
> 
>>
>> However, another point of my post was whether we really need 
>> Records (std.typecons.Tuple's), which take approximately the 
>> same role as traditional struct's after all, to allow returning 
>> multiple values from functions. Wouldn't AliasTuples take on 
>> the role as well?


Personally, I don't like Tuple unpacking automagically. It prevents 
creating a single element Typle, or a Tuple of Tuple, which may be 
harmful if Tuple gain a greater role than they have now. Creating a 
Tuple of Tuple is impossible or require heavy workarrounds with 
automagically unpacking Tuples, whereas with not automagically unpacking 
Tuples, it is very easy to introduce an unpacking syntax when necessary 
with a random unary operator.

-- 
Christophe


Re: why D matters for Bioinformatics

2012-05-23 Thread Christophe Travert
"bearophile" , dans le message (digitalmars.D:168160), a écrit :
> deadalnix:
 http://blog.thebird.nl/?p=93
>>>...
>> I spreaded the word. This article is great and I 100% agree 
>> with it :D
> 
> The article says:
> 
>>There are a few things I miss in D. For example pattern 
>>recognition on unpacking data, which is great in Haskell, 
>>Erlang, and Scala (see example 
>>[http://www.scala-lang.org/node/120 ]).<
> 
> The author of that article has missed that D lacks something much 
> simpler than pattern matching, and even more commonly useful. 
> Currently in D you have to write something like:
> 
> int[2][] directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
> foreach (sx_sy; directions) {
>  immutable sx = sx_sy[0];
>  immutable sy = sx_sy[1];
>  // code that uses sx and sy
> 
> 
> While a less clunky language allows you to unpack them better, 
> something like:
> 
> auto directions = [tuple(-1, 0), tuple(1, 0), tuple(0, -1), 
> tuple(0, 1)];
> foreach (immutable (sx, sy); directions) {
>  // code that uses sx and sy
> 
> 
> If you use tuples, you want to unpack them often, it's a basic 
> operation on tuples.

This little example raises a question if tuples becomes part of the 
langage. Should static array have tuple capabilities ?

Besides that, it is easy to emulate your example with a little 
library solution. Maybe something like that should be added to 
std.range.

-- 
Christophe


Re: why D matters for Bioinformatics

2012-05-23 Thread Christophe Travert
"bearophile" , dans le message (digitalmars.D:168206), a écrit :
>> Besides that, it is easy to emulate your example with a little
>> library solution. Maybe something like that should be added to
>> std.range.
> 
> What syntax do you suggest?

foreach (immutable sx, sy; unpack(s)) {...}

doing something like (but more optimised than):

foreach (immutable sx, sy; lockstep(s.map!"a[0]", s.map!"a[1]")) {...}

You have to be careful with types, I don't think the result of map here 
can be cast to immutable (but I didn't check).


Re: synchronized (this[.classinfo]) in druntime and phobos

2012-06-09 Thread Christophe Travert
deadalnix , dans le message (digitalmars.D:169136), a écrit :
> It open door for stuff like :
> ReadWriteLock rw;
> synchronized(rw.read) {
> 
> }
> 
> synchronized(rw.write) {
> 
> }
> 
> And many types of lock : spin lock, interprocesses locks, semaphores, . 
> . . And all can be used with the synchronized syntax, and without 
> exposing locking and unlocking primitives.
> 

synchronize (foo) { ... } could do anything.

You might as well propose a syntax sugar to call any function or method 
taking a delegate. You could make it do any operation with the idiom:

struct MyType {
  void foo(void delegate() dg) {
this.open();
scope(exit) this.close();
dg();
  }
  ...
}

a.foo { // maybe some keyword should be necessary
  ...
}

Same problem as opApply, should take different type of delegates, etc...


Re: static array literal syntax request: auto x=[1,2,3]S;

2012-06-11 Thread Christophe Travert
Jonathan M Davis , dans le message (digitalmars.D:169705), a écrit :
> auto found = find([1, 2, 3, 4, 5], 3);

No problem if the rule could be the following:
 - array literals are static by default
 - array literals are copied to the heap when assigned to a dynamic 
array.
 - the former rule applies even if the array literal is assigned to a 
dynamic array via a function call, like it is the case in this example.
 - if a function can take both static and dynamic array as parameter, 
the static version takes precedence (it is always possible to call the 
dynamic version via slicing, at your own risk since the array is no 
longer valid at the end of the function call, or, much more wisely, by 
explicitely using the .dup property).





Re: static array literal syntax request: auto x=[1,2,3]S;

2012-06-14 Thread Christophe Travert
Artur Skawina , dans le message (digitalmars.D:169717), a écrit :
> On 06/11/12 12:06, Christophe Travert wrote:
>> Jonathan M Davis , dans le message (digitalmars.D:169705), a écrit :
>>> auto found = find([1, 2, 3, 4, 5], 3);
>> 
>> No problem if the rule could be the following:
>>  - array literals are static by default
>>  - array literals are copied to the heap when assigned to a dynamic 
>> array.
>>  - the former rule applies even if the array literal is assigned to a 
>> dynamic array via a function call, like it is the case in this example.
>>  - if a function can take both static and dynamic array as parameter, 
>> the static version takes precedence (it is always possible to call the 
>> dynamic version via slicing, at your own risk since the array is no 
>> longer valid at the end of the function call, or, much more wisely, by 
>> explicitely using the .dup property).
> 
> T f(T)(T a) {}
> 
> artur

I don't see the problem. If T compiles with a static array, it returns a 
static array, there is no invalid pointer issue, since the returned 
value is not a slice, but a static array.

-- 
Christophe



Re: static array literal syntax request: auto x=[1,2,3]S;

2012-06-14 Thread Christophe Travert
"Steven Schveighoffer" , dans le message (digitalmars.D:169718), a
 écrit :
> Note that D1 was like this, [1,2,3] was auto-typed to int[3u].  It was a  
> constant source of pain that I would not like to revisit.  Especially  
> since static arrays are now passed by value.
> 
> -Steve

Since static are passed by value, static arrays are now safe to use. 
This should decrease the pain a lot.



Re: Segmented Ranges?

2012-06-18 Thread Christophe Travert
"bearophile" , dans le message (digitalmars.D:169611), a écrit :
> struct BilevelScan(Range) {

This is basically std.algorithm.joiner

> So an idea is to introduce in D the multi-level Ranges:
> SegmentedInputRange
> SegmentedOutputRange
> SegmentedForwardRange
> SegmentedBidirectionalRange
> SegmentedRandomAccessRange
> 
> I think a Segmented Input Range is more complex than an Input 
> Range. Inventing such ranges is like finding the miminal set of 
> axioms that allow to write down a theorem, that is to efficiently 
> implement the normal algorithms. This is not an easy for me, I 
> don't know much about this topic.

I would only use this axiom:
- a SegmentedRange defines a bySegment property, returning a Range of 
Range bearing the same element type as SegmentedRange.

and define BySegment!Range as the type returned by range.bySegment, and 
SegmentType!Range as the type returned by range.bySegment.front.

Then it is up to the implementer of the algorithm to test the type of 
a SegmentedRange (InputRange, OutputRange, etc.), the type of 
BySegment!Range, and the type of SegmentType!Range when needed. There 
are too many combinaisons to make specific range types for all of them.
Moreover SegmentedRandomAccessRange would be ambiguous: is it a 
RandomAccessRange, a Range whose BySegment method returns a 
RandomAccessRange, a Range whose SegmentType is a RandomAccessRange, 
or all of them ?

-- 
Christophe


Re: valid uses of shared

2012-06-18 Thread Christophe Travert
"Steven Schveighoffer" , dans le message (digitalmars.D:169568), a
 écrit :
> On Thu, 07 Jun 2012 22:16:21 -0400, Robert DaSilva   
> wrote:
> 
> 
>> You're forgetting about Global data.
> 
> I wasn't so much forgetting it as I was ignoring it :)
> 
> My thought on that is that the shared keyword in that case is truly a  
> storage class.  It's the one place where having a value-type based shared  
> value makes sense.  If we had some kind of synchronized/shared pairing,  
> the compiler would have to allocate mutex space for that too.
> 
>> I think rather the head shared should be striped as this fits better  
>> with how D treats meaningless specifiers.
> 
> I don't think we can do that with type constructors, but I'm not sure.   
> I'm certainly against it, as I am against the current abuses of that  
> methodology.
> 
>> And trying to put structs that contain shared data on the stack should  
>> be illegal.

The compiler can already heap-allocate function variables that should be 
on the stack. So why disallowing shared for function variables?

void foo()
{
  shared int test; // allocates test on shared memory block.
}

Just like:
int delegate(int) adder(int a)
{
  return b => (a+b); // allocates a on the heap to make a closure.
}

-- 
Christophe


Re: How to break const

2012-06-18 Thread Christophe Travert
Matthias Walter , dans le message (digitalmars.D:170036), a écrit :
> On 06/18/2012 07:36 AM, Mehrdad wrote:
>> Is it just me, or did I subvert the type system here?
>> 
>> 
>> import std.stdio;
>> 
>> struct Const
>> {
>> this(void delegate() increment)
>> { this.increment = increment; }
>> int a;
>> void delegate() increment;
>> void oops() const { this.increment(); }
>> }
>> 
>> void main()
>> {
>> Const c;
>> c = Const({ c.a++; });
>> writeln(c.a);
>> c.oops();
>> writeln(c.a);
>> }
>> 
> 
> I don't think so. When calling oops you have two references to the object c:
> 
> - The this-pointer of the object itself which is not allowed to change
> the object in the const-call.
> - The reference from within main which is allowed to change it and can
> be reached via the frame pointer of the delegate.
> 
> I see this as perfectly valid code. Of course, opinions may differ here.

But here, the frame pointer of the delegate is part of the const 
structure. By transitivity, the frame pointer should be const, and 
therefore, calling the delegate (which modifies the frame pointer) 
should not be legal.

To be callable from a const method (oops), the member delegate 
(increment) should be of type "void delegate() const". This type exists, 
but is not easy to get[1]. Because constness of frame pointers is not 
implemented as it should be[2], there is a hole in the const system.

[1] AFAIK, it can only be got by making an explicit delegate like 
&struct.const_method
[2] Just like pure and no_throw attributes are complicated to work with 
delegates, the const attribute would be a mess to use at the moment.

-- 
Christophe


Re: How to break const

2012-06-19 Thread Christophe Travert
Iain Buclaw , dans le message (digitalmars.D:170145), a écrit :
> On 19 June 2012 09:18, Don Clugston  wrote:
>> So would I. Can you think of one?
>> It was the best name I could come up with, given that the 'pure' was the
>> keyword.
>> We want a word that means 'no hidden state'.
> 
> I thought that was what pure was for. :~)
> 
> -- 
> Iain Buclaw
> 
> *(p < e ? p++ : p) = (c & 0x0f) + '0';


A delegate does have a frame pointer, and it's not that well hidden. If 
you want no such 'hidden state', you do not want a delegate, you want a 
function pointer. That means all delegates are weakly pure (until they 
have an immutable frame pointer qualifier).

If you want that this 'hidden state' does not change, that is another 
story. pure for a delegate could mean that the frame pointer does not 
change, but then, pure methods wouldn't allow you to make pure 
delegates:

struct S
{
  int i;
  int foo() pure { return i; }
}

S s;
int delegate() pure dg = &s.foo;
// error if pure change meaning when applied to a delegate



Re: Primary Ranges of Containers

2012-06-19 Thread Christophe Travert
Jonathan M Davis , dans le message (digitalmars.D:170054), a écrit :
>> I'd propose to always add a bool template parameter (maybe isConst?) to
>> the range since most of the write-functionality can be "removed" by a
>> static if statement in order to make the range read-only.
>> 
>> Any suggestions?

Boolean parameters are very obscure.
How do you guess what is the meaning of false in:
Range!false;

Range!IsConst.no would be better.

> struct ArrayRange(bool isConst) {...}
> alias ArrayRange!false Range;
> alias ArrayRange!true ConstRange;

Range and ConstRange seems a good thing to have, just like c++ 
containers have iterator and const_iterator.



Re: How to break const

2012-06-19 Thread Christophe Travert
Timon Gehr , dans le message (digitalmars.D:170178), a écrit :
> That is completely unrelated.
> It is impossible to justify transitivity of const for delegate context
> pointers using this argument. It is far too general and the
> justification for the general concept comes from a specific example
> that is different from the one at hand.
> 
> The question is, what the meaning of 'const' references should be:
> 
> 1. data cannot be changed transitively through the reference
> 
> 2. the reference can reference both 'const' and 'immutable' data and
> 'immutable' data can transitively not be changed through the
> reference.
> 
> 
> 1. requires transitive const for delegate context pointers, 2. does not.

A const reference can contain

I don't understand the difference.


struct Delegate(C, F, Args)
{
C* ptr; // points to some structure containing all referenced data
R function(C*, Args) fun;
R opCall(Args) { return ptr.fun(Args); }
}

The signature of opCall determines the type of the delegate. In reality, 
the delegate is opaque, and C is not typed. ptr is a pointer to void*, 
and fun knows how to use that pointer. But that does not prevent the 
pointer to be const or immutable.

Note that calling opCall is not possible if the Delegate is const, or 
part of a const structure, because opCall does not have the const 
attribute.

But the signature of opCall could have any kind of attributes: const, 
immutable, pure, nothrow, inout..., which can be reflected by the 
delegates type.

A delegate of type "R delegate(Args) const" would be like this:

struct DelegateConst(C, R, Args...)
{
  const C* ptr;
  R function(const C*, Args) fun;
  R opCall(Args) const { return ptr.fun(Args); }
}

Now it is possible to call opCall if the DelegateConst is const. 
However, it is possible to build this delegate only if fun is const with 
regard to its context argument.

The same holds if you replace const by immutable.

Now, the context pointer can point to all type of data. C is a like a 
structure, and can contain any kind of data (mutable, const, immutable, 
shared...). However transitivity rules must be preserved. If the data is 
immutable, the delegate context pointer must be. If the data is a mix of 
mutable, const, and immutable data, there is no problem, has long has 
the function mutates only the mutable data (but then, the delegate's 
frame pointer type must be mutable, and the delegate is not callable if 
it is const).

However, it must respect transitivity: if the delegate is immutable, all 
data contained in the context must be immutable. If the context pointer 
is const, the data can be mutable, const, or immutable.


And where does all this comes from ?
delegates are primarily methods applied to a struct or class instance.

class S
{
  data d;
  void method(arg);
}

S s = new S;
void delegate(arg) dg = &s.method;

The delegate is constructed directly from the object's method. (1)

That is why it must have the same signature has objects method. If you 
want to fully represent methods, delegates must have all methods 
attributes: pure, nothrow, (which is a problem to introduce in 
toString methods, for example), etc... but also const, immutable, and 
maybe one day inout.

Currently, delegates does not support all this. It makes life easier, 
because we do not have a zillion types of delegates, and it gives a 
little bit of air on const virality until we have proper systems to 
simplify all this. But this is a gap in the langage. It is up to the 
programmer to respect const transitivity, and not exploit this gap and 
break the langage.

(1) In my first example, a langage delegate can be obtained from my 
artificial Delegate template by taking the adresse of opApply:

Delegate!(C, R, Args) s;
R delegate(Args) dg = &s.opApply

(2) Note that it is however possible to obtain a 'C delegate(Args) 
const' but taking the adress of a const method.

class S
{
  data d;
  void method(Arg) const;
}

S s = new S;
auto dg = &s.method; 

dg is infered as 'void delegate(Arg) const' by the compiler

-- 
Christophe


Re: How to break const

2012-06-19 Thread Christophe Travert
Artur Skawina , dans le message (digitalmars.D:170175), a écrit :
> On 06/19/12 15:29, deadalnix wrote:
>> Le 19/06/2012 14:30, Artur Skawina a écrit :
 Due to D concept of weak purity, this doesn't ensure the required what we 
 need here.
>>>
>>> Actually, it does - if it can be proved that the delegate can't alter the 
>>> object
>>> via the context pointer (eg because whatever it points to is not mutable) 
>>> then
>>> even D's "pure" is enough. Because the delegate would need to be passed a 
>>> mutable
>>> ref to be able to alter the object, which then could hardly be constructed 
>>> as
>>> "breaking" constness.
>>>
>>> But such a limit (const/immutable context) would be a problem for the cases 
>>> where
>>> the delegate needs to alter some state (like export the result of some 
>>> operation),
>>> but does _not_ modify the object it's embedded in. Note that the current 
>>> object may
>>> very well be reachable (and mutable) from the delegate - but at some point 
>>> you have
>>> to trust the programmer. Sure, fixing this hole would be great, but /how/ - 
>>> w/o
>>> incurring unacceptable collateral damage?
>>>
>> 
>> This isn't a problem as long as the delegate isn't a member of the object. 
>> If it is, transitivity is broken, which is something you don't want.
>> 
>> Relying on the trust on the programmer is a dumb idea. Human do mistake, way 
>> more than computers. The basic behavior MUST be a safe one.
>> 
>> Transitivity has been introduced for good reason and language already 
>> provide a way to break it via cast. So it is unacceptable to rely on 
>> programmer on that point.
>> 
 It is possible to get the error when trying to call the delegate instead 
 of preventing to make it const, as I said in the post you quote. It is 
 probably a better solution.
>>>
>>> Any delegate?
>>>
>> 
>> No, any delegate that have type that isn't covariant with the expected 
>> delegate type.
> 
>struct S {
>   int i; this(int i) { this.i = i; }
>   T* p; 
>   void f(int i) { this.i = i; /*p.i++;*/ }
>}
>struct T {
>   int i; this(int i) { this.i = i; }
>   void delegate(int i) f;
>}
> 
>void main() {
>   auto t = new T(42);
>   auto s = new S(17);
>   s.p = t;
>   t.f = &s.f;
>   f(t);
>}
> 
>void f(const (T)* t) {
>   t.f(t.i*2);
>}
>
> You're proposing to make the last 'f' function illegal, just because the
> commented out part could happen. I'm saying that this is unlikely to happen
> *by accident*, and of course would still be possible by casting away the
> constness. 
> But banning "unsafe" delegates would result in casts *when using "safe" ones*
> - which is not a real improvement because this would make the "bad" casts much
> harder to spot.


The proper way to do this is not cast, it is to give the proper 
constness for all types and methods :

   struct S {
  int i; this(int i) { this.i = i; }
  T* p; 
  void f(int i) const { this.i = i; /*p.i++;*/ }
// the commented part is illegal because S.f is const
   }
   struct T {
  int i; this(int i) { this.i = i; }
  void delegate(int i) const f;
  // T.f is const to be callable from .f
   }

   void main() {
  auto t = new T(42);
  auto s = new S(17);
  s.p = t;
  t.f = &s.f; // legal when T.f is const because S.f is also const
  f(t);
   }

   void f(const (T)* t) {
  t.f(t.i*2); // legal because T.f is const
   }

-- 
Christophe


Re: How to break const

2012-06-19 Thread Christophe Travert
Christophe Travert, dans le message (digitalmars.D:170182), a écrit :
> Timon Gehr , dans le message (digitalmars.D:170178), a écrit :
>> That is completely unrelated.
>> It is impossible to justify transitivity of const for delegate context
>> pointers using this argument. It is far too general and the
>> justification for the general concept comes from a specific example
>> that is different from the one at hand.
>> 
>> The question is, what the meaning of 'const' references should be:
>> 
>> 1. data cannot be changed transitively through the reference
>> 
>> 2. the reference can reference both 'const' and 'immutable' data and
>> 'immutable' data can transitively not be changed through the
>> reference.
>> 
>> 
>> 1. requires transitive const for delegate context pointers, 2. does not.
> 
> A const reference can contain
> 
> I don't understand the difference.

Apologies, I forgot to complete my post:

A const reference can contain both mutable and immutable data, as long 
as it does not allow to mutate it. I don't understand point 2: if the 
reference contain const reference, it should not modify the const 
reference. Of course, immutable data should not be changed in any case.

This is how I understand delegates should work:

[the rest of the post is unchanged]
> 
> struct Delegate(C, F, Args)
> {
> C* ptr; // points to some structure containing all referenced data
> R function(C*, Args) fun;
> R opCall(Args) { return ptr.fun(Args); }
> }
> 
> The signature of opCall determines the type of the delegate. In reality, 
> the delegate is opaque, and C is not typed. ptr is a pointer to void*, 
> and fun knows how to use that pointer. But that does not prevent the 
> pointer to be const or immutable.
> 
> Note that calling opCall is not possible if the Delegate is const, or 
> part of a const structure, because opCall does not have the const 
> attribute.
> 
> But the signature of opCall could have any kind of attributes: const, 
> immutable, pure, nothrow, inout..., which can be reflected by the 
> delegates type.
> 
> A delegate of type "R delegate(Args) const" would be like this:
> 
> struct DelegateConst(C, R, Args...)
> {
>   const C* ptr;
>   R function(const C*, Args) fun;
>   R opCall(Args) const { return ptr.fun(Args); }
> }
> 
> Now it is possible to call opCall if the DelegateConst is const. 
> However, it is possible to build this delegate only if fun is const with 
> regard to its context argument.
> 
> The same holds if you replace const by immutable.
> 
> Now, the context pointer can point to all type of data. C is a like a 
> structure, and can contain any kind of data (mutable, const, immutable, 
> shared...). However transitivity rules must be preserved. If the data is 
> immutable, the delegate context pointer must be. If the data is a mix of 
> mutable, const, and immutable data, there is no problem, has long has 
> the function mutates only the mutable data (but then, the delegate's 
> frame pointer type must be mutable, and the delegate is not callable if 
> it is const).
> 
> However, it must respect transitivity: if the delegate is immutable, all 
> data contained in the context must be immutable. If the context pointer 
> is const, the data can be mutable, const, or immutable.
> 
> 
> And where does all this comes from ?
> delegates are primarily methods applied to a struct or class instance.
> 
> class S
> {
>   data d;
>   void method(arg);
> }
> 
> S s = new S;
> void delegate(arg) dg = &s.method;
> 
> The delegate is constructed directly from the object's method. (1)
> 
> That is why it must have the same signature has objects method. If you 
> want to fully represent methods, delegates must have all methods 
> attributes: pure, nothrow, (which is a problem to introduce in 
> toString methods, for example), etc... but also const, immutable, and 
> maybe one day inout.
> 
> Currently, delegates does not support all this. It makes life easier, 
> because we do not have a zillion types of delegates, and it gives a 
> little bit of air on const virality until we have proper systems to 
> simplify all this. But this is a gap in the langage. It is up to the 
> programmer to respect const transitivity, and not exploit this gap and 
> break the langage.
> 
> (1) In my first example, a langage delegate can be obtained from my 
> artificial Delegate template by taking the adresse of opApply:
> 
> Delegate!(C, R, Args) s;
> R delegate(Args) dg = &s.opApply
> 
> (2) Note that it is however possible to obtain a 'C delegate(Args) 
> const' but taking the adress of a const method.
> 
> class S
> {
>   data d;
>   void method(Arg) const;
> }
> 
> S s = new S;
> auto dg = &s.method; 
> 
> dg is infered as 'void delegate(Arg) const' by the compiler
> 
> -- 
> Christophe



Re: How to break const

2012-06-19 Thread Christophe Travert
Timon Gehr , dans le message (digitalmars.D:170185), a écrit :
> On 06/19/2012 07:18 PM, Christophe Travert wrote:
> In 2., mutable data referred to by a const reference might be changed 
> through it.

Then it is not a const reference, it is a normal reference. Qualifying 
the reference as 'const' does not by you anything at all. Of course it 
should not change immutable data, no delegate should do that !

You might state that frame pointer are opaque, have no const qualifier, 
and that any delegates is callable even if they are in a const 
structure. But saying a reference (frame pointer) can be const AND 
modify data is a bit weird.

-- 
Christophe


Re: How to break const

2012-06-20 Thread Christophe Travert
Artur Skawina , dans le message (digitalmars.D:170191), a écrit :
>> The proper way to do this is not cast, it is to give the proper 
>> constness for all types and methods :
>> 
>>struct S {
>>   int i; this(int i) { this.i = i; }
>>   T* p; 
>>   void f(int i) const { this.i = i; /*p.i++;*/ }
>> // the commented part is illegal because S.f is const

I missed that. Then I disagree with your use of delegates. The solution 
here is to make the free f function to take a non const T*, since the 
function tries to modify variables from that T instance (via a 
delegate).

I don't mind if the delegate modify an S or a T instance: it should not 
modify anything. You use a delegate to store an S structure that you can 
modifiy in a const T. This is only one step to consider the following 
acceptable:

struct T {
  ref int delegate() _i;
  this(int i_) { _i = () => i_ }
  @property ref int i() const { return _i(); }
}

void foo(const T*)
{
  T.i = 10;
}

Would you say that this is not a problem, since you do not modify a T 
instance with that delegate?

I prefer to legalise:
struct T
{
  mutable int i; // makes modifying i legal, but prevent instanciating 
 // an immutable T and several optimizations
}

> Only a const T.f would be pointless.
I would allows you to safely call that delegate from a const T instance.

> Like I've already said twice in this thread - it *can* be done (the
> function has to be "pure" too for it to work), but certain delegate
> uses, which are OK now, would be forbidden.
>
> I'm all for fixing this hole - it's just that the proposed "fix" would
> have consequences, which can't simply be ignored.
> 
> Language design is not a game of whack-a-mole, where you ban random
> features left and right, because you think you've noticed a problem,
> without properly identifying it nor spending even a single second
> figuring out the implications.

I completely agree with that. And I'm fine with the current situation: 
you trust the programmer not to use delegates to break the const system 
until we have a proper system to deal with this (inference of constness 
of delegates, for a start). But encouraging the use of delegates to 
modify variables from a const instance seems wrong to me.

-- 
Christophe


Re: How to break const

2012-06-20 Thread Christophe Travert
deadalnix , dans le message (digitalmars.D:170262), a écrit :
> Once again, this is inconsistent with how purity is defined elsewhere.
> 
>> I'm all for fixing this hole - it's just that the proposed "fix" would
>> have consequences, which can't simply be ignored.
>>
> 
> They are not ignored, but it seems you don't clearly understand the 
> implications and the big picture.

Let me stress that argument: It has been said in several places in the 
newsgroup that D had many features that work right, but now problems 
emerge when feature interacts with each other. That is why consistency 
in the langage design is *very* important.

Implementing delegates properly with constness of the context pointer, 
and a consistent pure attribute is a way that garanties that delegates 
interacts finely with all other feature (constness, purity, etc.): 
delegates are implementable has classique structures with an opApply 
like I showed in a post in this thread. There is no black magic (1), an 
thus delegates are guaranted not add any hole in the langage: delegates 
are only a very convenient way to do what you can do with a context 
pointer and a function pointer.

(1) there is still a little bit of magic: closure makes automatic heap 
allocation of normally stack variables. Since you could do that 
explicitely, this is no big problem. The only way to make this magic 
disappear is to make heap allocation of normally stack variables legal 
in other cases, which would cover another big hole in the langage but 
this is too big a change in the langage to be accepted for the moment.

-- 
Christophe


Re: How to break const

2012-06-20 Thread Christophe Travert
deadalnix , dans le message (digitalmars.D:170272), a écrit :
> Le 20/06/2012 10:18, Christophe Travert a écrit :
> What is the conclusion here ?

I think it is the same as yours.

My conclusion is that delegate's signature should be the same has 
methods's signature, and have the same meaning. That makes the langage 
consistent, and makes sure not to bring any new hole in the langage.

In detail, they should be able to have a const or immutable attribute 
for their frame pointer, just like methods can be const or immutable 
with regard to the this argument, and a pure attribute that has the same 
meaning as methods pure attribute: no global variable access (and no 
impure function calls), but possibility to access the frame pointer.

-- 
Christophe


Re: How to break const

2012-06-20 Thread Christophe Travert
Timon Gehr , dans le message (digitalmars.D:170288), a écrit :
> On 06/20/2012 09:16 AM, deadalnix wrote:
>> Le 19/06/2012 17:49, Timon Gehr a écrit :
>>>
>>> The question is, what the meaning of 'const' references should be:
>>>
>>> 1. data cannot be changed transitively through the reference
>>>
>>> 2. the reference can reference both 'const' and 'immutable' data and
>>> 'immutable' data can transitively not be changed through the
>>> reference.
>>>
>>>
>>> 1. requires transitive const for delegate context pointers, 2. does not.
>>>
>>
>> No, 2. require 1., even if the initialization is broken.
>>
>> class Foo {
>>  void delegate() dg;
>>
>>  this(immutable void delegate() dg) immutable {
>>  thid.dg = dg;
>>  }
>> }
>>
>> Now, as delegate doesn't carry the constness of its context, an
>> immutable instance of Foo can refers to something that isn't immutable.
> 
> Clarification: 'const' means 'const'. No other qualifiers.
>
> There is no 'const' in that example code. 'immutable' obviously needs to 
> be transitive regardless of the particular interpretation of 'const'.
> 
const means: maybe immutable. Thus const needs to be transitive too. If 
you apply different rules to const and to immutable, you are breaking 
the consistency of the langage.



Re: How to break const

2012-06-20 Thread Christophe Travert
Timon Gehr , dans le message (digitalmars.D:170296), a écrit :
> On 06/20/2012 01:36 PM, Christophe Travert wrote:
>> Timon Gehr , dans le message (digitalmars.D:170288), a écrit :
>>> On 06/20/2012 09:16 AM, deadalnix wrote:
>>>> Le 19/06/2012 17:49, Timon Gehr a écrit :
>>>>>
>>>>> The question is, what the meaning of 'const' references should be:
>>>>>
>>>>> 1. data cannot be changed transitively through the reference
>>>>>
>>>>> 2. the reference can reference both 'const' and 'immutable' data and
>>>>> 'immutable' data can transitively not be changed through the
>>>>> reference.
>>>>>
>>>>>
>>>>> 1. requires transitive const for delegate context pointers, 2. does not.
>>>>>
>>>>
>>>> No, 2. require 1., even if the initialization is broken.
>>>>
>>>> class Foo {
>>>>   void delegate() dg;
>>>>
>>>>   this(immutable void delegate() dg) immutable {
>>>>   thid.dg = dg;
>>>>   }
>>>> }
>>>>
>>>> Now, as delegate doesn't carry the constness of its context, an
>>>> immutable instance of Foo can refers to something that isn't immutable.
>>>
>>> Clarification: 'const' means 'const'. No other qualifiers.
>>>
>>> There is no 'const' in that example code. 'immutable' obviously needs to
>>> be transitive regardless of the particular interpretation of 'const'.
>>>
>> const means: maybe immutable.
> 
> Or maybe mutable. Therefore, interpretation '2.'
> 
>> Thus const needs to be transitive too.
> 
> Wrong. This is the (A==>B) ==> (B==>A) fallacy, where
> 
> A: 'const' is transitive
> B: 'const' references cannot modify 'immutable' data
> 
> The conclusion regarding transitivity, given interpretation 2., is that
> 'const' needs to be transitive _if_ it is actually 'immutable'.

OK, I understand what you mean. I think this behavior is dangerous. You 
have to add rules to make sure B is not violated. I am not sure this is 
worth it.

>> If you apply different rules to const and to immutable, you are breaking
>> the consistency of the langage.
>>
> 
> Certainly not. This is like saying that applying different rules to
> 'const' and mutable is breaking the consistency of the language.
> mutable is not transitive.

OK, that was not the right argument. Const is transitive according to 
the spec. Making it not transitive would break the mutable < const < 
immutable design. You're trying to make an exception for data hidden 
behind a delegate, which is a dangerous thing.

-- 
Christophe


Re: How to break const

2012-06-20 Thread Christophe Travert
Artur Skawina , dans le message (digitalmars.D:170305), a écrit :
> On 06/20/12 09:31, Christophe Travert wrote:
>> Artur Skawina , dans le message (digitalmars.D:170191), a écrit :
>>>> The proper way to do this is not cast, it is to give the proper 
>>>> constness for all types and methods :
>>>>
>>>>struct S {
>>>>   int i; this(int i) { this.i = i; }
>>>>   T* p; 
>>>>   void f(int i) const { this.i = i; /*p.i++;*/ }
>>>> // the commented part is illegal because S.f is const
>> 
>> I missed that. Then I disagree with your use of delegates. The solution 
>> here is to make the free f function to take a non const T*, since the 
>> function tries to modify variables from that T instance (via a 
>> delegate).
> 
> The issue is the delegate definition - what exactly is a delegate?
> My first message in this thread started with this sentence:
> "It's fine, if you view a delegate as opaque". Because if you do, then
> accesses via a delegates context pointer are no different from using
> an external reference. That message also contained a program to show
> how trivial bypassing const is, at least for impure functions. So
> banning "unsafe" delegates wouldn't really change the situation.

Having opaque delegate and no holes in the langage is a possibility, but 
not without changing the langage spec.

With opaque delegates, you can't have a pure delegate, only pure 
function pointers, because pure means you do not access external data, 
and accessing data pointed by a the opaque frame pointer of the delegate 
would be accessing external data.

You may then redefine pure for delegate as delegates having only 
immutable external data (const is not enough).

I'm definitely not in favor of that solution.




Re: How to break const

2012-06-20 Thread Christophe Travert
Timon Gehr , dans le message (digitalmars.D:170313), a écrit :
>> OK, I understand what you mean. I think this behavior is dangerous. You
>> have to add rules to make sure B is not violated. I am not sure this is
>> worth it.
>>
> 
> No additional rules necessary, only loosening of existing rules. (in
> the obvious design, not in how the compiler implements it.)
> 
 If you apply different rules to const and to immutable, you are breaking
 the consistency of the langage.

>>>
>>> Certainly not. This is like saying that applying different rules to
>>> 'const' and mutable is breaking the consistency of the language.
>>> mutable is not transitive.
>>
>> OK, that was not the right argument. Const is transitive according to
>> the spec.
> 
> IIRC the spec is based on the claim that this is necessary in order to
> maintain immutability guarantees, so this is irrelevant for discussions
> relating to the alternative interpretation.
> 
The spec, by saying it is necessary to maintain immutability, has its 
own reasons. In reality, it is not purely necessary to maintain 
immutability, as you say, but it is necessary to maintain immutability 
AND the benefit of purity, AND the integrity of the whole spec, which is 
based on the assumption that const is transitive.

>> Making it not transitive would break the mutable<  const< immutable design.
> 
> What is this '<' relation ?

I meant that const is somewhere between mutable and immutable.
Also, mutable variable can contains const and immutable data, const 
variable can contain immutable data, but no mutable data (meaning: 
mutable through this reference). Subparts of a reference can increase 
the protection level of the data, but not decrease it.

>> You're trying to make an exception for data hidden
>> behind a delegate, which is a dangerous thing.
>> 
> I understand the implications. There is nothing dangerous about it.

Making exception in the langage is dangerous because you can have 
surprising results when interacting with other exception. Give some spec 
explaining how your opaque delegate works, and someone will find a hole 
one day.

I can't think of a way of implementing opaque delegates without a hole 
inside, or without loosing most of the point of immutability and purity.
If you do, please share.



Re: Add := digraph to D

2012-06-21 Thread Christophe Travert
"Lars T. Kyllingstad" , dans le message (digitalmars.D:170370), a
>>> auto name = initializer;
>>> const name = initializer;
>>> immutable name = initializer;
>>> shared name = initializer;
>>> enum name = initializer;
>>
>> After the first aren't these all just short hand for "const 
>> auto name = init" etc with the given keyword?
> 
> No. Many don't realise this, but "auto" doesn't  actually stand 
> for "automatic type inference". It is a storage class, like 
> static, extern, etc., and it means that the variable stops 
> existing at the end of the scope. It is, however, the default 
> storage class, which is why it is useful when all you want is 
> type inference. Even C has auto.

auto is called a storage class, but where did you read that auto would 
make the variable stop existing at the end of the scope ?

int delegate(int) add(int i, int j)
{
  auto k = i+j;
  return a => a+k;
}

Are you sure k will stop existing at the end of the scope ? I have no 
compiler at hand to check this, but I would be very surprised.

Object Create()
{
  auto k = new Object();
  return k;
}

Same question.

-- 
Christophe




Re: Add := digraph to D

2012-06-21 Thread Christophe Travert
"Lars T. Kyllingstad" , dans le message (digitalmars.D:170381), a
> That said, I was still wrong. :)  I just tried it now, and 
> apparently you can write pointless stuff like "auto extern int 
> foo;" and DMD will compile it just fine.  (And, unless that is a 
> bug, it means D has redefined 'auto' to mean absolutely nothing, 
> except to be a marker saying "this is a declaration", allowing 
> one to omit both storage class and type.)

dlang.org states:

| Auto Attribute
| The auto attribute is used when there are no other attributes and type 
| inference is desired. 

So indeed, in a variable declaration, auto has no meaning other than 
saying "this is a declaration".

-- 
Christophe


Re: LinkedList and Deque API in DCollections

2012-06-21 Thread Christophe Travert
d coder , dans le message (digitalmars.D:170392), a écrit :
> --f46d04083de73dc5ab04c2fb032c
> Content-Type: text/plain; charset=ISO-8859-1
> 
> Greetings
> 
> I do not know if this email belongs to this list or not. It is about a
> package available at dsource. Let me know if I should be posting such
> emails elsewhere.
> 
> I need an implementation of Doubly Linked list and Deque. Since the
> std.container library does not support these containers yet, I am trying to
> use these from DCollections library available on DSource here
> http://www.dsource.org/projects/dcollections . I am using the D2 branch of
> this library.
> 
> Now the issue is, I could not find any API for adding elements to the
> beginning of the LinkList. In the documentation (which is there for D1
> version) I see methods like append and prepend, but I do not think these
> are implemented in the D2 version of the library. And I see that for Deque,
> only prepend is implemented, there is no append.
>
> Also it is not clear to me if the "add" method could be used to append
> elements on the back of the LinkList or Deque collection.

Did you try : list.insert(list.begin, value);


Re: LinkedList and Deque API in DCollections

2012-06-21 Thread Christophe Travert
d coder , dans le message (digitalmars.D:170396), a écrit :
> --e0cb4efe2a2821373604c2fc4a15
> Content-Type: text/plain; charset=ISO-8859-1
> 
>>
>>
>> Did you try : list.insert(list.begin, value);
>>
> 
> Thanks. But I believe append and prepend are too common use-cases to be
> ignored.
> 
> Also, I see that "assert(some-expression)" has been used at a lot of places
> in the code of the libraries. Since these libraries are built with -release
> option, should these assert statements not be replaced by "enforce"?
> 

auto append(T, V)(ref T t, V value)
{
  return t.insert(t.begin, value);
}

should do the trick.
(you may add a guard to make sure T is a container)


Re: csvReader read file byLine()?

2012-06-22 Thread Christophe Travert
Jens Mueller , dans le message (digitalmars.D:170448), a écrit :
> Jesse Phillips wrote:
>> On Friday, 22 June 2012 at 08:12:59 UTC, Jens Mueller wrote:
>> >The last line throws a CSVException due to some conversion error
>> >'Floating point conversion error for input "".' for the attached
>> >input.
>> >
>> >If you change the input to
>> >3.0
>> >4.0
>> >you get no exception but wrong a output of
>> >[[4], [4]]
>> >.
>> 
>> Yes, and it seems .joiner isn't as lazy as I'd have thought.
>> byLine() reuses its buffer so it will overwrite previous lines in
>> the file. This can be resolved by mapping a dup to it.
>> 
>> import std.stdio;
>> import std.algorithm;
>> import std.csv;
>> 
>> void main()
>> {
>> struct Record {
>> double one;
>> }
>> auto filename = "file.csv";
>> auto file = File(filename, "r");
>> auto input = map!(a => a.idup)(file.byLine()).joiner("\n");
>> auto records = csvReader!Record(input);
>> foreach(r; records)
>> {
>> writeln(r);
>> }
>> }
> 
> Thanks. That works. But this should be either mentioned in the
> documentation or fixed. I would prefer a fix because the code above
> looks like a work around. Probably byLine or joiner then need some
> fixing. What do you think?
> 
> Jens

Yes, and that increases GC usage a lot.

Looking at the implementation, joiner as a behavior that is incompatible 
with ranges reusing some buffer:

joiner immidiately call's the range of range's popFront after having 
taken its front range. Instead, it should wait until it is necessary 
before calling popFront (at least until all the data has be read by the 
next tool of the chain).

Fixing this should not be very hard. Is there an issue preventing to 
make this change?

-- 
Christophe


Re: An idea to avoid a narrow class of bugs

2012-06-25 Thread Christophe Travert
Alex Rønne Petersen , dans le message (digitalmars.D:170616), a écrit :
>>
>> To me, it is a GC implementation issue. You should be able to allocate
>> in destructors.
> 
> Yes, I don't understand why on earth this limitation is in place. There 
> is no (good) technical reason it should be there.

Allowing safe unwinding of the stack when throwing exceptions is not a 
'good' reason ?

Well, a destructor should rather be no_throw, and should be able to 
call no_throw (GC) functions.

-- 
Christophe


Re: An idea to avoid a narrow class of bugs

2012-06-25 Thread Christophe Travert
Alex Rønne Petersen , dans le message (digitalmars.D:170622), a écrit :
> On 25-06-2012 15:27, Christophe Travert wrote:
>> Alex Rønne Petersen , dans le message (digitalmars.D:170616), a écrit :
>>>>
>>>> To me, it is a GC implementation issue. You should be able to allocate
>>>> in destructors.
>>>
>>> Yes, I don't understand why on earth this limitation is in place. There
>>> is no (good) technical reason it should be there.
>>
>> Allowing safe unwinding of the stack when throwing exceptions is not a
>> 'good' reason ?
>>
>> Well, a destructor should rather be no_throw, and should be able to
>> call no_throw (GC) functions.
>>
> 
> I fail to see how this is a problem. The GC (read: finalizer thread) 
> simply has to catch the exception and Do Something Meaningful with it.
> 


I confused two dinstict issues concerning destructors:

  - 1/ during GC, the GC may collect data in any order. References in a 
collected object may be invalid. This is not specific to GC usage in 
destructors however.

Example:
struct B
{
  string x;
  ~this()
  {
writeln('Deleting ', x);
  }
}
struct A
{
  B* b;
  ~this()
  {
writeln('About to Delete ', b.x); // error since b may have been 
  // deleted before this A instance.
GC.free(b); // should be fine, GC.free has no effect on already 
// deallocated block
  }
}

=> Using a maybe dead reference should be forbidden in destructor: 
Problem: it is in the general case impossible to tell if A.b has been 
allocated from the GC or not at compile time. However, somebody trying 
to collect GC data in a destructor is likely to ignore that the data may 
already have been collected. I reckon it is legitimate to collect GC 
data from a destructor (provided issue 2 is handled).

  - 2/ When an exception is thrown, destructors are called to unwind the 
stack until the exception is caught. If destructors start to trigger 
exceptions, things can get really messy.

=> It is a good idea to make destructors no_throw (and as stupid and 
simple as possible).

Maybe there is a third issue that motivate bearophile's post.

-- 
Christophe


Re: Get rid of isInfinite()?

2012-06-26 Thread Christophe Travert
"Mehrdad" , dans le message (digitalmars.D:170697), a écrit :
> On Monday, 25 June 2012 at 23:03:59 UTC, Jonathan M Davis wrote:
>> You could store those elements internally as you iterate over 
>> them
> 
> That's *precisely* the point of my wrapper... sorry if that 
> wasn't clear.
> 
> Why shouldn't that be sufficient for making it random-access?
> 
> 
>> If you can somehow figure out how to do that via buffering, 
>> then you could make it a forward range as well as whatever 
>> other range types you could define the functions for, but you'd 
>> have to figure out a way to define save.
> 
> 
> 
> OK, now we're at the same place. :P
> 
> What I'm saying is, I __CAN__ get the buffering to work.
> 
> What I __cannot__ figure out what to do with is 'length'... I 
> can't think of anything reasonable to return, since it's not 
> infinite (which I might represent as ulong.max, if anything) but 
> it's unbounded.
> 
> 
> So the _ONLY_ problem I'm running into right now is length() -- 
> any ideas how I could fix that?

I you are planning to buffer all input as you read it to enable 
random-access, then you should better read the whole file in your buffer 
from the start and get done with it, since you will end up having read 
the whole file in the buffer at the end...

-- 
Christophe


Re: Get rid of isInfinite()?

2012-06-26 Thread Christophe Travert
Christophe Travert, dans le message (digitalmars.D:170706), a écrit :
> "Mehrdad" , dans le message (digitalmars.D:170697), a écrit :
>> On Monday, 25 June 2012 at 23:03:59 UTC, Jonathan M Davis wrote:
>>> You could store those elements internally as you iterate over 
>>> them
>> 
>> That's *precisely* the point of my wrapper... sorry if that 
>> wasn't clear.
>> 
>> Why shouldn't that be sufficient for making it random-access?
>> 
>> 
>>> If you can somehow figure out how to do that via buffering, 
>>> then you could make it a forward range as well as whatever 
>>> other range types you could define the functions for, but you'd 
>>> have to figure out a way to define save.
>> 
>> 
>> 
>> OK, now we're at the same place. :P
>> 
>> What I'm saying is, I __CAN__ get the buffering to work.
>> 
>> What I __cannot__ figure out what to do with is 'length'... I 
>> can't think of anything reasonable to return, since it's not 
>> infinite (which I might represent as ulong.max, if anything) but 
>> it's unbounded.
>> 
>> 
>> So the _ONLY_ problem I'm running into right now is length() -- 
>> any ideas how I could fix that?
> 
> I you are planning to buffer all input as you read it to enable 
> random-access, then you should better read the whole file in your buffer 
> from the start and get done with it, since you will end up having read 
> the whole file in the buffer at the end...
> 

Hum, sorry, that is irrelevant if your input is not a file.



Re: const ref in opAssign

2012-06-26 Thread Christophe Travert
"monarch_dodra" , dans le message (digitalmars.D:170728), a écrit :
> Thanks for the in-depth explanation! It is still not very clear 
> to me, mostly because I don't understand "you wouldn't be able to 
> assign a const member variable's value of rhs to a non-const 
> member variable of this". This works fine in C++. Must be because 
> I've never used a language with reference semantics before.

In D, const is transitive. It mean that if an instance is const, 
everything that is refered by this instance is const. Thus, I can't copy 
a reference of a const instance to make a non-const reference.

Example:

struct A
{
  int* x;
  ref A opAssign(const ref other)
  {
this.x = other.x; // error: other.x, which is a const(int*) because 
  // of transitivity, can't be assign to this.x 
  // (which is a non-const int*).
  }
}

You need to write either:
  ref A opAssign(ref other)
  {
this.x = other.x; // no problem, other.x is not const.
// Note: the value this.x is now shared between this and other
  }
or:
  ref A opAssign(const ref other) // deep copy
  {
this.x = new int(*other.x);
// this is a deep copy: a new int is created to take other.x's value
  }


If the structure contain no references at all, there is no problem, and 
opAssign should be const ref, with a const overload for l-values, as 
previously said.

-- 
Christophe


Re: standard ranges

2012-06-28 Thread Christophe Travert
"Jonathan M Davis" , dans le message (digitalmars.D:170852), a écrit :
> completely consistent with regards to how it treats strings. The _only_ 
> inconsintencies are between the language and the library - namely how foreach 
> iterates on code units by default and the fact that while the language 
> defines 
> length, slicing, and random-access operations for strings, the library 
> effectively does not consider strings to have them.

char[] is not treated as an array by the library, and is not treated as 
a RandomAccessRange. That is a second inconsistency, and it would be 
avoided is string were a struct.

I won't repeat arguments that were already said, but if it matters, to 
me, things should be such that:

 - string is a druntime defined struct, with an undelying 
immutable(char)[]. It is a BidirectionalRange of dchar. Slicing is 
provided for convenience, but not as opSlice, since it is not O(1), but 
as a method with a separate name. Direct access to the underlying 
char[]/ubyte[] is provided.

 - similar structs are provided to hold underlying const(char)[] and 
char[]

 - similar structs are provided for wstring

 - dstring is a druntime defined alias to dchar[] or a struct with the 
same functionalities for consistency with narrow string being struct.

 - All those structs may be provided as a template.
struct string(T = immutable(char)) {...}
alias string(immutable(wchar)) wstring;
alias string(immutable(dchar)) dstring;

string(const(char)) and string(char) ... are the other types of 
strings.

 - this string template could also be defined as a wrapper to convert 
any range of char/wchar into a range of dchar. That does not need to be 
in druntime. Only types necessary for string litterals should be in 
druntime.

 - string should not be convertible to char*. Use toStringz to interface 
with c code, or the underlying char[] if you know you it is 
zero-terminated, at you own risk. Only string litterals need to be 
convertible to char*, and I would say that they should be 
zero-terminated only when they are directly used as char*, to allow the 
compiler to optimize memory.

 - char /may/ disappear in favor of ubyte (or the contrary, or one could 
alias the other), if there is no other need to keep separate types that 
having strings that are different from ubyte[]. Only dchar is necessary, 
and it could just be called char.

That is ideal to me. Of course, I understand code compatibility is 
important, and compromises have to be made. The current situation is a 
compromise, but I don't like it because it is a WAT for every newcomer. 
But the last point, for example, would bring no more that code breakage. 
Such code breakage may make us find bugs however...

-- 
Christophe


Re: standard ranges

2012-06-28 Thread Christophe Travert
Jonathan M Davis , dans le message (digitalmars.D:170872), a écrit :
> On Thursday, June 28, 2012 08:05:19 Christophe Travert wrote:
>> "Jonathan M Davis" , dans le message (digitalmars.D:170852), a écrit :
>> > completely consistent with regards to how it treats strings. The _only_
>> > inconsintencies are between the language and the library - namely how
>> > foreach iterates on code units by default and the fact that while the
>> > language defines length, slicing, and random-access operations for
>> > strings, the library effectively does not consider strings to have them.
> 
>> char[] is not treated as an array by the library
> 
> Phobos _does_ treat char[] as an array. isDynamicArray!(char[]) is true, and 
> char[] works with the functions in std.array. It's just that they're all 
> special-cased appropriately to handle narrow strings properly. What it 
> doesn't 
> do is treat char[] as a range of char.
> 
>> and is not treated as a RandomAccessRange.

All arrays are treated as RandomAccessRanges, except for char[] and 
wchar[]. So I think I am entitled to say that strings are not treated as 
arrays. An I would say I am also entitle to say strings are not normal 
ranges, since they define length, but have isLength as true, and define 
opIndex and opSlice, but are not RandomAccessRanges.

The fact that isDynamicArray!(char[]) is true, but 
isRandomAccessRange is not is just another aspect of the schizophrenia. 
The behavior of a templated function on a string will depend on which 
was used as a guard.

> 
> Which is what I already said.
> 
>> That is a second inconsistency, and it would be avoided is string were a 
> struct.
> 
> No, it wouldn't. It is _impossible_ to implement length, slicing, and 
> indexing 
> for UTF-8 and UTF-16 strings in O(1). Whether you're using an array or a 
> struct to represent them is irrelevant. And if you can't do those operations 
> in O(1), then they can't be random access ranges.

I never said strings should support length and slicing. I even said 
they should not. foreach is inconsistent with the way strings are 
treated in phobos, but opIndex, opSlice and length, are inconsistent to. 
string[0] and string.front do not even return the same

Please read my post a little bit more carefully before 
answering them.

About the rest of your post, I basically say the same as you in shorter 
terms, except that I am in favor of changing things (but I didn't even 
said they should be changed in my conclusion).

newcomers are troubled by this problem, and I think it is important. 
They will make mistakes when using both array and range functions on 
strings in the same algorithm, or when using array functions without 
knowing about utf8 encoding issues (the fact that array functions are 
also valid range functions if not for strings does not help). But I also 
think experienced programmers can be affected, because of inattention, 
reusing codes written by inexperienced programmers, or inappropriate 
template guards usage.

As a more general comment, I think having a consistent langage is a very 
important goal to achieve when designing a langage. It makes everything 
simpler, from langage design to user through compiler and library 
development. It may not be too late for D.

-- 
Christophe


Re: standard ranges

2012-06-28 Thread Christophe Travert
"David Nadlinger" , dans le message (digitalmars.D:170875), a écrit :
> On Thursday, 28 June 2012 at 09:49:19 UTC, Jonathan M Davis wrote:
>>> char[] is not treated as an array by the library, and is not 
>>> treated as a RandomAccessRange. That is a second 
>>> inconsistency, and it would be avoided is string were a struct.
>>
>> So, it looked to me like you were saying that making string a 
>> struct would
>> make it so that it was a random access range, which would mean 
>> implementing
>> length, opSlice, and opIndex.
> 
> I think he meant that the problem would be solved because people 
> would be less likely to expect it to be a random access range in 
> the first place.

Yes.



Re: standard ranges

2012-06-28 Thread Christophe Travert
Timon Gehr , dans le message (digitalmars.D:170884), a écrit :
>> An I would say I am also entitle to say strings are not normal
>> ranges, since they define length, but have isLength as true,
> 
> hasLength as false.
Of course, my mistake.

> They define length, but it is not part of the range interface.
> 
> It is analogous to the following:
> [...]

I consider this bad design.

>> and define opIndex and opSlice,
> 
> [] and [..] operate on code units, but for a random access range as
> defined by Phobos, they would not.

A bidirectional range of dchar with additional methods of a random 
access range of char. That is what I call schizophrenic.

>> but are not RandomAccessRanges.
>>
>> The fact that isDynamicArray!(char[]) is true, but
>> isRandomAccessRange is not is just another aspect of the schizophrenia.
>> The behavior of a templated function on a string will depend on which
>> was used as a guard.
>>
> No, it won't.

Take the isRandomAccessRange specialization of an algorithm in Phobos, 
replace the guard by isDynamicArray, and you are very likely to change 
the behavior, if you do not simply break the function.

> When read carefully, the conclusion says that code compatibility is
> important only a couple sentences before it says that breaking code for
> the fun of it may be a good thing.

It was intended as a side-note, not a conclusion. Sorry for not being 
clear.

>> newcomers are troubled by this problem,  and I think it is important.
> 
> Newcomers sometimes become seasoned D programmers. Sometimes they know
> what Unicode is about even before that.

I knew what unicode was before coming to D. But, strings being arrays, I 
suspected myString.front would return the same as myString[0], i.e., a 
char, and that it was my job to make sure my algorithms were valid for 
UTF-8 encoding if I wanted to support it. Most of the time, in langage 
without such UTF-8 support, they are without much troubles. Code units 
matters more than code points most of the time.

> The language is consistent here. The library treats some language
> features specially. It is not the language that is "confusing". The
> whole reason to introduce the library behaviour is probably based on
> similar reasoning as given in your post.

OK, I should have said the standard library is inconsistent (with the 
langage).

> The special casing has not caused me any trouble, and sometimes it was 
> useful.

Of course, I can live with that.


Re: Creating a Sub-view of a non - RA (hasSlicing) range.

2012-07-02 Thread Christophe Travert
Have you had a look at dcollection ?

http://www.dsource.org/projects/dcollections

There is a doubly linked list implementation, with range and cursors 
(entities that have iterator functionalities).


Re: foreach and retro

2012-07-02 Thread Christophe Travert
"bearophile" , dans le message (digitalmars.D:171013), a écrit :
> It's not a bug, it's caused by how ranges like retro work. retro 
> yields a single item. In D you can't overload on return values, 

But you can overload OpApply.

-- 
Christophe


Re: Proposal: takeFront and takeBack

2012-07-03 Thread Christophe Travert
takeFront implementation is dangerous for ranges which invalidates their 
front value when popFront is called, for instance, File.byLine. Thus 
takeFront will have to be used with care: any range implement takeFront 
(because of the template and USFC), but it may not be valid. That makes 
the range interface more complicated: There is a takeFront property, but 
you have to check it is safe to use... how do you check that by the way?

Are there so many ranges that duplicate efforts in front and popFront, 
where there is no easy workarround? Would it be such an improvement in 
performance to add takeFront? strings being immutable, my guess would be 
that it is not that hard for the compiler to optimize a foreach loop (it 
should be easy to profile this...). Even if there is an efficiency 
issue, you could easily solve it by implementing a range wrapper or an 
opApply method to make foreach faster.

-- 
Christophe


Re: Proposal: takeFront and takeBack

2012-07-03 Thread Christophe Travert

Range have been designed with the idea that front is valid until next 
call to popFront. If popFront was to be called right after front, then 
it would just be a popFront that returns a value, and maybe a justPop or 
something if you don't want to copy the value.

It's delicate to come now and decide that if a previously returned front 
value is no longer valid after a call to popFront, it should be 
documented by an enum. Who is going to review all libraries (written and 
to come) to make sure the enum is placed when it should be? The reverse 
should be done instead: if you want you range to be optimized by calling 
takeFront, define something (for example... takeFront). Then use 
algorithms that are specialized for takeFront.


Re: Pure functions and pointers (yes, again)

2012-07-04 Thread Christophe Travert
Denis Shelomovskij , dans le message (digitalmars.D:171072), a écrit :
> Since issue 8185 has been closed, I'm still very confused. I just 
> understood that endless discussion doesn't result in anything.
> 
> See example from http://d.puremagic.com/issues/show_bug.cgi?id=8185#c40
> ---
> int f(size_t p) pure
> {
>  return *cast(int*) p;
> }
> 
> void g(size_t p, ref size_t) pure
> {
>  ++*cast(int*) p;
> }
> 
> void h(size_t a, size_t b) pure
> {
>  int res = f(a);
>  g(b, b);
>  assert(res == f(a)); // may fail, no guaranties by language!
> }
> 
> void main()
> {
>  int a;
>  h(cast(size_t) &a, cast(size_t) &a);
> }
> ---
> 
> Jonathan M Davis (whose documentation correcting pull closed the issue) 
> told me that this code result in undefined behaviour. What _exectly_ 
> language rule this violates? I don't see this rule, but if there is no 
> such rule, how can we treat anything as strongly pure function?

Casting a value to a pointer clearly subvert the type system. A value 
with no reference (like size_t) is convertible to immutable, but a 
pointer cannot. Thus, a function with f's signature is strongly pure, 
but it is not if takes an int*. That's how you can create a bug.

The same issue occurs if you 'create' a pointer by illegal pointer 
arithmetic for instance: this is undefined behavior. Creating and using 
any kind reference by casting is undefined. That's not only a purity 
problem, it is a safety, a garbage collection, an and optimisation 
issue. In your case, you know what you ar doing regarding safety and 
garbage collection. Fine. But you do not respect optimisations linked 
with purity. Too bad, you can't ignore that the compiler is going to 
make assumptions regarding your datas types.

What change would you expect in the langage? making pure function 
automatically @safe? That may not be such a ba idea. However, that is 
not even enough, you could still create bug from optimizations with 
casting outside the pure function (for instance, you could alias 
variables that should not be aliased).

The only possibility to completely avoid this kind of bug is to forbid 
either optimization or casting. That's not going to happen in D.


Re: Proposal: takeFront and takeBack

2012-07-04 Thread Christophe Travert
"Roman D. Boiko" , dans le message (digitalmars.D:171108), a écrit :
> 
> What is error-prone in current client code?
> 
> If particular algorithm wants to take advantage of a potential 
> speed-up, it may decide to check whether hasConsume!Range, and 
> call consumeFront instead of front + popFront. Nobody is obliged 
> to do so.
> 
> The only problem I can see is potential code duplication, which 
> is lower priority in performance-critical part of code, IMO.
> 

You can do that in your own code if you think it is critical. 

Code-duplicating phobos and other libraries to make everyone take 
advantage of that may be a good intent, but I am afraid the cost in code 
maintenance may overcomes the performance gain.

I am not sure what would be worse, in the long run, between asking 
developpers to make front remain valid after popFront until next call to 
front, or having two different standard ways to iterate an input range 
(front/popFront and consumeFront).



Re: Proposal: takeFront and takeBack

2012-07-05 Thread Christophe Travert
If you really don't need the value, you could devise a "justPop" method 
that does not return (by the way, overloading by return type would be an 
amazing feature here). The idea is not "we should return a value 
everytime we pop", but "we should pop when we return a value".

-- 
Christophe


Re: Proposal: takeFront and takeBack

2012-07-05 Thread Christophe Travert
"monarch_dodra" , dans le message (digitalmars.D:171175), a écrit :
> For those few algorithms that work on bidirRange, we'd need a 
> garantee that they don't ever front/back the same item twice. We 
> *could* achieve this by defining a bidirectionalInputRange class 
> of range.

filter does that. If you want to call front only oncem you have to cache 
the results or... pop as you take the front value.

popFrontN and drop will crash too.


Re: Let's stop parser Hell

2012-07-09 Thread Christophe Travert
deadalnix , dans le message (digitalmars.D:171330), a écrit :
> D isn't 100% CFG. But it is close.

What makes D fail to be a CFG?


Re: Why is std.algorithm so complicated to use?

2012-07-10 Thread Christophe Travert
Jacob Carlborg , dans le message (digitalmars.D:171685), a écrit :
> I mean, is it possible to have the original code work?
> 
> auto bar = foo.chain("bar");
> 
> Or perhaps more appropriate:
> 
> auto bar = foo.append("bar");

What is wrong with foo.chain(["bar"])?

If you do not want the heap allocation of the array, you can create a 
one-element range to feed to chain (maybe such a thing could be placed 
in phobos, next to takeOne).

struct OneElementRange(E)
{
  E elem;
  bool passed;
  @property ref E front() { return elem; }
  void popFront() { passed = true; }
  @property bool empty() { return passed; }
  @property size_t length() { return 1-passed; }
  //...
}

You can't expect chain to work the same way as run-time append. A 
compile-time append would be very inefficient if misused.

> https://github.com/jacob-carlborg/dstep/blob/master/dstep/translator/Translator.d#L217

you might try this (untested)


string function(Parameter) stringify = (x)
{
 return (x.isConst? "const("~x.type~")": x.type)
~ (x.name.any?" "~translateIdentifier(x.name):"");
}

auto params = parameters
  .map!stringify()
  .chain(variadic? []: ["..."])
  .joiner(", ");

context ~= params;

I am not sure this will be more efficient. joiner may be slowed down by 
the fact that it is called with a chain result, which is slower on 
front. But at leat you save yourself the heap-allocation of the params 
array*.

I would use:
context ~= parameters.map!stringify().joiner(",  ");
if (variadic) context ~= ", ...";

To make the best implementation would require to know how the String 
context works.

*Note that here, stringify is not lazy, and thus allocates. It 
could be a chain or a joiner, but I'm not sure the result would really 
be more efficient.


Re: Why is std.algorithm so complicated to use?

2012-07-10 Thread Christophe Travert
Dmitry Olshansky , dans le message (digitalmars.D:171679), a écrit :
> Because uniq work only on sorted ranges? Have you tried reading docs?
> "
> Iterates unique consecutive elements of the given range (functionality 
> akin to the uniq system utility). Equivalence of elements is assessed by 
> using the predicate pred, by default "a == b". If the given range is 
> bidirectional, uniq also yields a bidirectional range.
> "

Not, as the doc says, uniq work on any range, but remove only the 
consecutive elements. It you want to remove all duplicates, 
then you need a sorted range.




Re: Why is std.algorithm so complicated to use?

2012-07-10 Thread Christophe Travert
"Simen Kjaeraas" , dans le message (digitalmars.D:171678), a écrit :
>> Well, I haven't been able to use a single function from std.algorithm  
>> without adding a lot of calls to "array" or "to!(string)". I think the  
>> things I'm trying to do seems trivial and quite common. I'm I overrating  
>> std.algorithm or does it not fit my needs?
>>
> 
> bearophile (who else? :p) has suggested the addition of eager and in-place
> versions of some ranges, and I think he has a very good point.

That would have been useful before UFSC.
Now, writing .array() at the end of an algorithm call is not a pain.

int[] = [1, 2, 2, 3].uniq().map!toString().array();



Re: Why is std.algorithm so complicated to use?

2012-07-10 Thread Christophe Travert
Jacob Carlborg , dans le message (digitalmars.D:171690), a écrit :
>> int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
>> assert(equal(uniq(arr), [ 1, 2, 3, 4, 5 ][]));
> 
> How should I know that from the example?


Maybe there should be an example with an unsorted range, and a better 
explanation:

| auto uniq(...)
|   Iterates unique consecutive elements of a given range (...)
|   Note that equivalent elements are kept if they are not consecutive.
| 
| Example:
|   int[] arr = [ 1, 2, 2, 3, 4, 4, 4, 2, 4, 4];
|   assert(equal(uniq(arr), [ 1, 2, 3, 4, 2, 4][]));


Re: Why is std.algorithm so complicated to use?

2012-07-10 Thread Christophe Travert
Andrei Alexandrescu , dans le message (digitalmars.D:171717), a écrit :
> On 7/10/12 11:11 AM, Christophe Travert wrote:
>> If you do not want the heap allocation of the array, you can create a
>> one-element range to feed to chain (maybe such a thing could be placed
>> in phobos, next to takeOne).
>>
>> struct OneElementRange(E)
>> {
>>E elem;
>>bool passed;
>>@property ref E front() { return elem; }
>>void popFront() { passed = true; }
>>@property bool empty() { return passed; }
>>@property size_t length() { return 1-passed; }
>>//...
>> }
> 
> Yah, probably we should add something like this:
> 
> auto singletonRange(E)(E value)
> {
>  return repeat(value).takeExactly(1);
> }

It would be much better to use:

auto singletonRange(E)(E value)
{
 return repeat(value).takeOne;
}

as well as:

auto emptyRange(E)(E value)
{
  return repeat(value).takeNone;
}

to have the advantages of takeOne and takeNone over takeExactly.

> I don't think it would be considerably less efficient than a handwritten 
> specialization. But then I've been wrong before in assessing efficiency.

Error message displaying the type of singletonRange(E) will be weird, 
but that's far from being the first place where it will be. Simplicity 
and maintainance of phobos seems more important to me. At least until 
these algorithm get stable, meaning open bug reports on algorithm and 
range are solved, and new bugs appears rarely. Optimisers should have no 
trouble inlining calls to Repeat's methods...




Re: Why is std.algorithm so complicated to use?

2012-07-10 Thread Christophe Travert
Andrei Alexandrescu , dans le message (digitalmars.D:171723), a écrit :
>> auto emptyRange(E)(E value)
>> {
>>return repeat(value).takeNone;
>> }

> That also seems to answer Jonathan's quest about defining emptyRange. 
> Just use takeNone(R.init).

err, that should be more like:

auto singletonRange(E)() // with no parameters
{
  return takeNone!type_of(repeat(E.init))();
}

An emptyRange compatible with singletonRange should be called 
singletonRange and take no parameter, so that emptyRange name could be 
reserved to a real statically empty range (which is pretty easy to 
implement).

-- 
Christophe


Re: Why is std.algorithm so complicated to use?

2012-07-10 Thread Christophe Travert
"Daniel Murphy" , dans le message (digitalmars.D:171720), a écrit :
> Could it be extended to accept multiple values? (sort of like chain)
> eg.
> foreach(x; makeRange(23, 7, 1990)) // NO allocations!
> {
> 
> }
> I would use this in a lot of places I currently jump through hoops to get a 
> static array without allocating. 

That's a good idea. IMHO, the real solution would be to make an easy way 
to create static arrays, and slice them when you want a range.

-- 
Christophe

I it were just me, array litterals would be static, and people 
should use .dup when they want a a surviving slice.

Well, if it were just me, all function signature should tell when 
references to data escape the scope of the function, and all data would 
be allocated automatically where it should by the compiler.


Re: Why is std.algorithm so complicated to use?

2012-07-10 Thread Christophe Travert
Jacob Carlborg , dans le message (digitalmars.D:171725), a écrit :
> On 2012-07-10 17:11, Christophe Travert wrote:
> 
>> What is wrong with foo.chain(["bar"])?
> 
> I think it conceptually wrong for what I want to do. I don't know if I 
> misunderstood ranges completely but I'm seeing them as an abstraction 
> over a collection. With most mutable collection you can add/append an 
> element.

That may be the source of your problem. ranges are not collections. They 
do not own data. They just show data. You can't make them grow. You can 
only consume what you have already read.



Re: Why is std.algorithm so complicated to use?

2012-07-10 Thread Christophe Travert
Jacob Carlborg , dans le message (digitalmars.D:171739), a écrit :
> On 2012-07-10 18:42, Daniel Murphy wrote:
>> "Jacob Carlborg"  wrote in message
>> news:jthlpf$2pnb$1...@digitalmars.com...
>>>
>>> Can't "map" and "filter" return a random-access range if that's what they
>>> receive?
>>>
>> map can, and does.
> 
> It doesn't seem to:
> 
> auto a = [3, 4].map!(x => x);
> auto b = a.sort;
> 
> Result in one of the original errors I started this thread with.

here, map is random-access. But random access is not enough to call 
sort: you need to have assignable (well, swapable) elements in the 
range, if you want to be able to sort it. values accessed via a map are 
not always assignable, since they are the result of a function.

It seems the map resulting from (x => x) is not assignable. This is 
debatable, but since (x => x) is just a stupid function to test. 
Otherwise, you could try the folowing:

auto a = [3, 4].map!(ref int (ref int x) { return x; })();
a.sort;



Re: Why is std.algorithm so complicated to use?

2012-07-10 Thread Christophe Travert
"Daniel Murphy" , dans le message (digitalmars.D:171741), a écrit :
> "Christophe Travert"  wrote in message 
> news:jthmu8$2s5b$1...@digitalmars.com...
>> "Daniel Murphy" , dans le message (digitalmars.D:171720), a écrit :
>>> Could it be extended to accept multiple values? (sort of like chain)
>>> eg.
>>> foreach(x; makeRange(23, 7, 1990)) // NO allocations!
>>> {
>>> 
>>> }
>>> I would use this in a lot of places I currently jump through hoops to get 
>>> a
>>> static array without allocating.
>>
>> That's a good idea. IMHO, the real solution would be to make an easy way
>> to create static arrays, and slice them when you want a range.
> 
> It's not quite the same thing, static arrays are not ranges and once you 
> slice them you no longer have a value type, and might be referring to stack 
> allocated data.  With... this thing, the length/progress is not encoded in 
> the type (making it rangeable) but the data _is_ contained in the type, 
> making it safe to pass around.  The best of both worlds, in some situations.

OK, I see. This goes against the principle that ranges are small and 
easy to copy arround, but it can be useful when you know what you are 
doing, or when the number of items is small.

I don't like makeRange much. Would you have a better name? smallRange? 
rangeOf?




Re: Why is std.algorithm so complicated to use?

2012-07-10 Thread Christophe Travert
Jacob Carlborg , dans le message (digitalmars.D:171725), a écrit :
>> To make the best implementation would require to know how the String
>> context works.
>>
> String is a wrapper around str.array.Appender.

Then, if the purpose is to make the code efficient, I would use the loop 
and append everything to the result without creating the params array, 
and even without creating the string p. Appender is made to append 
everything directly to it efficiently.



Re: Why is std.algorithm so complicated to use?

2012-07-10 Thread Christophe Travert
Jacob Carlborg , dans le message (digitalmars.D:171769), a écrit :
> On 2012-07-10 20:04, Andrei Alexandrescu wrote:
> 
>> Then store an array. "No one's put a gun to yer head."
>> http://youtu.be/CB1Pij54gTw?t=2m29s
> 
> That's what I'm doing.
> 

And that's what you should do. Algorithm are not made to be stored in 
struct or class instance. You could use InputRange(E) and friends 
to do that, but that's often not optimal. Algorithm are here to do 
their job and output non-lazy result in the end.


Re: Let's stop parser Hell

2012-07-11 Thread Christophe Travert
Timon Gehr , dans le message (digitalmars.D:171814), a écrit :
> On 07/11/2012 01:16 AM, deadalnix wrote:
>> On 09/07/2012 10:14, Christophe Travert wrote:
>>> deadalnix , dans le message (digitalmars.D:171330), a écrit :
>>>> D isn't 100% CFG. But it is close.
>>>
>>> What makes D fail to be a CFG?
>>
>> type[something] <= something can be a type or an expression.
>> typeid(somethning) <= same here
>> identifier!(something) <= again
> 
> 'something' is context-free:
> 
> something ::= type | expression.

Do you have to know if something is a type or an expression for a simple 
parsing? The langage would better not require this, otherwise simple 
parsing is not possible without looking at all forward references and 
imported files.


Re: Inherited const when you need to mutate

2012-07-11 Thread Christophe Travert
Andrei Alexandrescu , dans le message (digitalmars.D:171828), a écrit :
> On 7/10/12 5:19 PM, H. S. Teoh wrote:
> 
> There is value in immutable objects that has been well discussed, which 
> is incompatible with logical constness. We can change the language such 
> as: a given type X has the option to declare "I want logical const and 
> for that I'm giving up the possibility of creating immutable(X)". That 
> keeps things proper for everybody - immutable still has the strong 
> properties we know and love, and such types can actually use logical const.
> 
> A number of refinements are as always possible.

I think this is a good idea, but for classes, inheritance is an issue. 

Example:

class A
{
  int a;
  int compute() const pure { return a; }
  final int fun() const pure
  {
a.compute; // optimised out by the compiler
return a.compute;
  }
}

class B : A
{
  @mutable int b;
  override int compute() const pure
  {
 if(!b) b = longComputation(a);
 // a += 1; // error, a is bitwise-const
 return b; // mutating and returning a mutable part at the 
   // programmer's risk
  }
}

A.compute is bitwise const. However B.compute is logical const. A.fun is 
bitwise const, and can be optimised. But that is no longer true with a B 
instance. However, the compiler must be able to make those 
optimizations, otherwise all the power of const for any non final object 
is lost, because someone may derive a logical const class. This means 
the programmer is *responsible* for creating a logical const-behavior. 
This is a serious issue.

Given the system-programming aspect of D, I would say the programmer 
should be allowed to do such a thing, taking the risk to have an 
undefined behavior. But with great caution. At least, it will be less 
dangerous than casting away const. Just providing a way to make it 
impossible to create an immutable instance of some classes would make it 
less dangerous to cast away constness.

-- 
Christophe


Re: opApply not called for foeach(container)

2012-07-11 Thread Christophe Travert
"monarch_dodra" , dans le message (digitalmars.D:171868), a écrit :
> I'm wondering if this is the correct behavior? In particular, 
> since foreach guarantees a call to opSlice(), so writing "arr[]" 
> *should* be redundant, yet the final behavior is different.
> 
> That said, the "issue" *could* be fixed if the base class defines 
> opApply as: "return opSlice().opApply(dg)" (or more complex). 
> However:
> a) The implementer of class has no obligation to do this, since 
> he has provided a perfectly valid range.
> b) This would force implementers into more generic useless 
> boilerplate code.
> 
> What are your thoughts? Which is the "correct" solution? Is it a 
> bug with foreach, or should the base struct/class provide an 
> opApply?

I think foreach should never call opSlice. That's not in the online 
documentation (http://dlang.org/statement.html#ForeachStatement), unless 
I missed something. If you want to use foreach on a class with an 
opSlice, then yes, you should define opApply. Otherwise, the user have 
to call opSlice himself, which seems reasonable. That's how I understand 
the doc.

-- 
Christophe


Re: opApply not called for foeach(container)

2012-07-11 Thread Christophe Travert
"monarch_dodra" , dans le message (digitalmars.D:171902), a écrit :
> I just re-read the docs you linked to, and if that was my only 
> source, I'd reach the same conclusion as you.

I think the reference spec for D should be the community driven and 
widely available website, not a commercial book. But that's not the 
issue here.

> however, my "The D 
> Programming Language", states:
> *12: Operator Overloading
> **9: Overloading foreach
> ***1: foreach with Iteration Primitives
> "Last but not least, if the iterated object offers the slice 
> operator with no arguments lst[], __c is initialized with lst[] 
> instead of lst. This is in order to allow ?extracting? the 
> iteration means out of a container without requiring the 
> container to define the three iteration primitives."
> 
> Another thing I find strange about the doc is: "If the foreach 
> range properties do not exist, the opApply method will be used 
> instead." This sounds backwards to me.

Skipping the last paragraph, a reasonable interpretation would be that 
foreach try to use, in order of preference:
 - for each over array
 - opApply
 - the three range primitives (preferably four if we include save)
 - opSlice (iteration on the result of opSlice is determined by the same 
system).

 opApply should come first, since if someone defines opApply, he or she 
obviously wants to override the range primitive iteration.
 opApply and range primitives may be reached via an alias this.
 opSlice is called only if no way to iterate the struct/class is found. 
 I would not complain if the fourth rule didn't exist, because it's not 
described in dlang.org, but it is a reasonable feature that have be 
taken from TDPL (but then it should be added in dlang.org).


This way, if arr is a container that defines an opSlice, and that does 
not define an opApply, and does not define range primitives:

foreach (a, arr) ...
and
foreach (a, arr[]) ...
should be stricly equivalent. Since the first is translated into the 
second. Both work only if arr[] is iterable.

I think you hit a bug.

-- 
Christophe


Re: Congratulations to the D Team!

2012-07-11 Thread Christophe Travert
Andrei Alexandrescu , dans le message (digitalmars.D:171945), a écrit :
> On 7/11/12 1:40 PM, Jakob Ovrum wrote:
>> Some classes don't lend themselves to immutability. Let's take something
>> obvious like a class object representing a dataset in a database. How is
>> an immutable instance of such a class useful?
> 
> This is a good point. It seems we're subjecting all classes to certain 
> limitations for the benefit of a subset of those classes.

Does Object really need to implement opEquals/opHash/... ? This is what 
limits all 
classes that do not want to have the same signature for opEquals.

The problem is not only in the constness of the argument, but also in 
its purity, safety, and throwability (although the last two can be 
worked arround easily).

You can compare struct, all having different opEquals/opHash/ You 
can put them in AA too. You could do the same for classes. Use a 
templated system, and give the opportunity for the class to provide its 
own signature ? There may be code bloat. I mean, classes will suffer 
from the same code bloat as structs. Solutions can be found reduce this 
code bloat.

Did I miss an issue that makes it mandatory for Object to implement 
opEquals and friends ?

-- 
Christophe


Re: Congratulations to the D Team!

2012-07-12 Thread Christophe Travert
"David Piepgrass" , dans le message (digitalmars.D:172007), a écrit :
> @mutating class B : A
> {
> private int _x2;
> public @property override x() { return _x2++; }
> }

A fun() pure;

You can't cast the result of fun to immutable, because it may be a B 
instance.


Re: Inherited const when you need to mutate

2012-07-12 Thread Christophe Travert
"David Piepgrass" , dans le message (digitalmars.D:172009), a écrit :
>> Now, I recognize and respect the benefits of transitive 
>> immutability:
>> 1. safe multithreading
>> 2. allowing compiler optimizations that are not possible in C++
>> 3. ability to store compile-time immutable literals in ROM
>>
>> (3) does indeed require mutable state to be stored separately, 
>> but it doesn't seem like a common use case (and there is a 
>> workaround), and I don't see how (1) and (2) are necessarily 
>> broken.
> 
> I must be tired.
> 
> Regarding (1), right after posting this I remembered the 
> difference between caching to a "global" hashtable and storing 
> the cached value directly within the object: the hashtable is 
> thread-local, but the object itself may be shared between 
> threads. So that's a pretty fundamental difference.
> 
> Even so, if Cached!(...) puts mutable state directly in the 
> object, fast synchronization mechanisms could be used to ensure 
> that two threads don't step on each other, if they both compute 
> the cached value at the same time. If the cached value is 
> something simple like a hashcode, an atomic write should suffice. 
> And both threads should compute the same result so it doesn't 
> matter who wins.

Yes. It is possible to write a library solution to compute a cached 
value by casting away const in a safe manner, even in a multithreaded 
environment. The limitation is that, if I'm not mistaken, this library 
solution cannot ensure it is not immutable (and potentially placed in 
ROM) when it is const, making the cast undefined.


Re: Congratulations to the D Team!

2012-07-12 Thread Christophe Travert
"Jonathan M Davis" , dans le message (digitalmars.D:172005), a écrit :
> On Wednesday, July 11, 2012 13:46:17 Andrei Alexandrescu wrote:
>> I don't think they should be pure. Do you have reasons to think otherwise?
> 
> As I understand it, Walter's current plan is to require that opEquals, opCmp, 
> toString, and toHash be @safe const pure nothrow - for both classes and 
> structs.

And is the plan add each tag one by one, breaking codes in many places 
each time ?




Re: All right, all right! Interim decision regarding qualified Object methods

2012-07-12 Thread Christophe Travert
Timon Gehr , dans le message (digitalmars.D:172014), a écrit :
> Thank you for taking the time.
> 
> Removing the default methods completely is actually a lot better than 
> making inheriting from Object optional or tweaking const beyond 
> recognition and/or usefulness.
> I was afraid to suggest this because it breaks all code that assumes
> that the methods are present in object (most code?), but I think it is
> a great way to go forward.

It's not worse thant breaking all code that overrides opEqual by 
changing it's signature.

> Regarding toString, getting rid of it would imply that the default way
> of creating a textual representation of an object would no longer be
> part of Object, paving the way for the proposal that uses buffers and
> scope delegates - this will be purely a library thing.

I agree. toString should be a purely library solution. The standard 
library could easily use templates trying to use different ways to print 
the the object, depending on what methods are implemented for that 
object: direct conversion to string/wstring/dstring, a standard method 
using delegates, etc.

> Regarding backwards-compatibility, an issue that is trivial to fix is
> the invalidation of 'override' declarations in the child classes.
> They can be allowed with the -d switch for those methods. And if they
> use 'super', the compiler could magically provide the current default
> implementations.

Magic is not good for langage consistency. I would rather do a different 
fix:

Introduce a class in the standard library that is like the current 
Object. To correct broken code, make all classes inheriting from Objet 
inherit from this new class, and rewrite opEqual/opCmp to take this new 
class as an argument instead of Object. This can be done automatically.

People may not want to use that fix, but in that case, we don't have to 
implement a magical behavior with super. What can be used is 
deprecation: if I someone uses super.opEqual (meaning Object.opEqual), 
and others, he should bet a warning saying it's deprectated, with 
explanations on how to solve the issue.

A possible course of action is this:
 - revert changes in Object (with renewed apologies to people having 
worked on that)
 - introduce a class implementing basic Hashes functions with the 
current signatures. (A class with the new signatures could be provided 
too, making use of the late work on Object, which would not be 
completely wasted after all)
 - introduce a deprecation warning on uses of Object.opEqual and 
friends, informing the programmer about the possibility to derive from 
the new class to solve the issue.
 - in the mean time, make the necessary changes to enable classes not to 
have those methods (like structs)
 - after the deprecation period, remove Object.opEqual and friends.


Re: All right, all right! Interim decision regarding qualified Object methods

2012-07-12 Thread Christophe Travert
"Mehrdad" , dans le message (digitalmars.D:172012), a écrit :
> On Thursday, 12 July 2012 at 04:15:48 UTC, Andrei Alexandrescu 
> wrote:
>> Required reading prior to this: http://goo.gl/eXpuX
> 
> Referenced post (for context):
>>> The problem is not only in the constness of the argument, but 
>>> also in
> its purity, safety, and throwability (although the last two can be
> worked arround easily).
> 
> I think we're looking at the wrong problem here.
> 
> If we're trying to escape problems with 'const' Objects by 
> removing the members form Object entirely, that should be raising 
> a red flag with const, not with Object.

const has no problem. It is bitwise const, and it works like that.
Logical const is not implemented in D, but that is a separate issue.

The problem is to force people to use const, because bitwise const may 
not be suited for their problems. If opEquals and friends are const, 
then D forces people to use bitwise const, and that is the problem, that 
is largely widened by the fact that bitwise transitive const is 
particularly viral. But if we do not impose to implement any const 
methods, the problem disappear.


Re: just an idea (!! operator)

2012-07-12 Thread Christophe Travert
"Jonas Drewsen" , dans le message (digitalmars.D:172039), a écrit :
> On Wednesday, 11 July 2012 at 11:18:21 UTC, akaz wrote:
>> if needed, the operator !! (double exclamation mark) could be 
>> defined.
>>
>> ...
> 
> Or the operator?? could be borrowed from c#
> 
> auto a = foo ?? new Foo();
> 
> is short for:
> 
> auto a = foo is null ? new Foo() : foo;

or maybe:
auto a = ! ! foo ? foo : new Foo();

|| could be redifined to have this a behavior, but it would break code.



Re: just an idea (!! operator)

2012-07-12 Thread Christophe Travert
Christophe Travert, dans le message (digitalmars.D:172047), a écrit :
> "Jonas Drewsen" , dans le message (digitalmars.D:172039), a écrit :
>> On Wednesday, 11 July 2012 at 11:18:21 UTC, akaz wrote:
>>> if needed, the operator !! (double exclamation mark) could be 
>>> defined.
>>>
>>> ...
>> 
>> Or the operator?? could be borrowed from c#
>> 
>> auto a = foo ?? new Foo();
>> 
>> is short for:
>> 
>> auto a = foo is null ? new Foo() : foo;
> 
> or maybe:
> auto a = ! ! foo ? foo : new Foo();

I forgot to mention that foo would be evaluated only once (and the 
second operand would be evaluated lazily). This is the main point of 
this syntax, and it is not easily emulable (as long a lazy is not 
fixed).



Re: just an idea (!! operator)

2012-07-12 Thread Christophe Travert
Jacob Carlborg , dans le message (digitalmars.D:172056), a écrit :
> On 2012-07-12 13:35, Jonas Drewsen wrote:
> 
>> Or the operator?? could be borrowed from c#
>>
>> auto a = foo ?? new Foo();
>>
>> is short for:
>>
>> auto a = foo is null ? new Foo() : foo;
>>
>> /Jonas
>>
> 
> I really like that operator. The existential operator, also known as the 
> Elvis operator :) . It's available in many languages with slightly 
> different semantics.
> 
> -- 
> /Jacob Carlborg

Sweet.

| Elvis Operator (?: )
|
| The "Elvis operator" is a shortening of Java's ternary operator. One 
| instance of where this is handy is for returning a 'sensible default' 
| value if an expression resolves to false or null. A simple example 
| might look like this:
|
| def displayName = user.name ? user.name : "Anonymous" //traditional 
| ternary operator usage
|
| def displayName = user.name ?: "Anonymous"  // more compact Elvis 
| operator - does same as above


(taken from 
http://groovy.codehaus.org/Operators#Operators-ElvisOperator)



Re: Making uniform function call syntax more complete a feature

2012-07-12 Thread Christophe Travert
"Thiez" , dans le message (digitalmars.D:172060), a écrit :
>>> Have you considered adding operator overloading using UFCS 
>>> while you're at it?
>>
>> I assumed it's already possible to add operators 
>> non-intrusively, because operators are just syntactic sugar for 
>> method calls:
>>
>> ++var;  // actual code
>> var.opUnary!"++"(); // lowered once
>> opUnary!"++"(var);  // lowered twice (if necessary)
>>
>> If you're talking about overloading existing operators (which 
>> have been implemented as member functions) non-intrusively for 
>> other types, then I don't know, doesn't it work?
>
> I actually tried those yesterday (with opEquals and opCmp on 
> structs) and couldn't get it to work. Code still used what 
> appeared to be an automatically generated opEquals (that appears 
> to perform a bitwise comparison) instead of my UFCS opEquals.

This behavior for opEquals is debatable, but make sense. If the designer 
of a struct did not implement opEquals, it may be that he intended 
opEqual to be the default opEqual. If you overload opEquals for such 
struct, you may be hijacking it's intended behavior: your not just 
adding a functionality, your overriding an existing functionality.

Did you try operators that are not automatically generated ?
 
> It's already quite obvious that the compiler does not obey its 
> own rewrite rules (see 
> http://dlang.org/operatoroverloading.html#compare) Consider opCmp:
>   a < b
> is rewritten to
>   a.opCmp(b) < 0
> or
>   b.opCmp(a) > 0
> Let's assume the first rule is always chosen. According to the 
> very rewrite rule we just applied, this must be rewritten to
>   a.opCmp(b).opCmp(0) < 0
> 
> It seems quite obvious the compiler does not rewrite compares on 
> integers or all hell would break loose... The language reference 
> should be more specific about these things.

The rewrite rule obviously apply only if the comparison operator is not 
already defined for those types by the langage. That could be precised 
in the web site, but it's consistent.

By the way, would it be possible to implement an opCmp that returns a 
double, to allow it to return a NaN ? That may allow to create values 
that are neither superior, nor inferior to other value, like NaNs. It's 
not possible to implement opCmp for a floating point comparison if opCmp 
is bound to return an int.

Another reason to ban Object imposing a specific signature for opCmp in 
all classes...


Re: Counterproposal for extending static members and constructors

2012-07-12 Thread Christophe Travert
"Jonathan M Davis" , dans le message (digitalmars.D:172156), a écrit :
> On Thursday, July 12, 2012 18:25:03 David Piepgrass wrote:
>> I'm putting this in a separate thread from
>> http://forum.dlang.org/thread/uufohvapbyceuaylo...@forum.dlang.org
>> because my counterproposal brings up a new issue, which could be
>> summarized as "Constructors Considered Harmful":
>> 
>> http://d.puremagic.com/issues/show_bug.cgi?id=8381
> 
> I think that adding constructors to a type from an external source is 
> downright evil. It breaks encapsulation. I should be able to constrain 
> exactly 
> how you construct my type. If you want to create a free function (e.g. a 
> factory function) which uses my constructors, fine. But I'm completely 
> against 
> adding constructors externally.

The proposal is not that add constructors. It is to create a free 
function (.make!Type(args)), that can called like a constructor, by 
writing Type(args). That does not break encapsulation.


Re: just an idea (!! operator)

2012-07-13 Thread Christophe Travert
"David Piepgrass" , dans le message (digitalmars.D:172164), a écrit :
>>> Yeah, I've been planning to try and get this into D one day.  
>>> Probably
>>> something like:
>>> (a ?: b) ->  (auto __tmp = a, __tmp ? __tmp : b)
>>
>> gcc used to have that extension and they dropped it...
> 
> But GCC can't control the C++ language spec. Naturally there is a 
> reluctance to add nonstandard features. It's a successful feature 
> in C#, however, and a lot of people (including me) have also been 
> pestering the C# crew for "null dot" (for safely calling methods 
> on object references that might be null.)
> 
> I don't see why you would use ?: instead of ??, though.

Because ?: is the ternary conditionnal operator with missing 
second operand.

a ?: b // or maybe a ? : b
is just a short hand for
a ? a : b
(except a is evaluated only once).


Re: Counterproposal for extending static members and constructors

2012-07-13 Thread Christophe Travert
>> In any case, std.container already declares a make which encapsulates
>> constructing an object without caring whether it's a struct or class (since
>> some containers are one and some another), which I intend to move to
>> std.typecons and make work with all types. That seems a lot more useful to me
>> than trying to make a function act like a constructor when it's not - though 
>> I
>> guess that as long as you imported std.typecons, I would just be providing 
>> the
>> free function that your little constructor faking scheme needs.

The same can be said for UFCS. Your just faking member functions with a 
free function. I don't understand why constructors are so different.

A library might write generic code and use a constructor to perform 
something. I can't use that generic code if I don't own the struct or 
class and am able to write a constructor. You can argue that the library 
should have use make, instead of calling the constructor or using some 
cast. But they can't think of every usages, and may not know about make. 
Plus it may make the code uglier.

It's like standard UFCS. library writer should never call a member 
function, and always call a free function that is templated, specialised 
to use the member function if available, to provide workarround, or 
that can be further specialised if someone wants to extend the class. 
yk...


Re: Move semantics for D

2012-07-13 Thread Christophe Travert
Benjamin Thaut , dans le message (digitalmars.D:172207), a écrit :
> Move semantics in C++0x are quite nice for optimization purposes. 
> Thinking about it, it should be fairly easy to implement move semantics 
> in D as structs don't have identity. Therefor a move constructor would 
> not be required. You can already move value types for example within an 
> array just by plain moving the data of the value around. With a little 
> new keyword 'mov' or 'move' it would also be possible to move value 
> types into and out of functions, something like this:
> 
> mov Range findNext(mov Range r)
> {
>//do stuff here
> }
> 
> With something like this it would not be neccessary to copy the range 
> twice during the call of this function, the compiler could just plain 
> copy the data and reinitialize the origin in case of the argument.
> In case of the return value to only copying would be neccessary as the 
> data goes out of scope anyway.

If Range is a Rvalue, it will be moved, not copied.
It it's a Lvalue, your operation is dangerous, and does not bring you 
much more than using ref (it may be faster to copy the range than to 
take the reference, but that's an optimiser issue).

auto ref seems to be the solution.

> I for example have a range that iterates over a octree and thus needs to 
> internally track which nodes it already visited and which ones are still 
> left. This is done with a stack container. That needs to be copied 
> everytime the range is copied, which causes quite some overhead.

I would share the tracking data between several instance of the range, 
making bitwise copy suitable. Tracking data would be duplicated only on 
call to save or opSlice(). You'd hit the issue of foreach not calling 
save when it should, but opSlice would solve this, and you could still 
overload opApply if you want to be sure.

-- 
Christophe


Re: just an idea (!! operator)

2012-07-13 Thread Christophe Travert
"Jonas Drewsen" , dans le message (digitalmars.D:172242), a écrit :
> Can you identify any ambiguity with an ?. operator.

? could be the begining of a ternary operator, and . the module scope 
indicator, or the beginning of a (badly) written float number.

Both case can be disambiguated by the presence of the ':' in the case 
of a ternary operator.

I don't think '?' has currently any other meaning in D.


Re: just an idea (!! operator)

2012-07-13 Thread Christophe Travert
"Roman D. Boiko" , dans le message (digitalmars.D:172259), a écrit :
> On Friday, 13 July 2012 at 13:46:10 UTC, David Nadlinger wrote:
>> I guess that this operator is only really worth it in languages
>> where every type is nullable, though.
>>
>> David
> 
> It might mean identity (return the argument unchanged) for value 
> types.

It might mean: give me the default I provide as an extra argument:

Example:
car?.driver?.name ?: "anonymous";

rewrites:
car? car.driver? car.driver.name? car.driver.name? car.driver.name
 :anonymous
:anonymous
   :anonymous
   :anonymous



Re: nested class inheritance

2012-07-13 Thread Christophe Travert
"Era Scarecrow" , dans le message (digitalmars.D:172269), a écrit :
> class Fruit {
>   int x;
>   class Seed {
>void oneMoreToX() {
> x++; //knows about Fruit.x, even if not instantiated
>}
>   }
> 
>   static class Seed2 {
>void oneMoreToX() {
> //  x++; //fails to compile, no knowledge of Fruit
>}
>   }
> }
> 
> class Apple: Fruit {
>   class AppleSeed: Fruit.Seed { } //fails (no outer object 
> (Fruit.x) and makes no sense)
>   class AppleSeed2: Fruit.Seed2 { } //works fine
> }

AppleSeed does have a outer Fruit, and this Fruit happens to be an 
Apple. I don't see what is the issue, and what prevents the langage to 
support such AppleSeed. I'm not saying there is nothing that prevents 
the langage to support such pattern, I am not used to inner classes. 
Pleas enlighten me.


Re: nested class inheritance

2012-07-13 Thread Christophe Travert
"Era Scarecrow" , dans le message (digitalmars.D:172272), a écrit :
>   Then perhaps have the inherited class within fruit?
> 
> class Fruit {
>class Seed {}
>class Appleseed : Seed {}
> }

But then AppleSeed doesn't know about Apple



Re: nested class inheritance

2012-07-13 Thread Christophe Travert
Andrei Alexandrescu , dans le message (digitalmars.D:172280), a écrit :
>> For Fruit.Seed it's Fruit, for AppleSeed it's Apple. This makes sense
>> because the Apple, which AppleSeed sees is the same object, which
>> Fruit.Seed sees as it's base type Fruit.
> 
> That would mean AppleSeed has two outer fields: a Fruit and an Apple.

Only one. Apple. And when AppleSeed.super seed this Apple, it sees a 
fruit.

AppleSeed a;
assert(is(typeof(a.outer) == Apple));
assert(is(typeof(a.super) == Seed));
assert(is(typeof(a.super.outer) == Fruit));
//but:
assert(a.outer is a.super.outer);

If you can't figure out how can a.outer and a.super.outer have two 
different types, but be the same, think about covariant return.




Re: Making uniform function call syntax more complete a feature

2012-07-16 Thread Christophe Travert
"Simen Kjaeraas" , dans le message (digitalmars.D:172349), a écrit :
> On Thu, 12 Jul 2012 16:31:34 +0200, Christophe Travert  
>  wrote:
> 
>> By the way, would it be possible to implement an opCmp that returns a
>> double, to allow it to return a NaN ? That may allow to create values
>> that are neither superior, nor inferior to other value, like NaNs. It's
>> not possible to implement opCmp for a floating point comparison if opCmp
>> is bound to return an int.
> 
> Why don't you just test it? Not like it'd be many lines of code.
> 
> Anyways, yes this works.

Thanks. I don't always have a d compiler at hand when I read this 
newsgroup. Maybe I should just write myself a todo to make this kind of 
test back home rather than directly posting the idea.


Re: Array index slicing

2012-07-16 Thread Christophe Travert
"bearophile" , dans le message (digitalmars.D:172300), a écrit :
> If enumerate() is well implemented it's one way to avoid that 
> problem (other solutions are possible), now 'c' gets sliced, so 
> it doesn't start from zero:
> 
> import std.stdio;
> void main() {
>  auto M = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]];
>  foreach (r, row; M)
>  foreach (c, ref item; enumerate(row)[1 .. $])
>  item = c * 10 + r;
>  writefln("%(%s\n%)", M);
> }

enumerate could be useful with retro too. You may want to change the 
order of the enumeration, but not the order of the indices.




Re: Why doesn't to!MyEnumType(42) work

2012-07-17 Thread Christophe Travert
"Era Scarecrow" , dans le message (digitalmars.D:172568), a écrit :
> On Monday, 16 July 2012 at 21:59:17 UTC, Tommi wrote:
>> On Monday, 16 July 2012 at 20:22:12 UTC, Era Scarecrow wrote:
>>> MyEnumType y = cast(MyEnumType) 42; //Error: wtf is 42 anyways?
>>
>> Like the previous fellow said, it's not an error.
> 
>   I had the impression it was illegal by the compiler; Logically 
> forcing an enum to an invalid state is probably undefined and 
> unstable (but casting with compile-time constant that's provably 
> correct would be different). Also I've never tried force casting 
> the enums, so.. Hmmm...
> 
>   I suppose 'use at your own risk' applies here.

For what it's worth, I think cast should be at your own risk, and 
to!MyEnumType should assert the enum is valid.


Re: Why is std.algorithm so complicated to use?

2012-07-17 Thread Christophe Travert
Jonathan M Davis , dans le message (digitalmars.D:172564), a écrit :
> They're likely to contain a lot of stuff negation of other template 
> constraints. For instance,
> 
> auto func(R)(R range)
> if(isForwardRange!R && !isBidirectionalRange!R)
> {}
> 
> auto func(R)(R range)
> if(isBidirectionalRange!R)
> {}
> 
> If you have a function with very many overloads, it can be very easy to end 
> up 
> with a bunch of different template constraints which are all slightly 
> different. 
> std.algorithm.find is a prime example of this.
> 
> But as much as it may be a bit overwhelming to print every failed constraint, 
> without doing that, you _have_ to go to the source code to see what they 
> were, 
> which isn't all that great (especially if it's not in a library that you 
> wrote 
> and don't normally look at the source of - e.g. Phobos).
> 
> On the other hand, a failed instantiation of std.conv.to would print out 
> reams 
> of failed constraints...

The compiler could stop displaying at about 10 failed constrains and 
claim there are more. It would be best if it could figure out what are 
the 10 most interesting constrains, but that may not be easy!

Then it's up to the programmer to use template constrains, static if and 
eventually pragma, to allow the compiler to display pleasant error 
messages. The langage could help you by allowing you to make hiearchized 
template constrains, but static if is a fine solution most of the time.


Re: Definition of "OutputRange" insuficient

2012-07-17 Thread Christophe Travert
"monarch_dodra" , dans le message (digitalmars.D:172586), a écrit :
> I was trying to fix a few bugs in algorithm, as well as be more 
> correct in template type specifiers, and I have to say: There is 
> a serious restraint in the definition of an outputRange.
> 
> The current definition of "isOutputRange" is simply that "an 
> output range is defined functionally as a range that supports the 
> operation put".
> 
> The problem with this is this definition is not very useful (at 
> all). Not just because it lacks front/popFront (this is actually 
> OK). It is because it lacks an "empty". This makes it virtually 
> un-useable unless you are blind writing to an infinite output 
> range.
> 
> The proof that it is useless is that NOT A SINGLE ALGORITHM is 
> compatible with output ranges. All algorithms that really only 
> require an "output" systematically actually require 
> "isForwardRange", or, worse yet "isInputRange" (!). This is only 
> because they all make use of range.empty.

OutputRange is designed for infinite output ranges, like output files 
and appender.

Copy is the only algorithm that uses OutputRange. But still, this is 
enough. That enables to do a lot of things, since copy can branch to any 
lazy input range performing all the generic operation you want*.

However, output range with a limited capacity are not taken into 
account. They are partly covered by the input ranges family. Most ranges 
that are output ranges with a capacity are also input ranges. Arguably, 
we may want to cover output ranges with capacity that are not input 
range. This would may make fill cleaner. uninitializedFill would 
be fill with an output range that does not defines a front method, which 
would be much cleaner than using emplace arbitrarily on a range that is 
not designed for that. This also opens the door to an algorithm that 
copy a input range into an output range, but stops when the output range 
is full of the input range is empty, and return both filled output range 
and consumed input range (the input range could be taken by ref). Copy 
could be improved with an additional "StopPolicy" template argument.

To do this, two methods could be added to output ranges, one telling if 
the range is full, and one telling what is the remaining capacity of the 
range (capacity already has a meaning, some other word should be used). 
These methods are like empty and length.

-- 
Christophe

Out of topic foot note:
* After thoughts, one thing you can't do directly in phobos is to modify 
an output range to return a new output range. You are obliged 
to apply the operation on the input range.
For example:
input.map!toString().copy(output);
can't be written:
input.copy(output.preMap!toString());

Creating a modified output range like my (badly named) output.preMap can 
be useful, but may be to marginal to be put in Phobos. With a revamped 
stdio using more ranges, this may become less marginal. map, joiner, 
filter, chain, take and friends, zip and chunks may have a meaning for 
output range with capacity...


Re: Initialization of std.typecons.RefCounted objects

2012-07-18 Thread Christophe Travert
Matthias Walter , dans le message (digitalmars.D:172673), a écrit :
> I looked at Bug #6153 (Array!(Array!int) failure) and found that the
>
> This exactly is what makes the following code fail:
> 
> Array!(Array!int) array2d;
> array2d.length = 1;
> array2d[0].insert(1);
> 
> The inner array "array2d[0]" was not initialized and hence the reference
> pointer is null. Since Array.opIndex returns by value, the 'insert'
> method is called on a temporary object and does not affect the inner
> array (still being empty) which is stored in the outer array.
> 
> What do you think about this?
> 
> Must the user ensure that the Array container is always initialized
> explicitly? If yes, how shall this happen since the only constructor
> takes a (non-empty) tuple of new elements. Or shall opIndex return by
> reference?

I think opIndex should return by reference. opIndexAssign is of no help 
when the user want to use a function that takes a reference (here 
Array.insert). It is normal that Array uses default construction when 
someone increases the array's length.

Besides that point, I don't see why default-constructed Array have an 
uninitialised Payload. This makes uninitialised Array behaves 
unexpectedly, because making a copy and using the copy will not affect 
the original, which is not the intended reference value behavior.

Correcting default-initialization of Array would correct your bug, but 
would not correct the wrong behavior of Array when the value returned by 
opIndex is used with a non-const method with other objects (dynamic 
arrays?). So both have to be changed in my opinion.

-- 
Christophe



Re: Initialization of std.typecons.RefCounted objects

2012-07-18 Thread Christophe Travert
I see you found the appropriate entry to discuss this bug:

http://d.puremagic.com/issues/show_bug.cgi?id=6153



Re: Octal Literals

2012-07-18 Thread Christophe Travert
"Dave X." , dans le message (digitalmars.D:172680), a écrit :
> Not that this really matters, but out of curiosity, how does this 
> template work?


By looking at the sources, if the template argument is a string, the 
program just compute the octal value as a human would do, that is it 
makes the sum of the digits multiplied by their respective power of 8. 
If the template argument is not a string, it is transformed into a 
string and the previous algorithm is used.

-- 
Christophe


Re: Formal request to remove "put(OutRange, RangeOfElements)"

2012-07-18 Thread Christophe Travert
That sounds reasonable and justified. Let's wait to know if people 
maintaining legacy code will not strongly oppose to this.


Re: Initialization of std.typecons.RefCounted objects

2012-07-19 Thread Christophe Travert
"monarch_dodra" , dans le message (digitalmars.D:172700), a écrit :
> I think it would be better to "initialize on copy", rather than 
> default initialize. There are too many cases an empty array is 
> created, then initialized on the next line, or passed to 
> something else that does the initialization proper.

Not default-initializing Array has a cost for every legitimate use of an 
Array. I think people use Array more often than they create 
uninitialized ones that are not going to be used before an other Array 
instance is assigned to them, so Array would be more efficient if it was 
default initialized and never check it is initialized again. But that's 
just speculation.

> You'd get the correct behavior, and everything else (except dupe) 
> works fine anyways.

Keeping the adress of the content secret may be a valuable intention, 
but as long as properties and opIndex does not allow to correctly 
forward methods, this is completely broken. Is there even a begining of 
a plan to implement this? I don't see how properties or opIndex could 
safely forward methods that uses references and that we do not control 
without escaping the reference to them. That's not possible until D has 
a complete control of escaping references, which is not planned in the 
near or distant future. Not to mention that having a complete control of 
escaping reference break lots of code anyway, and might considerably 
decrease the need for ref counted utilities like... Array.

Please, at least give me hope that there is light at the end of the 
tunnel.

-- 
Christophe


Re: Initialization of std.typecons.RefCounted objects

2012-07-19 Thread Christophe Travert
"monarch_dodra" , dans le message (digitalmars.D:172710), a écrit :
> One of the reason the implementation doesn't let you escape a 
> reference is that that reference may become (_unverifiably_) 
> invalid.

The same applies to a dynamic array: it is undistinguishable from a 
sliced static array. More generally, as long as you allow variables on 
the stack with no escaped reference tracking, you can't ensure 
references remain valid. Even in safe code.

If I want my references to remain valid, I use dynamic array and garbage 
collection. If I use Array, I accept that my references may die. Array 
that protects the validity of their references are awesome. But, IMHO, 
not at that cost.

> ...That said, I see no reason for the other containers (SList, 
> I'm looking at you), not to expose references.

I'm against not exposing reference, but all containers will be 
implemented with custom allocator someday.

> The current work around? Copy-Extract, manipulate, re-insert. 
> Sucks.

IMO, what sucks even more is that arr[0].insert(foo) compiles while it 
has no effect. arr[0] is a R-value, but applying method to R-value is 
allowed. I don't know the state of debates about forbiding to call 
non-const methods on R-values. I think this would break too much code.



  1   2   >