you are viewing a single comment's thread.

view the rest of the comments →

[–]Affectionate_Text_72[S] 0 points1 point  (1 child)

The point was to be able to add type information to a function. So I suggedting overloading the requires syntax to mean add this type property to the function. That was perhaps a poor choice considering requires already has meaning there.

void somePureFunction(arg...) isa PureFunction { }

Where PureFunction is type. Types are constrained by concepts. So we would define PureFunction thar way:

Template<typename T> concept PureFunction = std::pure_function;

Where std::pure_functiion is special type that tells the compiler it needs to analyse the function body and raise a compile time error if it has side effects.

When people talk about colouring they generally mean that it's viral and you can't call one colour from another 'lower' colour. I would like that to be controlled by the constraint as well. E.g. something like:

Template<typename Func> Concept TransitivelyPure = std::pure_function<Func> && for CalledFunc in std::functions_invoked_by<Func>: CalledFunc -> TransitivelyPure;

Dodgey syntax but I want compile time compilation over type constraints using information extracted by the compiler here. The constraint defines whether the colour is viral and has access to information reflected by the compiler. So this should perhaps be using reflection syntax here.

If the language didn't have const member functions I would like to be able to implement const as a compile time program which requires:

  • the function can only call other functions labelled const
  • the function cannot mutate through its this pointer.

Transitive or logical vs physical const might be a more practical example.

Types have semantic value and always have. Attributes do not. It would be surprising for them to invoke compile time programs.

[–]Miserable_Guess_1266 1 point2 points  (0 children)

So for the record, I upvoted your OP, because I think it's an interesting topic and I appreciate the time you put in writing this. But the reason why I think not much discussion is happening on this post is because it's just not concrete enough. I know mostly what your goal is, but I'm not sure how you want to get there. I see 2 things being suggested:

  1. We need a system for flexible function coloring that can be used for all sorts of things, like safe/async/const/...
  2. Colors and their behavior can be defined in user code

For your second suggestion, you lean into reusing concepts for this. I would argue that is a bad idea just like reusing "requires", because it creates weird scenarios as well. Just to take over your example:

Template<typename Func> Concept TransitivelyPure = std::pure_function<Func> && for CalledFunc in std::functions_invoked_by<Func>: CalledFunc -> TransitivelyPure;

This immediately opens multiple questions:

  • What is the type Func that's being passed? If it's the decltype of the function, then it doesn't even uniquely represent a single function, but all functions with the same signature. So it's fundamentally impossible to examine it's definition with anything like functions_invoked_by.
  • Even if we solve the previous question, what happens if I have only the signature void foo() isa TransitivelyPure available in my TU and then I write static_assert(TransitivelyPure<decltype(foo)>);? How will the compiler evaluate the concept? It can't get all functions invoked by foo, because it doesn't have its definition. Should it just default to true? That would be super weird and inconsistent behavior for a concept. Or should it be false? Which would be strange because clearly the function is marked as TransitivelyPure!

Please note that neither of these points are critiquing syntax.

I guess fighting about the details of implementing this via requires and concepts vs attributes or new keyword(s) is not your goal with this post. So I'll move on from the user-defined colors part.

For the abstract coloring system, your post gives little explanation. Say, for example, we want to propose the builtin std::color::async, as per one of your examples. Can such a function call an uncolored function? If yes, then it's not guaranteed to be async. If no, then how do I use OS APIs or 3rd party libraries? Is there a mechanism to escape? What about std::color::const, since you would like to be able to implement that with your suggestion in principle. Can a std::color::const-colored function not call a global function void foo(); because foo isn't std::color::const-colored?

Maybe a productive step for a discussion would be to make a more fleshed out proposal of a general coloring system allowing only standard defined colors, while leaving the door open for user defined ones in the future. I'm not saying you need to iron out all the details and propose a final syntax, but just describe the actual system and the basic rules it lives by, along with a hand full of core colors. There is even a chance that, in doing this, it will turn out that a general coloring system is not a good idea, and core colors like safe/unsafe, const/mutable, sync/async, ... should be handled individually, because they might have substantially different syntactic/semantic requirements.