Jump to page: 1 2
Thread overview
Re: D as a prototyping language (for C/C++ projects)
Feb 27, 2013
H. S. Teoh
Feb 27, 2013
Jacob Carlborg
Feb 27, 2013
H. S. Teoh
Feb 27, 2013
Jacob Carlborg
Feb 27, 2013
Ary Borenszweig
Feb 28, 2013
Jacob Carlborg
Feb 28, 2013
Jacob Carlborg
Feb 28, 2013
David Nadlinger
Feb 28, 2013
Jacob Carlborg
Mar 02, 2013
Timon Gehr
Feb 27, 2013
Rob T
Feb 27, 2013
Jacob Carlborg
Feb 27, 2013
Ary Borenszweig
Mar 02, 2013
Daniel Murphy
February 27, 2013
On Wed, Feb 27, 2013 at 02:02:01PM +0100, Jacob Carlborg wrote:
> On 2013-02-26 22:26, H. S. Teoh wrote:
> 
> >I find the Phobos version more readable, actually. Writing "2.days.ago" is all neat and clever and everything, but the logician in me protests at what all those '.'s are referring to, and they do. Code is not English, and when it pretends to be English, it makes me suspicious that something is subtle going on that I'm not aware of. And when I'm coding, that's something I don't like -- I need to have the assurance I know exactly what's going on. And yes I can learn to parse "2.days.ago" eventually, but it's just additional mental load for only superficial convenience.
> 
> I don't agree. BTW, the whole point of having programming languages is for programmers to read and write it. Therefore they are similar to English (or some other language). For the computer the language is just in the way. It just wants to execute machine code.

OK, then let's agree to disagree. I see that there's no point in arguing about this, since it ultimately comes down to preference, which can never be reconciled.


> >IMHO this is just bikeshedding. I actually find Phobos' order more intuitive (to me anyway), because the function you're reducing on, ideally, should be inlined, so it makes sense to pass it as a compile-time parameter, and the seed value is what you start with, so it makes sense to have it appear as the first parameter.
> 
> I never said it shouldn't be passed as a compile time parameter. Look at the example. I added the seed to the compile time parameter list.

Yes, I was just talking about the order of parameters.


> >But I can see where UFCS will fail to kick in here, which makes it a bit annoying, I suppose. But reduce was designed before UFCS made it into DMD.
> 
> Then it should be changed. Oh, wait. We can't break any code, every
> 
> I don't know why I keep bothering. You (not as in "you" personally)
> never want to improve anything.

Not sure who you're referring to here, but the reason reduce probably will not change is because a lot of code relies on the current order of parameters, and deliberately breaking that just for aesthetic (rather than functional) reasons is not a good idea. It would be a different story if the current order of parameters somehow makes it impossible to implement some particular functionality. I agree that perhaps the current situation is not perfect, but at least it's not a complete road blocker.


> >Couldn't you use map for this purpose?
> 
> No, not as far as I know. "map" expects a range and returns a new range. "tap" expects an object and returns the same object. You can use "tap" to tap into chains of method calls and change an object.

Isn't that the same as:

	map!((obj x) { doSomething(x); return x; })(range)

?

I know it's not as pretty, but at least it's possible.


> >Unfortunately, on this point I have to disagree.
> >
> >I find !isBlank much better. Generally, I prefer a more minimal API, but I suppose people coming from Ruby might expect a more "rich" kind of API design (caveat: I don't know Ruby). Having too many ways of stating the same thing makes me wonder if there's some subtle bug lurking somewhere.  What if isPresent is not 100% equivalent to !isBlank? And I don't mean just hypothetically; I've seen cases of libraries where bugs cause two supposedly-opposite functions to be not exactly opposite, and then code starts to depend on the buggy behaviour, causing hidden bugs later when the code gets fixed, etc.. A lot of unnecessary problems when you could have just written !isBlank in the first place.
> 
> This is not about adding "isPresent" to an already existing "isBlank" this is about adding "isBlank" or "isPresent" which don't exist.

My point was that you only need one of them, not both. I don't see what's the advantage of adding both isBlank and isPresent, when adding just one will already give you the functionality of the other. I didn't mean to say that it's a bad idea to add *either* one.


> >Now, I used to be a Perl fanatic, so I totally understand TIMTOWTDI and all that, but I think having a separate function just to encode !isBlank is a bit too extreme.  The language has boolean operators for a reason, after all.
> 
> isBlank does way more than checking if a value is false or not. Do you read the code at all?
[...]

Again, my point was not that it's a bad idea to have isBlank. My point was that if you add isBlank, then isPresent is redundant, and I would argue even harmful. The best APIs are minimal ones, that provide all *necessary* primitives with minimal overlap between them.


T

-- 
Who told you to swim in Crocodile Lake without life insurance??
February 27, 2013
On 2013-02-27 18:35, H. S. Teoh wrote:


> Not sure who you're referring to here, but the reason reduce probably
> will not change is because a lot of code relies on the current order of
> parameters, and deliberately breaking that just for aesthetic (rather
> than functional) reasons is not a good idea. It would be a different
> story if the current order of parameters somehow makes it impossible to
> implement some particular functionality. I agree that perhaps the
> current situation is not perfect, but at least it's not a complete road
> blocker.

That's the problem. Then we will have a language with a slightly inconsistent and inconvenient standard library.

> Isn't that the same as:
>
> 	map!((obj x) { doSomething(x); return x; })(range)
>
> ?
>
> I know it's not as pretty, but at least it's possible.

No it's not the same thing. "map" expects a range and returns a new range. "map" will pass each element from the range to the delegate and then return a new range of all elements returned for each call to the delegate.

"tap" on the other hand expects an object or value. It will then pass the object to the delegate and then return the object. Note that it doesn't return whats returned from the delegate. It will always return the object passed to "tap".

The most basic, non-templated implementation of "tap" would look like this:

Object tap (alias func) (Object o)
{
    func(o);
    return o;
}

class Point
{
    int x;
    int y;
}

Point createPoint ()
{
    return (new Point).tap!((p) { p.x = 3; p.y = 4 });
}

> Again, my point was not that it's a bad idea to have isBlank. My point
> was that if you add isBlank, then isPresent is redundant, and I would
> argue even harmful. The best APIs are minimal ones, that provide all
> *necessary* primitives with minimal overlap between them.

OK, then it was a misunderstanding, my bad. But with this philosophy it will not be as easy create quick scripts compared to scripting languages. This the original question, how to improve that in D.

-- 
/Jacob Carlborg
February 27, 2013
On Wednesday, 27 February 2013 at 17:37:34 UTC, H. S. Teoh wrote:
[...]
>
> Again, my point was not that it's a bad idea to have isBlank. My point
> was that if you add isBlank, then isPresent is redundant, and I would
> argue even harmful. The best APIs are minimal ones, that provide all
> *necessary* primitives with minimal overlap between them.
>
>
> T

I agree.

I can only see a need for "isPresent" (or the opposite) only if it was made clear that isPresent does something different than !isBlank.

If you continue to use isPresent, then I suggest that it is implemented as a convenience wrapper around !isBlank to make sure that changes to isBlank automatically propagate back to isPresent.

--rt
February 27, 2013
On Wed, Feb 27, 2013 at 08:54:21PM +0100, Jacob Carlborg wrote:
> On 2013-02-27 18:35, H. S. Teoh wrote:
> 
> >Not sure who you're referring to here, but the reason reduce probably will not change is because a lot of code relies on the current order of parameters, and deliberately breaking that just for aesthetic (rather than functional) reasons is not a good idea. It would be a different story if the current order of parameters somehow makes it impossible to implement some particular functionality. I agree that perhaps the current situation is not perfect, but at least it's not a complete road blocker.
> 
> That's the problem. Then we will have a language with a slightly inconsistent and inconvenient standard library.

Well, given that we have UFCS now, maybe we *do* want to standardize on the range always being the first argument, so that UFCS chaining always works.  I'm OK to have reduce change the order of arguments... but given the amount of code it will break, I doubt it will get in. One way is to introduce the new version under a different name and deprecate "reduce".

I'm not the person you have to convince, though; it's Jonathan or Andrei who will decide whether to accept this change.


> >Isn't that the same as:
> >
> >	map!((obj x) { doSomething(x); return x; })(range)
> >
> >?
> >
> >I know it's not as pretty, but at least it's possible.
> 
> No it's not the same thing. "map" expects a range and returns a new range. "map" will pass each element from the range to the delegate and then return a new range of all elements returned for each call to the delegate.
> 
> "tap" on the other hand expects an object or value. It will then pass the object to the delegate and then return the object. Note that it doesn't return whats returned from the delegate. It will always return the object passed to "tap".

Oh I get it now. So basically it's a wrapper around functions to make UFCS chaining possible?


> The most basic, non-templated implementation of "tap" would look like this:
> 
> Object tap (alias func) (Object o)
> {
>     func(o);
>     return o;
> }

What would a templated version add to this functionality?


> class Point
> {
>     int x;
>     int y;
> }
> 
> Point createPoint ()
> {
>     return (new Point).tap!((p) { p.x = 3; p.y = 4 });
> }

I guess I'm skeptical about the value of using tap in this context, since you could just call the function on the object, then set its values, then return it. So this is just syntactic sugar.

But I guess I can see some use cases where you're chaining a bunch of stuff inside nested function calls, then tap might become convenient:

	return map!myFunc(zip(r1, [
		new X().tap!myFunc2(),
		new X().tap!myFunc2()
	]));

Which can be unpacked into linear code, but you would would need a bunch of temporary variables to hold the intermediate results.


> >Again, my point was not that it's a bad idea to have isBlank. My point was that if you add isBlank, then isPresent is redundant, and I would argue even harmful. The best APIs are minimal ones, that provide all *necessary* primitives with minimal overlap between them.
> 
> OK, then it was a misunderstanding, my bad. But with this philosophy it will not be as easy create quick scripts compared to scripting languages. This the original question, how to improve that in D.
[...]

I disagree with this point. Writing !isBlank is just as easy as writing isPresent (in fact, a few characters less). I don't see how this helps with writing scripts faster.


T

-- 
Answer: Because it breaks the logical sequence of discussion. / Question: Why is top posting bad?
February 27, 2013
On 2013-02-27 21:12, H. S. Teoh wrote:

> Oh I get it now. So basically it's a wrapper around functions to make
> UFCS chaining possible?

Yes, something like that.

> What would a templated version add to this functionality?

Allow structs to be passed as well. Although they need to be passed by ref. But then you would be able to do this:

auto s = Struct();
s.tap!((ref e) { e = Struct(); });

Which should be avoidable if possible.

> I guess I'm skeptical about the value of using tap in this context,
> since you could just call the function on the object, then set its
> values, then return it. So this is just syntactic sugar.
>
> But I guess I can see some use cases where you're chaining a bunch of
> stuff inside nested function calls, then tap might become convenient:
>
> 	return map!myFunc(zip(r1, [
> 		new X().tap!myFunc2(),
> 		new X().tap!myFunc2()
> 	]));
>
> Which can be unpacked into linear code, but you would would need a bunch
> of temporary variables to hold the intermediate results.

This is about the little things that makes it convenient to write code. I'm not saying that you cannot do anything without these things. But the question was, what can we do to make D more usable as a scripting language.

> I disagree with this point. Writing !isBlank is just as easy as writing
> isPresent (in fact, a few characters less). I don't see how this helps
> with writing scripts faster.

No, I was more thinking of "The best APIs are minimal ones". If you only have the minimal building blocks you would need to create wrappers and similar to create convenient functions.

-- 
/Jacob Carlborg
February 27, 2013
On 2013-02-27 21:01, Rob T wrote:

> I agree.
>
> I can only see a need for "isPresent" (or the opposite) only if it was
> made clear that isPresent does something different than !isBlank.
>
> If you continue to use isPresent, then I suggest that it is implemented
> as a convenience wrapper around !isBlank to make sure that changes to
> isBlank automatically propagate back to isPresent.

As I said to H.S. Teoh, the question is not about adding the inverse of an already existing function. It's about adding functionality that doesn't exist. It doesn't matter if isPresent or isBlank would be added.

If you had looked at the code you would see the the implementation of isPresent looks like this:

@property bool isPresent (T) (T t)
{
    return !isBlank(t);
}

Which is _exactly_ the inverse of isBlank.

-- 
/Jacob Carlborg
February 27, 2013
On 2/27/13 2:35 PM, H. S. Teoh wrote:
>> This is not about adding "isPresent" to an already existing "isBlank"
>> this is about adding "isBlank" or "isPresent" which don't exist.
>
> My point was that you only need one of them, not both. I don't see
> what's the advantage of adding both isBlank and isPresent, when adding
> just one will already give you the functionality of the other. I didn't
> mean to say that it's a bad idea to add *either* one.

Then why not remove the binary "-" from D. You can always do:

a + -b

I believe this makes the language simpler.
February 27, 2013
On 2/27/13 5:12 PM, H. S. Teoh wrote:
>> The most basic, non-templated implementation of "tap" would look like
>> this:
>>
>> Object tap (alias func) (Object o)
>> {
>>      func(o);
>>      return o;
>> }
>
> What would a templated version add to this functionality?
>
>
>> class Point
>> {
>>      int x;
>>      int y;
>> }
>>
>> Point createPoint ()
>> {
>>      return (new Point).tap!((p) { p.x = 3; p.y = 4 });
>> }
>
> I guess I'm skeptical about the value of using tap in this context,
> since you could just call the function on the object, then set its
> values, then return it. So this is just syntactic sugar.

Tap is nice when you want to print-debug something and you have a chain of calls:

auto foo = x.map!(...).reduce!(...).nWayUnion!(...);

Now something is not working correctly and you want to see what's after the "reduce!" step:

auto foo = x.map!(...).reduce!(...).tap!(r) { writefln(r); }).nWayUnion!(...);

Otherwise you'd have to break it in many lines and then put them back together.
February 27, 2013
On 2/27/13 3:53 PM, Ary Borenszweig wrote:
> Tap is nice when you want to print-debug something and you have a chain
> of calls:
>
> auto foo = x.map!(...).reduce!(...).nWayUnion!(...);
>
> Now something is not working correctly and you want to see what's after
> the "reduce!" step:
>
> auto foo = x.map!(...).reduce!(...).tap!(r) { writefln(r);
> }).nWayUnion!(...);
>
> Otherwise you'd have to break it in many lines and then put them back
> together.

I like Tap (including the name). It is similar to Unix's "tee" utility.

Andrei
February 28, 2013
On 2013-02-27 22:59, Andrei Alexandrescu wrote:

> I like Tap (including the name). It is similar to Unix's "tee" utility.

Cool, is this something that could be included in Phobos? If yes, where?

-- 
/Jacob Carlborg
« First   ‹ Prev
1 2