December 18, 2018
On Tuesday, December 18, 2018 6:35:34 AM MST Pjotr Prins via Digitalmars-d- announce wrote:
> On Tuesday, 18 December 2018 at 11:25:17 UTC, Jonathan M Davis
>
> wrote:
> > Of course, even if we _did_ have a solution for reversing attributes, slapping an attribute on the top of the module would still potentially be a maintenance problem, because it's then really easy to miss that an attribute is in effect (it's a problem that we've had on several occasions with druntime and Phobos in the few cases where attributes are mass-applied). So, there is no silver bullet here (though regardless of whether mass-applying attributes is something that should ever be considered good practice, we really should add a way to be able to reverse them).
>
> Thanks Jonathan for your elaborate explanation. I personally have no problem with the attributes which - in practice - means I don't use them much unless I want to make sure something is nogc, for example. For library designers it makes sense to be explicit. I guess that is where the trade-off kicks in. Maybe it is just a feature. We argue against specifying them because other languages are not as explicit. It does add a little noise.

In practice, library developers are forced to worry about it more, because it affects everyone using their code, whereas within a program, how valuable it is to worry about them depends on the size of the program and what you expect to get out of them. Very large programs can definitely benefit (especially with @safe and pure), because it reduces how much code you have to worry about when tracking down the problems that those attributes address, but with small programs, the benefit is far more debatable. And for simple scripts and the like, they're almost certainly a waste of time - which is part of why more restrictive attributes are not the default. It's supposed to be easy to write code that doesn't deal with attributes if you don't want to, but they're there for those who do care. The problem of course is that when you do care, they tend to become a bit of a pain.

I confess that I do tend to think about things from the standpoint of a library designer though, in part because I work on stuff like Phobos, but also because I tend to break up my programs into libraries as much as reasonably possible. In general, the more that's in a reusable, easily testable library the better. And with that approach, a lot less of the code for your programs is actually in the program itself, and the attributes tend to matter that much more.

- Jonathan M Davis



December 18, 2018
On Tue, Dec 18, 2018 at 06:53:02PM -0700, Jonathan M Davis via Digitalmars-d-announce wrote: [...]
> I confess that I do tend to think about things from the standpoint of a library designer though, in part because I work on stuff like Phobos, but also because I tend to break up my programs into libraries as much as reasonably possible. In general, the more that's in a reusable, easily testable library the better. And with that approach, a lot less of the code for your programs is actually in the program itself, and the attributes tend to matter that much more.
[...]

My recent programming style has also become very library-like, often with standalone library-style pieces of code budding off a messier, experimental code in main() (and ultimately, if the project is long-lasting, main() itself becomes stripped down to the bare essentials, just a bunch of library components put together).  But I've not felt a strong urge to deal with attributes in any detailed way; mostly I just templatize everything and let the compiler do attribute inference on my behalf. For the few cases where explicit attributes matter, I still only use the bare minimum I can get away with, and mostly just enforce template attributes using the unittest idiom rather than bother with writing explicit attributes everywhere in the actual code.


T

-- 
He who sacrifices functionality for ease of use, loses both and deserves neither. -- Slashdotter
December 18, 2018
On Tuesday, December 18, 2018 7:02:43 PM MST H. S. Teoh via Digitalmars-d- announce wrote:
> On Tue, Dec 18, 2018 at 06:53:02PM -0700, Jonathan M Davis via Digitalmars-d-announce wrote: [...]
>
> > I confess that I do tend to think about things from the standpoint of a library designer though, in part because I work on stuff like Phobos, but also because I tend to break up my programs into libraries as much as reasonably possible. In general, the more that's in a reusable, easily testable library the better. And with that approach, a lot less of the code for your programs is actually in the program itself, and the attributes tend to matter that much more.
>
> [...]
>
> My recent programming style has also become very library-like, often with standalone library-style pieces of code budding off a messier, experimental code in main() (and ultimately, if the project is long-lasting, main() itself becomes stripped down to the bare essentials, just a bunch of library components put together).  But I've not felt a strong urge to deal with attributes in any detailed way; mostly I just templatize everything and let the compiler do attribute inference on my behalf. For the few cases where explicit attributes matter, I still only use the bare minimum I can get away with, and mostly just enforce template attributes using the unittest idiom rather than bother with writing explicit attributes everywhere in the actual code.

That works when you're in control of everything and not making libraries public, but it tends to be worse when you're making stuff publicly available, because then there's a much higher risk of accidentally screwing up someone else's code by breaking an attribute. It's also much worse from a documentation standpoint, because users of the library can't look at the documentation to know which attributes are in effect. That's pretty much unavoidable with heavily templated code, but there are generally going to be fewer maintainence problems with a publicly available library if attributes are explicit. And the more heavily used the library is, the more likely it is that there are going to be problems.

It's like how if you want your library to be stable with regards to CTFE, you pretty much have to test that CTFE works in your unit tests. It's easy to forget, and ideally, you wouldn't have to worry about it, but if someone else uses your library and uses one of its functions with CTFE, and you then make a change that doesn't work with CTFE and don't realize it, you'll break their code. Ideally, you wouldn't have to worry about ensuring that stuff works with CTFE (after all, D specifically doesn't require that stuff be marked to work with it), but with regards to publicly available libraries, if it's not tested for, it can become a problem. So, arguably, it's best practice to test that stuff works with CTFE (at least in publicly available libraries), but I doubt that all that many libraries actually do it. And attributes are in the same boat in many respects (especially if attribute inference is used heavily).

But again, if you're not making stuff publicly available, it's usually easy to just not worry about attributes (or CTFE-ability) except in those cases where you have to or decide that you need to in order to ensure that a particular attribute and its benefits are in effect. It's when you make stuff publicly available that it becomes more of an issue - especially if the library is heavily used.

- Jonathan M Davis



December 18, 2018
On Tuesday, December 18, 2018 8:00:48 AM MST Steven Schveighoffer via Digitalmars-d-announce wrote:
> On 12/17/18 4:42 AM, Dukc wrote:
> > On Monday, 17 December 2018 at 09:41:01 UTC, Dukc wrote:
> >> On Saturday, 15 December 2018 at 19:53:06 UTC, Atila Neves wrote:
> >>> @safe and pure though...
> >>
> >> Why @safe? Can't you just write "@safe:" on top and switch to @system/@trusted as needed?
> >
> > Argh, I forgot that you are not supposed to @safe templates away.
>
> You can apply @safe to anything. It's @trusted you have to be careful with.
>
> Of course, it probably won't work for many templates.

@safe should only be used on a template if the @safety of the code does not depend on the template arguments, and it frequently does depend on them. Mass-applying attributes should rarely be done with templated code. It already causes enough problems with non-templated code, because it's easy to not realize that an attribute has been mass-applied, but without a way to explicitly mark a templated function so that an attribute is inferred in spite of it being mass-applied, mass-applying attributes with templated code will usually result in attributes being wrongly applied.

Now, for any attribute other than @trusted, the worst that you're going to get out of incorrectly applying an attribute to a template is a compilation error when a particular instantiation doesn't work with that attribute, whereas for @trusted it's a disaster in the making. Mass-applying @trusted is almost always a terrible idea. The one exception would maybe be something like the bindings in druntime, where a number of modules do it, because it's just a bunch of C prototypes. But even then, there's a high risk of marking a function as @trusted later when someone adds it and doesn't realize that @trusted was applied.

- Jonathan M Davis



December 18, 2018
On Tuesday, December 18, 2018 1:17:28 AM MST Russel Winder via Digitalmars- d-announce wrote:
> On Mon, 2018-12-17 at 12:16 -0800, Walter Bright via
> Digitalmars-d-announce
> wrote:
> > […]
> >
> > Going pure, however, is much harder (at least for me) because I'm not
> > used to
> > programming that way. Making a function pure often requires
> > reorganization of
> > how a task is broken up into data structures and functions.
> >
> > […]
>
> I can recommend a short period of working only with Haskell. And then a short period working only with Prolog. Experience with Java and Python people trying to get them to internalise the more declarative approach to software, shows that leaving their programming languages of choice behind for a while is important in improving their use of their languages of choice.

+1

The only reason that I'm as good at writing functional-style code as I am is because I used Haskell as my goto language for a couple of years towards the end of college. By being forced to program functionally, I got _much_ better at things like recursion, and it significantly improved aspects of my programming in languages like C++ or D.

That being said, I think that anyone who programs in such a language longterm by choice has got to be a masochist. Functional programming is a fantastic tool to have in your toolbox, but man does it force you to contort things to make it work in many cases. I've never spent as much time per line of code with any other language as I did with haskell. It's great for stretching your brain but terrible for being productive. I'm sure that folks who always program that way get much better at it, but some problems simply work better with other programming paradigms, and it's fantastic to use a language like D that allows you to program in a variety of paradigms rather than forcing a particular paradigm on you all the time. But without spending a lot of time in a language that forces a particular paradigm, you're likely to be much worse at that particular paradigm in a flexible language such as D.

One side effect of having spent as much time as I did with haskell is that I've rarely found the functional nature of D's templates to be much of a problem. Sometimes, it can be, and the addition of static foreach is certainly welcome, but I rarely missed it, because I rarely needed it. The declaritive stuff just tends to fit nicely into the functional paradigm in ways that normal functions often don't. LOL. And it took quite a while before I realized that templates were really a functional language (in fact, I think that I only realized it when I read Bartosz's article on the subject). I just understood how to use them and didn't think about it, though once I did realize it, I felt stupid for not having realized it.

- Jonathan M Davis




December 19, 2018
On Wed, 2018-12-19 at 01:45 +0000, Neia Neutuladh via Digitalmars-d-announce wrote:
> On Wed, 19 Dec 2018 01:04:24 +0000, Nathan S. wrote:
> > On Saturday, 15 December 2018 at 19:53:06 UTC, Atila Neves wrote:
> > > Not the case in Rust, not the case in how I write D. TBH it's not such a big deal because something has to be typed, I just default to const now anyway instead of auto. @safe and pure though...
> > 
> > I'd be interested in seeing some of that Rust code. My impression from Clojure is that an all-immutable style requires leaning heavily on the garbage collector and as far as I know Rust has none.

Rust is garbage collector free. This is why it is appealing to the C (and C++) people who think garbage collectors are anathema.

Rust has let and let mut to introduce new bindings and variables respectively, you have to type less for immutable, which his the right way round. If you want mutability, you have to say so explicitly. Single assignment rocks. :-)

> It is greatly simplified by automatic memory management. Rust doesn't have a GC, but it has a complex ownership system instead, and that's the basis of its memory management. When that's insufficient, you use reference counting.

With Rust (as with most languages) you have to separate stack and heap
allocation. The Rust borrow checker carefully tracks all stack usage and
(caveat unsafe activity) will not compile code that has any memory problems
that the borrow checker can find. For the heap, Rust provides reference
counted pointers which work a lot better than the equivalents in C++:
std::shared_ptr and std::unique_ptr can be a right pain. Also of course,
unlike C++, Rust has very good support for multi-threading in the language and
the standard library, mostly built around std::Mutex. Rust integrates all this
with std::Option, std::Error, if let, and match in a way that makes dealing
with locks and RAII quite simple. C++ may have brought RAII to mainstream
programming, but it's support for multiple threads is still very weak comparedto D, Rust, Go, etc.

[…]

--
Russel.
===========================================
Dr Russel Winder      t: +44 20 7585 2200
41 Buckmaster Road    m: +44 7770 465 077
London SW11 1EN, UK   w: www.russel.org.uk



December 19, 2018
On Wednesday, 12 December 2018 at 07:44:12 UTC, Walter Bright wrote:
> On 12/11/2018 4:51 AM, Nicholas Wilson wrote:
>> > Returning a reference
>> Wow, thats f*ck'n stupid! https://run.dlang.io/is/SAplYw
>
> It's quite deliberate.
>
> ref in C++ is a type constructor, but it's so special-cased to behave like a storage class, it might as well be one. In D it is.
>
> (For example, ref in C++ can only appear at the top level. There are no "pointers to refs".)
>
> refs exist so the lifetime of them can be controlled for memory safety. Treating them as flexibly as pointers would make that pretty difficult. refs are the primary way a memory safe container can expose pointers to its contents.

Could you please elaborate a little bit more on this?  In the linked program, I had expected that "ref" would return a reference to "a" that would behave similar to a pointer. But when that reference is assigned to "b", and "b" is modified, "a" appears to retain its original value, implying that "b" is a copy.

When was the copy of "a" made?  Was it during the assignment to "b"?

I use ref regularly, especially when I have to port C++ code that does exactly that, exposing modifiable references to its members. And in my experience it works quite well, especially for array types and classes.

So what is the best way to understand this program and know why a copy of "a" is made?
December 19, 2018
On Wed, 19 Dec 2018 17:28:01 +0000, Vijay Nayar wrote:
> Could you please elaborate a little bit more on this?  In the linked program, I had expected that "ref" would return a reference to "a" that would behave similar to a pointer.

They work like pointers that automatically dereference when assigning to the base type.

Only three things in D can be ref:
* A function parameter
* A function return value
* A foreach variable (since that's either going to be a function return
value, a function parameter, or a pointer, depending on what you're
iterating over)

So when the compiler sees something like:

    ref int foo();
    auto a = foo();

It sees that the type of 'a' has to be the same as the return type of 'foo'. Except that's not possible, so it uses the nearest equivalent type: int.

And if you have:

    ref int foo();
    int a = foo();

That obviously converts by copying the value.
December 19, 2018
On Wednesday, 19 December 2018 at 19:58:53 UTC, Neia Neutuladh wrote:
> On Wed, 19 Dec 2018 17:28:01 +0000, Vijay Nayar wrote:
>> Could you please elaborate a little bit more on this?  In the linked program, I had expected that "ref" would return a reference to "a" that would behave similar to a pointer.
>
> They work like pointers that automatically dereference when assigning to the base type.
>
> Only three things in D can be ref:
> * A function parameter
> * A function return value
> * A foreach variable (since that's either going to be a function return
> value, a function parameter, or a pointer, depending on what you're
> iterating over)
>
> So when the compiler sees something like:
>
>     ref int foo();
>     auto a = foo();
>
> It sees that the type of 'a' has to be the same as the return type of 'foo'. Except that's not possible, so it uses the nearest equivalent type: int.
>
> And if you have:
>
>     ref int foo();
>     int a = foo();
>
> That obviously converts by copying the value.

To be fair even in c++ this won't be a reference.

int& foo();
auto a = foo(); // a == int
auto& a = foo(); // a == int&

So it shouldn't be that surprising.
December 19, 2018
On 12/13/2018 10:29 AM, Adam D. Ruppe wrote:
> The attribute spam is almost longer than the function itself.
Attributes only start to matter when creating code that will be around for a long time (such as reusable libraries). It's a waste of effort for short term code.