Jump to page: 1 2
Thread overview
Optional tags and attributes
Jan 18, 2014
Stanislav Blinov
Jan 18, 2014
Adam D. Ruppe
Jan 18, 2014
Stanislav Blinov
Jan 18, 2014
Rikki Cattermole
Jan 18, 2014
Stanislav Blinov
Jan 18, 2014
Rikki Cattermole
Jan 18, 2014
Stanislav Blinov
Jan 18, 2014
H. S. Teoh
Jan 18, 2014
Stanislav Blinov
Jan 18, 2014
H. S. Teoh
Jan 18, 2014
Stanislav Blinov
Jan 18, 2014
Stanislav Blinov
Jan 18, 2014
Jacob Carlborg
Jan 18, 2014
Timon Gehr
January 18, 2014
Hopefully, now I'm posting to the right place (instead of .announce like last time >_<).

Is there a way to make function tags and attributes optional? For example, C++ has noexcept tag that takes an optional bool that actually enables or desables the whole specifier. I find its uses in C++ somewhat painful due to clumsy template syntax, but the value is there.

I found a discussion from 2010 about doing something similar in D, but it doesn't seem to have went far, and I don't know if anything has been done in this direction.



In essence, here's what I mean:

import std.traits;

template isNoThrow(alias func) {
  enum bool isNoThrow = (functionAttributes!func & nothrow_) != 0;
}

void thisIsSoPolymorphic(T)(T t) nothrow(isNoThrow!(T.unsure)) {
  // ...
  t.unsure();
  // ...
}

Without this behavior, I basically have to either drop nothrow entirely (bummer), or make two functions enclosed in static if checks, which obviously blows up if I want to make more tags optional.
January 18, 2014
Templates automatically infer their attributes based on the input, so you don't have to specify them there.

If you do a foo!(nothrow_function)(), foo is also nothrow (unless, of course, it throws!)
January 18, 2014
On Saturday, 18 January 2014 at 00:23:14 UTC, Adam D. Ruppe wrote:
> Templates automatically infer their attributes based on the input, so you don't have to specify them there.
>
> If you do a foo!(nothrow_function)(), foo is also nothrow (unless, of course, it throws!)

This doesn't apply here. I'm not using function as a template paramter.

That's the whole case here. Suppose I'm writing a template class that's parametrized by some type, and in my method I want to call methods of that type. I cannot make any of my functions nothrow until I am sure the methods I call will not throw, and the compiler will tell me. And how can I be sure they don't throw if it's an arbitrary type? I can check it, sure, with that isNoThrow() template. But how then to use that information in my own function declaration?
January 18, 2014
On Saturday, 18 January 2014 at 00:32:53 UTC, Stanislav Blinov wrote:
> On Saturday, 18 January 2014 at 00:23:14 UTC, Adam D. Ruppe wrote:
>> Templates automatically infer their attributes based on the input, so you don't have to specify them there.
>>
>> If you do a foo!(nothrow_function)(), foo is also nothrow (unless, of course, it throws!)
>
> This doesn't apply here. I'm not using function as a template paramter.
>
> That's the whole case here. Suppose I'm writing a template class that's parametrized by some type, and in my method I want to call methods of that type. I cannot make any of my functions nothrow until I am sure the methods I call will not throw, and the compiler will tell me. And how can I be sure they don't throw if it's an arbitrary type? I can check it, sure, with that isNoThrow() template. But how then to use that information in my own function declaration?

Perhaps using template if statements and pure functions. A little like how I implemented some of Dvorm's[0] utility functions. That way it can execute at compile time. You can pass the type to it and check any method you would call if its nothrow or not via traits.
That way you can have two declarations but with one being opposite of the if.

[0] https://github.com/rikkimax/Dvorm/blob/master/src/orm/util.d#L31
January 18, 2014
On Saturday, 18 January 2014 at 00:42:35 UTC, Rikki Cattermole wrote:
> Perhaps using template if statements and pure functions. A little like how I implemented some of Dvorm's[0] utility functions. That way it can execute at compile time. You can pass the type to it and check any method you would call if its nothrow or not via traits.

Obviously I didn't explain myself clearly. I know how to determine if a function is nothrow or pure or @safe or anything else thanks to D's awesomeness :) But what I want is a way to *use* that knowledge when declaring my own functions. Or rather, tell the compiler "Wait, I really want this to be nothrow, but I don't know if that function will throw. Here's a check for you, please make me nothrow if it passes". After all, tags are not just for enforcing correctness at compile time, they can be used (once verified) for optimization too. So it'd be nice to find a way to provide all the nice info to the compiler whenever possible. It's not just about nothrow, but also pure, @safe/@system/@trusted, hell, even public/protected/private for that matter. :)

> That way you can have two declarations but with one being opposite of the if.

...Or four in case I'd also want pure/not pure, or nine if I'd also want @safe/not @safe...
January 18, 2014
On Saturday, 18 January 2014 at 00:57:27 UTC, Stanislav Blinov wrote:
> Obviously I didn't explain myself clearly. I know how to determine if a function is nothrow or pure or @safe or anything else thanks to D's awesomeness :) But what I want is a way to *use* that knowledge when declaring my own functions. Or rather, tell the compiler "Wait, I really want this to be nothrow, but I don't know if that function will throw. Here's a check for you, please make me nothrow if it passes". After all, tags are not just for enforcing correctness at compile time, they can be used (once verified) for optimization too. So it'd be nice to find a way to provide all the nice info to the compiler whenever possible. It's not just about nothrow, but also pure, @safe/@system/@trusted, hell, even public/protected/private for that matter. :)
>
>> That way you can have two declarations but with one being opposite of the if.
>
> ...Or four in case I'd also want pure/not pure, or nine if I'd also want @safe/not @safe...

Okay, I'll explain what I was inferring. If the methods your calling are lets say nothrow which can be checked by a pure function lets say. Using the template if statement we can check that it does throw. For example:

void myfunc(T)(T arg) nothrow if (checkIfNothrow!T) {}
void myfunc(T)(T arg) if (checkIfNoModifiers!T) {}
void myfunc(T)(T arg) pure if (checkIfPure!T) {}

Basically you have to define all combinations. That is what I was meaning.
Preferably you'll use q{} your code that is actually used. And use a template mixin to generate all these statements, so you don't have to!
January 18, 2014
On Saturday, 18 January 2014 at 01:52:56 UTC, Rikki Cattermole wrote:

> Okay, I'll explain what I was inferring. If the methods your calling are lets say nothrow which can be checked by a pure function lets say. Using the template if statement we can check that it does throw.

I'm going to continue to think that I am overly not explicit and thus there continues to be a misunderstanding. However, I'd like to correct that misunderstanding by saying that *I KNOW HOW TO DO THIS WITH TEMPLATES AND STATIC IF* :D (By the way, I did sort of mention it in the first post). In other words, I wasn't asking *how* to do it in principle, I'm interested if there's any possibility to do it elegantly.

> For example:
> void myfunc(T)(T arg) nothrow if (checkIfNothrow!T) {}
> void myfunc(T)(T arg) if (checkIfNoModifiers!T) {}
> void myfunc(T)(T arg) pure if (checkIfPure!T) {}
>
> Basically you have to define all combinations. That is what I was meaning.
> Preferably you'll use q{} your code that is actually used. And use a template mixin to generate all these statements, so you don't have to!

That is precisely the thing I'd like to avoid entirely. Following your example (which is not entirely correct since in this case I won't be checking T, I'll be checking T.foo or T.bar or whatever combination of those :o), I'd have to have three distinct function definitions, even though in the end only one of them will ever be compiled into my class. Yes, this can be simplified by mixing-in the actual function body into all of those definitions. But we have to be able to do better :)

I'm not trying to say that C++'s syntax wins, no. The syntax can be anything, other than blowing up my class with all those declarations and ifs :). If we're talking templates, it would sure be nice to be just a tad more generic.

Consider:

void myfunc(T)(T arg) pure(isPure!(T.foo)) nothrow(isNoThrow!(T.bar)) { ... }

I know that myfunc *can* be pure (I wrote it) and it *won't* throw anywhere, and that would be true for all the function body, *except* I also know that T.foo *may* be impure or T.bar *may* throw (because I didn't write them, T is an arbitrary type possibly from user code). Because of those "may"s I either have to drop the tags entirely, or to have four different declarations with various combinations of constraints. Having the ability to specify the condition for a tag would elegantly solve this issue. It's one definition, no static ifs or additional template constraints. Again, the syntax here could be anything, like something proposed in that discussion I mentioned earlier:

void myfunc(T)(T arg) @optional(isPure!(T.foo), pure) @optional(isNoThrow!(T.bar), nothrow) { ... }

or anything else. Granted, presence of overloads and templates may make those ifPure/ifNoThrow/ifWhatever checks not that trivial to implement or invoke, but still possible nevertheless.
January 18, 2014
On Sat, Jan 18, 2014 at 01:52:55AM +0000, Rikki Cattermole wrote:
> On Saturday, 18 January 2014 at 00:57:27 UTC, Stanislav Blinov wrote:
> >Obviously I didn't explain myself clearly. I know how to determine if a function is nothrow or pure or @safe or anything else thanks to D's awesomeness :) But what I want is a way to *use* that knowledge when declaring my own functions. Or rather, tell the compiler "Wait, I really want this to be nothrow, but I don't know if that function will throw. Here's a check for you, please make me nothrow if it passes". After all, tags are not just for enforcing correctness at compile time, they can be used (once verified) for optimization too. So it'd be nice to find a way to provide all the nice info to the compiler whenever possible. It's not just about nothrow, but also pure, @safe/@system/@trusted, hell, even public/protected/private for that matter. :)
> >
> >>That way you can have two declarations but with one being opposite of the if.
> >
> >...Or four in case I'd also want pure/not pure, or nine if I'd also want @safe/not @safe...
> 
> Okay, I'll explain what I was inferring. If the methods your calling are lets say nothrow which can be checked by a pure function lets say. Using the template if statement we can check that it does throw. For example:
> 
> void myfunc(T)(T arg) nothrow if (checkIfNothrow!T) {}
> void myfunc(T)(T arg) if (checkIfNoModifiers!T) {}
> void myfunc(T)(T arg) pure if (checkIfPure!T) {}
> 
> Basically you have to define all combinations. That is what I was
> meaning.
> Preferably you'll use q{} your code that is actually used. And use a
> template mixin to generate all these statements, so you don't have
> to!

What's wrong with just letting the compiler infer the maximal function attributes?

	void fun(T)(T arg)
		if (is(typeof(arg.method())))
	{
		arg.method();
	}

	int globvar;
	struct Impure {
		void method() @safe nothrow { globvar++; }
	}

	struct Thrower {
		void method() pure @safe { throw new Exception("abc"); }
	}

	struct Unsafe {
		void method() pure nothrow {
			int[2] arr;
			int* ptr = arr.ptr;
			ptr++;
			*ptr = 123;
		}
	}

	pragma(msg, typeof(fun!Impure));
	pragma(msg, typeof(fun!Thrower));
	pragma(msg, typeof(fun!Unsafe));

Compiler output:

	nothrow @safe void(Impure arg)
	pure @safe void(Thrower arg)
	pure nothrow @system void(Unsafe arg)

Notice how the best attributes have been inferred for fun(), depending
on what type was passed in, even though we never annotated fun() with
any attributes. That is, fun()'s attributes change depending on how
T.method() was implemented. So if T.method happens to be pure, safe, and
nothrow, then so will fun(). If fun() calls multiple methods, then its
final attributes will be the intersection of the attributes of all the
method calls plus its own code (so if you threw in fun(), then it would
become nothrow regardless of whether T.method is nothrow or not, but if
fun() itself never throws, then it will be marked nothrow depending on
whether T.method throws).

Sadly, this only works for template functions currently -- so I still had to annotate the various method()'s by hand, but if you were to turn them into template functions too, their attributes will also be inferred automatically.


T

-- 
Almost all proofs have bugs, but almost all theorems are true. -- Paul Pedersen
January 18, 2014
On Saturday, 18 January 2014 at 02:38:28 UTC, H. S. Teoh wrote:

> What's wrong with just letting the compiler infer the maximal function
> attributes?

...

> Sadly, this only works for template functions currently -- so I still
> had to annotate the various method()'s by hand, but if you were to turn
> them into template functions too, their attributes will also be inferred
> automatically.

Oh, I see now where it went the wrong way. From my own example, of course :)

Surely I was meaning:

class Careful(T) {
//...
  void thisIsSoPolymorphic() nothrow(isNoThrow!(T.foo)) { ... }
//...
}

Something to that extent. So that yes, the function itself is not a template.
January 18, 2014
On Fri, 17 Jan 2014 21:50:07 -0500, Stanislav Blinov <stanislav.blinov@gmail.com> wrote:

> On Saturday, 18 January 2014 at 02:38:28 UTC, H. S. Teoh wrote:
>
>> What's wrong with just letting the compiler infer the maximal function
>> attributes?
>
> ...
>
>> Sadly, this only works for template functions currently -- so I still
>> had to annotate the various method()'s by hand, but if you were to turn
>> them into template functions too, their attributes will also be inferred
>> automatically.
>
> Oh, I see now where it went the wrong way. From my own example, of course :)
>
> Surely I was meaning:
>
> class Careful(T) {
> //...
>    void thisIsSoPolymorphic() nothrow(isNoThrow!(T.foo)) { ... }
> //...
> }
>
> Something to that extent. So that yes, the function itself is not a template.

I was about to respond with a similar point, but it seems you are understanding now how the nothrow inference works :)

I can't think of a correct way to do this without repeating code, since it has to be polymorphic (and therefore not a template), nothrow inference only works on templates.

The idea to be able to attach attributes/annotations based on compile-time introspection would be a worthy addition to the language IMO, but I really don't like the syntax you have outlined (I see you don't like it either). Especially if you have to do all the attributes this way.

I think a "use the attributes of X" would be a general enough tool.

Something like:

 void thisIsSoPolymorphic() attrOf(T.foo)

-Steve
« First   ‹ Prev
1 2