www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - strict

reply bearophile <bearophileHUGS lycos.com> writes:
Pure functions are nice because a smart compiler can sometimes perform some
optimizations with them that aren't possible with normal functions. But for me
their main advantage is that they help avoid some bugs, because they can't
modify global state nor change their behaviour on its base (we can ignore
memory overflow, and maybe even exceptions). My Python/Delphi programs that are
mostly composed of nearly-pure functions are often the ones with the lower bug
count.

I have seen that's true in OOP too: methods that are static (and don't use
static attributes, they are pure methods, essentially) are often the less buggy
ones. So I use static methods where I can, every time I can write a method that
uses just the input arguments.

But programming in D with just pure functions is not always handy, and in OOP I
sometimes must alter instance attributes, so they can't be pure. And sometimes
using a variable that's not an argument of the function produces a little
faster code/function.

So in D I can think of something intermediate between a pure function and a
normal function/method, something that keeps code flexible but with a lower bug
count. For this I can think about an attribute  strict that can be used on a
free function, a nested function, an object method, and even a class (it means
all it methods are  strict). Even  strict modules are possible (it means all
callables inside it are  strict). (If I will ever design a new imperative
language this will be the default behaviour. Using outer names randomly as in

"nonlocal" statements).

 strict denotes a normal function/method, where all the names (variable) it
uses inside are written down in an explicit way. Some of such names are normal
function arguments, the other names can be global variables, variables from an
outer function, instance attributes, static class attributes, enums.

// inside here types are optional
 strict(in int y, in z, out k, inout w) int foo(int x) {
  // ... code
}

This function/method "foo" takes int "x" as argument. Foo can't use
outer/instance/static names beside "y","z","w","k" ("y" and "z" can't be
written inside foo, "k" can't be read inside foo, and "w" is read/write by foo).

Some alternative syntaxes, a better syntax can be invented:

 strict_in(int y, z)
 strict_out(k)
 strict_inout(w)
int foo(int x) {
  // ... code
}


 strict int foo(int x) {
   in int y;
   in z; // types are optional
   out k;
   inout w;
  // ... code
}


 strict [in int y, in z, out k, inout w]
int foo(int x) {
  // ... code
}


 in int y;
 in z;
 out k;
 inout w;
 strict int foo(int x) {
  // ... code
}


 strict int foo(int x) {
   strict_in int y;
   strictl_in z;
   strict_out k;
   strict_inout w;
  // ... code
}


 strict int foo(int x) {
   nonlocal_in int y
   nonlocal_in z
   nonlocal_out k
   nonlocal_inout w
  // ... code
}


Why it's useful: system theory says that improving the separation of subsystems
you reduce unwanted side effects, improving the reliability of the whole system.

That's why D modules too must produce a better isolation: as in Python "import
bar;" has to import only the "bar" name in the namespace, so for example there
are no new global variables and no surprises. This is a small change to the D
module system that will improve D code. A simple syntax like "import foo: *;"
can be used for the current (to be discouraged) behaviour.

Bye,
bearophile
Feb 14 2010
next sibling parent BCS <none anon.com> writes:
Hello bearophile,

  strict denotes a normal function/method, where all the names
 (variable) it uses inside are written down in an explicit way. Some of
 such names are normal function arguments, the other names can be
 global variables, variables from an outer function, instance
 attributes, static class attributes, enums.
 
 // inside here types are optional
  strict(in int y, in z, out k, inout w) int foo(int x) {
 // ... code
 }
I Like the idea but I think that is a bit /to/ explicit. It makes me think of the declared exceptions thing in Java. I don't remember exactly what the problem is there (I've done next to no Java) but being overly explicit seems to me to be an invitation for problems. Might the following get most of the advantages? strict acts like pure but allows the function to modify it's arguments. that plus some relaxed rules about what can be used for default parameters would get >90% of what I see you asking for: int a; void Outer() { int b; strict int Inner(int i, ref j = b, out k = a) { k = j; return i + j; } int m = Inner(); } -- <IXOYE><
Feb 14 2010
prev sibling next sibling parent bearophile <bearophileHUGS lycos.com> writes:
retard:
 This kind of code is just unreadable. It's so bad it might be a joke as 
 well..
A better syntax can be invented, do you like this one? outer(in int y, in z, out k, inout w) int foo(int x) { // ... code } Bye, bearophile
Feb 15 2010
prev sibling parent bearophile <bearophileHUGS lycos.com> writes:
BCS:
 I Like the idea but I think that is a bit /to/ explicit. It makes me think 
 of the declared exceptions thing in Java. I don't remember exactly what the 
 problem is there (I've done next to no Java) but being overly explicit seems 
 to me to be an invitation for problems.
As I have designed it now it's something optional, you don't have to use outer() if you don't want to use it. And even if you use it, you can use it in just one function in isolation (if you apply outer to a class/module all its methods/callables will need the annotations). So I think it's very different from Java checked exceptions, that are a little viral. Another nice thing of this idea is that you don't need to modify or remove things from D2 to implement it, it seems a purely additive change. Bye, bearophile
Feb 15 2010