The Forum for Discussion about The Third Manifesto and Related Matters

Please or Register to create posts and topics.

Possreps, Objects, Mutability, Immutability, and Developing Non-database Applications.

PreviousPage 2 of 7Next
Quote from AntC on June 19, 2019, 11:12 pm
Quote from johnwcowan on June 19, 2019, 12:12 pm
Quote from Dave Voorhis on June 19, 2019, 9:55 am

I'd use SAME_TYPE_AS(Grid) and SAME_TYPE_AS(Board) -- and/or SAME_HEADING_AS(...) -- where I need to refer to the type (or heading) of Grid or Board.

Or, as you noted on the original thread, add type aliases to the language.

I don't think it was me that noted it,

That was me https://forum.thethirdmanifesto.com/forum/topic/what-is-the-purpose-of-relations-containing-tuples-relations/?part=7#postid-984859

I said 'type synonyms', but 'aliases' is better.

but no matter. I was thinking in terms of Tutorial D, which doesn't have type aliases -- which makes sense for pedagogic purposes -- but would certainly be a reasonable feature in any other D.

Does not having aliases help pedagogy? Are you thinking that writing out some long-winded type (or the almost as long-winded SAME_TYPE_AS) multiple times is like learning copper-plate handwriting? Practice makes perfect.

What I meant is that the lack of type aliases -- along with other limitations -- might be unduly limiting for an industrial language, but is harmless in a pedagogic language.

And not having aliases means one less relatively-irrelevant thing to learn when you're concentrating on the relevant content. A bit more verbosity isn't an issue when most of what you're writing is less than five or so lines.

I'm the forum administrator and lead developer of Rel. Email me at dave@armchair.mb.ca with the Subject 'TTM Forum'. Download Rel from https://reldb.org
Quote from dandl on June 20, 2019, 2:22 am

Same way you do it in object-oriented programs -- set a TileModelChanged global flag (housed in a relvar, probably) when you update the model. The view can query the flag.

The other responses seem perfectly reasonable, so this is the one I think to concentrate on.

  • There is a collection of tile models and a collection of tile views. In the OO case they are linked by a reference. Would you suggest that the view holds a key to find its model? Or the grid operates on both collections?

I suppose there's any number of different and equally valid ways of handling this, though it seems reasonable to me that the view holds the key to its model. Then you can trivially JOIN views to models.

Quote from dandl on June 20, 2019, 2:22 am
  • Do you propose that having only local and global variables is enough? The power of OO comes largely from encapsulated instance variables, long lifetime, restricted visibility. What replaces that?

TTM's equivalent is having encapsulated values. TTM makes what is behind the TYPE interface so hidden that it's considered to lie outside the model entirely.

TTM restricts encapsulation -- and inheritance and polymorphism -- to values, and permits updates only to variables. Note that values include tuples and relations, so rich composition is easy: You don't need to define a class to hold a bundle of values -- simply use a tuple. You don't need to define a container to hold a bundle of values -- simply use a relation, and if it's mutable, use a relvar.

Relvars are never hidden (except for local relvars inside operators). Things of long lifetime are meant to be visible. If you need to restrict access, you should -- per TTM -- use security mechanisms, not access modifiers. Modularity of code is defined by operators, not classes.

Aside: Note that encapsulation and information hiding and "bundling" of methods are orthogonal, but occur in conjunction so frequently that they're often (erroneously) treated as all being the same "encapsulation" thing. Systems supporting multiple dispatch aka multimethods such as Tutorial D do not define methods inside classes/types because they don't "belong" to a class/type -- that's a syntactic convention borne of the limitations of conventional object-orientation's single dispatch. Encapsulation only means being able to pass around bundles of data as singular things; that doesn't necessarily mean their internal structure must be hidden or that methods/operators must be bundled with them.

 

I'm the forum administrator and lead developer of Rel. Email me at dave@armchair.mb.ca with the Subject 'TTM Forum'. Download Rel from https://reldb.org
Quote from AntC on June 20, 2019, 6:33 am
Quote from dandl on June 20, 2019, 12:51 am
Quote from johnwcowan on June 19, 2019, 12:12 pm
Quote from Dave Voorhis on June 19, 2019, 9:55 am

My proposition is that relvars are the main holders of mutable state. Mutable state can also be represented by local scalar variables within operators and maybe global but non-persistent scalar variables.

And, not to be stroppy about it, when you have functions with mutable local variables[1], you have everything you need for objects.  Impure FP and OO are duals.  There's a poem about it, "Lords of the Lambda", written by the redoubtable Anon.:

To be clear, my intention was never to propose that OO could do what the TTM type system could not, just to argue the consequences of having no equivalent for "packets of state".

I freely admit to being largely ignorant of the innards of OOP, but I'm just not seeing any difficulties here.

The equivalent for "packets of state" is the whole database; or relvar values; or tuples-in-relvars; or attribute-values-in-tuples -- depending on how small your packet needs to be. You might temporarily load some packet from the database into a program-local var (possibly with some sort of calculation/aggregation), possibly writing it back to the database before that var goes out of scope/existence.

No, it really isn't. One thing I learned the hard way over the decades of building software is to control visibility. Wherever there is visibility, there is dependency. If you (anyone) can see my data  values or call my functions then my changes can affect your code. I was a slow convert to OO; I thought and still think that inheritance is basically a bad idea, but encapsulation is why OO works. It isn't the only way, but it does do the job.

So if you want to explain why a TTM language and type system can be used to build big complicated programs with lots of independently developed modules and libraries, you can't start by just putting all the "packets of state" in the database. It doesn't fly.

Tiny immutable classes are homologous with FP values and their types, but what replaces a mutable instance variable? I accept all the suggestions of how to answer my questions, but mostly they reinforce the view that you can't get there from here, you have to start from somewhere else.

I don't follow what you don't follow.

I don't follow what hasn't been said.

So now, in what sense are these duals?I couldn't find anything particularly helpful on the Web.

The ordinary local variable has restrictions on both its scope and its lifetime. How do you make local variables live longer than the function that created them? Are you talking closures here? Is that the missing link?

In a monolithic program, you'd return the value of local variables as a result from the function -- typically inside a data structure with other local stuff. I think we're long past the days of return values being limited to a machine word on the stack/register. In a database-centric application, you'd write that value out to the database as soon as you can (subject to combining it with sufficient other data to make it a coherent/valid transaction).

See above. My example is a computer game, worked on independently by a team of developers. It isn't monolithic; it uses a database for persistence, but it's not database-centric.

  • Do you propose that having only local and global variables is enough? The power of OO comes largely from encapsulated instance variables, long lifetime, restricted visibility. What replaces that?

I'd actually propose that the only global variable you need is the database; and that local variables be considered views into it or holding slots for to-be-transactions.

Why does the power of OO come from encapsulation? (It seems to me a great deal of the trouble in OO comes from encapsulation, i.e. private mutable state; and business rules built into private method instances). Put your state into the database. Put your business rules into database constraints. If you're worried about other processes/users seeing implementation details, or poking into what they don't understand, there's ways to hide that. (Make the data content of a distinct user-defined type; don't expose the means to construct or deconstruct its innards.)

Then I have no idea how what you're talking about applies to my program. What is a business rule? Why flatten everything out to be either local or global? Why expose first and the find ways to hide stuff? There is nothing about this that makes any kind of sense to me.

Andl - A New Database Language - andl.org
Quote from Dave Voorhis on June 20, 2019, 6:36 am

Java has supported closures since Java 1.1, but with some limitations (non-final variables in the outer scope aren't accessible) via an explicit mechanism called an anonymous inner class, which is essentially a cross between a conventional class and one or more lambda functions (-ish) with added verbosity.

Java has supported "true" lambdas with closures since Java 8, with fewer restrictions than earlier Java versions.

Tutorial D implies closures but doesn't specify them explicitly, but the grammar permits nested operators so in Rel inner operators close over their outer scope. Rel also defines higher order operators (see https://reldb.org/c/index.php/read/anonymous-and-first-class-operators/) so closures can be passed around.

My reading is that a 'closure' with access only to 'effectively final' variables is not a closure. In particular it cannot capture mutable state as per the example. C# certainly does. Here's a test program (and link):

// https://stackoverflow.com/questions/428617/what-are-closures-in-net

using System;

class Test {
  static void Main() {
    Action action = CreateAction();
    action();
    action();
    var counter = 0;
    Action action2 = () => { Console.WriteLine("counter={0}", counter++); };
    action2();
    action2();
  }

  static Action CreateAction() {
    int counter = 0;
    return delegate
    {
      // Yes, it could be done in one statement; 
      // but it is clearer like this.
      counter++;
      Console.WriteLine("counter={0}", counter);
    };
  }
}

Each call prints a different value, because the state in the closure is mutable. Can Java do that? Can Rel?

Andl - A New Database Language - andl.org
Quote from dandl on June 20, 2019, 11:22 am
Quote from AntC on June 20, 2019, 6:33 am
Quote from dandl on June 20, 2019, 12:51 am
Quote from johnwcowan on June 19, 2019, 12:12 pm
Quote from Dave Voorhis on June 19, 2019, 9:55 am

My proposition is that relvars are the main holders of mutable state. Mutable state can also be represented by local scalar variables within operators and maybe global but non-persistent scalar variables.

And, not to be stroppy about it, when you have functions with mutable local variables[1], you have everything you need for objects.  Impure FP and OO are duals.  There's a poem about it, "Lords of the Lambda", written by the redoubtable Anon.:

To be clear, my intention was never to propose that OO could do what the TTM type system could not, just to argue the consequences of having no equivalent for "packets of state".

I freely admit to being largely ignorant of the innards of OOP, but I'm just not seeing any difficulties here.

The equivalent for "packets of state" is the whole database; or relvar values; or tuples-in-relvars; or attribute-values-in-tuples -- depending on how small your packet needs to be. You might temporarily load some packet from the database into a program-local var (possibly with some sort of calculation/aggregation), possibly writing it back to the database before that var goes out of scope/existence.

No, it really isn't. One thing I learned the hard way over the decades of building software is to control visibility. Wherever there is visibility, there is dependency. If you (anyone) can see my data  values or call my functions then my changes can affect your code. I was a slow convert to OO; I thought and still think that inheritance is basically a bad idea, but encapsulation is why OO works. It isn't the only way, but it does do the job.

So if you want to explain why a TTM language and type system can be used to build big complicated programs with lots of independently developed modules and libraries, you can't start by just putting all the "packets of state" in the database. It doesn't fly.

There's nothing in TTM that precludes your D having modules, classes, namespaces, or any other familiar or new mechanism for grouping code, managing complexity, and handling local state.

Though I rather like the TTM implication that global mutable state == the database. It's also certainly reasonable to distinguish local state and global state, but that's outside of the scope of TTM.

One of the TTM writings suggests using security mechanisms to control access rather than access modifiers. I think that's an interesting approach to explore.

I'm the forum administrator and lead developer of Rel. Email me at dave@armchair.mb.ca with the Subject 'TTM Forum'. Download Rel from https://reldb.org
Quote from dandl on June 20, 2019, 11:27 am
Quote from Dave Voorhis on June 20, 2019, 6:36 am

Java has supported closures since Java 1.1, but with some limitations (non-final variables in the outer scope aren't accessible) via an explicit mechanism called an anonymous inner class, which is essentially a cross between a conventional class and one or more lambda functions (-ish) with added verbosity.

Java has supported "true" lambdas with closures since Java 8, with fewer restrictions than earlier Java versions.

Tutorial D implies closures but doesn't specify them explicitly, but the grammar permits nested operators so in Rel inner operators close over their outer scope. Rel also defines higher order operators (see https://reldb.org/c/index.php/read/anonymous-and-first-class-operators/) so closures can be passed around.

My reading is that a 'closure' with access only to 'effectively final' variables is not a closure. In particular it cannot capture mutable state as per the example. C# certainly does. Here's a test program (and link):

// https://stackoverflow.com/questions/428617/what-are-closures-in-net

using System;

class Test {
  static void Main() {
    Action action = CreateAction();
    action();
    action();
    var counter = 0;
    Action action2 = () => { Console.WriteLine("counter={0}", counter++); };
    action2();
    action2();
  }

  static Action CreateAction() {
    int counter = 0;
    return delegate
    {
      // Yes, it could be done in one statement; 
      // but it is clearer like this.
      counter++;
      Console.WriteLine("counter={0}", counter);
    };
  }
}
  1. // https://stackoverflow.com/questions/428617/what-are-closures-in-net
  2. using System;
  3. class Test {
  4. static void Main() {
  5. Action action = CreateAction();
  6. action();
  7. action();
  8. var counter = 0;
  9. Action action2 = () => { Console.WriteLine("counter={0}", counter++); };
  10. action2();
  11. action2();
  12. }
  13. static Action CreateAction() {
  14. int counter = 0;
  15. return delegate
  16. {
  17. // Yes, it could be done in one statement;
  18. // but it is clearer like this.
  19. counter++;
  20. Console.WriteLine("counter={0}", counter);
  21. };
  22. }
  23. }
// https://stackoverflow.com/questions/428617/what-are-closures-in-net

using System;

class Test {
  static void Main() {
    Action action = CreateAction();
    action();
    action();
    var counter = 0;
    Action action2 = () => { Console.WriteLine("counter={0}", counter++); };
    action2();
    action2();
  }

  static Action CreateAction() {
    int counter = 0;
    return delegate
    {
      // Yes, it could be done in one statement; 
      // but it is clearer like this.
      counter++;
      Console.WriteLine("counter={0}", counter);
    };
  }
}

Each call prints a different value, because the state in the closure is mutable. Can Java do that? Can Rel?

Yes, Java can do that (since Java 8) and so can Rel. 

There are still some restrictions in Java 8 +, but I don't recall what they are (and can't be bothered to look, because it doesn't really matter.) They certainly haven't impacted any code I've written and I use lambdas extensively.

By definition, a closure is a reference to a method/function/operator's invocation environment. It's still considered a closure whether there are limitations or not. If there are limitations, it's simply a closure with limitations.

I'm the forum administrator and lead developer of Rel. Email me at dave@armchair.mb.ca with the Subject 'TTM Forum'. Download Rel from https://reldb.org
Quote from Dave Voorhis on June 20, 2019, 11:35 am
[...]

Each call prints a different value, because the state in the closure is mutable. Can Java do that? Can Rel?

Yes, Java can do that (since Java 8) and so can Rel. 

There are still some restrictions in Java 8 +, but I don't recall what they are (and can't be bothered to look, because it doesn't really matter.) They certainly haven't impacted any code I've written and I use lambdas extensively.

By definition, a closure is a reference to a method/function/operator's invocation environment. It's still considered a closure whether there are limitations or not. If there are limitations, it's simply a closure with limitations.

I should point out one of the restrictions. If you want to mutate a variable in the outer scope, you have to wrap it (i.e., make it effectively final). Like this:

import java.util.LinkedList;

public class LambdaDemo {
  public static void main() {
    var wrapper = new Object() { int ordinal = 0; };
    var list = new LinkedList<Integer>();
    list.add(3);
    list.forEach(s -> wrapper.ordinal++);
  }
}

You can't do this:

import java.util.LinkedList;

public class LambdaDemo {
  public static void main() {
    int ordinal = 0;
    var list = new LinkedList<Integer>();
    list.add(3);
    list.forEach(s -> ordinal++);		
  }
}

But you can do this, i.e., read but not write variables in the outer scope:

import java.util.LinkedList;

public class LambdaDemo {
  public static void main() {
    int ordinal = 0;
    var list = new LinkedList<Integer>();
    list.add(3);
    list.forEach(s -> System.out.println(ordinal));		
  }
}

Determining whether this is a limitation or a feature is left as an exercise for the reader.

I'm the forum administrator and lead developer of Rel. Email me at dave@armchair.mb.ca with the Subject 'TTM Forum'. Download Rel from https://reldb.org

Indeed, but the answer to my question is no, Java does not support closures of the kind referred to (packets of mutable state similar to OO instances). That was indeed my reading.

C# does, because it creates a hidden class.

Andl - A New Database Language - andl.org
Quote from Dave Voorhis on June 20, 2019, 11:29 am

No, it really isn't. One thing I learned the hard way over the decades of building software is to control visibility. Wherever there is visibility, there is dependency. If you (anyone) can see my data  values or call my functions then my changes can affect your code. I was a slow convert to OO; I thought and still think that inheritance is basically a bad idea, but encapsulation is why OO works. It isn't the only way, but it does do the job.

So if you want to explain why a TTM language and type system can be used to build big complicated programs with lots of independently developed modules and libraries, you can't start by just putting all the "packets of state" in the database. It doesn't fly.

There's nothing in TTM that precludes your D having modules, classes, namespaces, or any other familiar or new mechanism for grouping code, managing complexity, and handling local state.

No argument with that. It's just about getting agreement that this a big difference. OO classes provide both lifetime and access other than global/forever and local/invocation out of the box and TTM types do not. OO programming makes heavy use of this, and TTM types will need to find a quite different way. They might extend the language, or they might use closures or they might do something else entirely, but they need to do something.

Though I rather like the TTM implication that global mutable state == the database. It's also certainly reasonable to distinguish local state and global state, but that's outside of the scope of TTM.

One of the TTM writings suggests using security mechanisms to control access rather than access modifiers. I think that's an interesting approach to explore.

Yes. I deeply distrust this suggestion, but that could be because I've never seen a detailed explanation of how it might work. It suggests a program might compile (because X is visible) and then not run (because X is not accessible). Doesn't sound like a good idea.

Andl - A New Database Language - andl.org
Quote from dandl on June 20, 2019, 1:28 pm

Indeed, but the answer to my question is no, Java does not support closures of the kind referred to (packets of mutable state similar to OO instances). That was indeed my reading.

C# does, because it creates a hidden class.

There are indeed minor differences. C# clearly doesn't go to the same effort to make code "safe" that Java does. Java requires that you wrapperise variables you intend to mutate, which makes it clear what outer scope variables can change and what can't. (Of course, nothing stops you from invoking methods that mutate state all over the place in either language, but that's a characteristic (flaw?) of procedural programming in general.)

I would guess requiring wrapperisation was originally done for technical reasons -- with added safety being a bonus -- when closures were implemented via inner classes in the pre-lambda days, but has been retained in lambdas because safety. Also, lambdas are syntactic sugar for inner classes, so it makes sense that Java/Oracle/OpenJDK didn't want to break any more legacy behaviour than they had to.

I'm the forum administrator and lead developer of Rel. Email me at dave@armchair.mb.ca with the Subject 'TTM Forum'. Download Rel from https://reldb.org
PreviousPage 2 of 7Next