Everything is two string

Introduction

The Russell's paradox is an antinomy discovered by Russell, in the Frege ensemble theory. The problem was that elements and ensembles were handled exactly the same way. This was leading to a contradiction. To avoid that, Russell's proposed what has become nowerdays type theory

When people says that a list is just a special kind of string, do they realize that they mean that plurality is just a special kind of unity ?

Hence, when Tcl propose « Everything is A string », Tcl looses the ability to distinguish between element and ensemble, between string and list, between unity and plurality, what lead fataly to contradiction (bug)

Types, Protocols and braced-prefix

Russell's paradox resolution imply the notion of type. A type is a string, associated with a data, that helps to interpret it.

Protocol, in ancient greek, means « glued in front ». Protocol are everywhere in the network. They imply generally an header and encapsulation of data. In fact, the header of a protocol look a lot like a type. It will help to work whith the data which is encapsulated.

In Tcl, Braced-pefixes are strings between braces that can be put in front of a Tcl_Word. Currently, the only one which exist is the expansion prefix, all other braced-prefix result in a syntax error. They are just a concept for now. But we can notice that they look like an header for the prefixed data. Maybe they could help

Unity and Plurality confusion

When we have a string like {word word}, we can see it as one string or as 2 elements list, since it obey to the list parser syntax.

But we are lacking control on the interpretation Tcl can do of it, especially in a flow of successive substitutions. Sometimes, silentely, Tcl will shimmer it, going from list to string, then from string to list,...etc.

We might need sometimes a string with space inside. And I might not want it to be interpreted as a list.

Conversely, I might need a list that I don't want to be interpreted as a sole string.

Solution proposal

There is a space, at the script level, for a syntax improvement, since braced-prefix are actually an error. We may use it for this purpose to enforce the interpretation of the data type

set AmbiguousArity {my string with space}
for {set i 0} {$i < [llength $AmbiguousArity]} {incr i} {}
puts $i ; # i = 3

set UnambigousArity {string}{my string with space}
for {set i 0} {$i < [llength $UnambiguousArity]} {incr i} {}
puts $i ; # i = 1

set Sz {string}[.entry get]

This solution is to tag data with an header, to help Tcl to make the good choice. This header should be a braced-prefixed string, because this will be backward-compatible.

Of course, to get it right, llength should be made aware of this. These can be achive by introducing the notion of protocol.

protocol llength string {return 1}

In fact, allmost every command could be modulated like this. Imagine you have a JSON string

# set can use typed_json::json2dict for a JSON value
protocol set value JSON typed_json::json2dict
set data {JSON}{"name": "Alice", "age": 30}
#  Returns {dict}{name Alice age 30}

protocol set variable dict dict set
set {dict}data name Bob
#  Returns {dict}{name Bob age 30}

Any string could be used as braced-prefixes header of data. If there is a protocol that is set in a command, the command will use the protocol with this data, if not it will use the usual way.

Of course, we may need to check the data header. That implies some specifics commands.

set header [meta get $data]
lassign [meta split $data] header data
switch $header {
...
}

Conclusion

With 2 strings, the first beeing an header, the second beeing the data, Tcl could be able to resolve some ambiguities that its current model is suffering now.

Those headers should not only concern basic types. More evolved behaviour could be implement, in link with their own packages, which would modulate, by use of protocols at the script level, the modus operandi of basic tcl core commands. We can think about {XML}, {JSON}, {C}, well, whatever, since those header are just arbitrary string.

Then we will say : Everything is TWO strings.


Where could this idea be usefull ?

Here is to collect various pages that may be related to this concept.


Object Type Control Discussion : This page relates to the fact that Tcl_Obj values are lost by the machinery during any string concatenation. That leads to TIP#126 project (never implemented). Last proposal from jyl was « I'd first like to ask the source type to convert *itself* to the target type. This could return one of three results: a converted object, a flat-out refusal, or a defer-to-target-type ».

A solution could be to prefix a quoted string, so that a special command would be used to operate the substitution. Nota bene : this is actually a syntax error.

protocol parseQuotedString magic magic::appendTo; # The protocol to apply when a quoted string is prefixed by '{magic}'
set str {magic}"$obj foo foo foo"; # str is returned with an appropriate type representation.

AMB: I do like the idea of expanding Tcl through the braced prefix, as it already has been done with {*}. It wouldn't even require adding rules to the Dodekalogue. Rule #5, currently titled "Argument Expansion" could be changed to "Braced Prefix".

However, I think that implementation of type in Tcl would be best handled through Tcl's object-oriented framework. I think the current barrier there is a lack of automatic garbage collection for Tcl objects, which makes it difficult to create container classes that enforce a type.

Also, I think that any new braced prefixes should be implemented in the core, and that the number of braced prefixes should be small. Adding the ability for people to create customized protocols in pure Tcl would create backwards compatibility issues down the road. This is an area of Tcl where improvements can be made without breaking any old code, so we should be careful to not waste the opportunity.

I think the best protocols to add at the moment (in addition to the {*} prefix) are {=} and {#}.

The protocol {=} would take a comma-separated string of expressions, evaluate them, and then expand the arguments. This would improve user experience in the case of entering multiple expressions as arguments to a command.

.canvas create rectangle [expr {$w-100}] [expr {$h-40}] [expr {$w+100}] [expr {$h+40}]; # This is obtuse
.canvas create rectangle {=}{$w-100,$h-40,$w+100,$h+40}; # much easier

The protocol {#}, which has already been suggested in TIP 401 , would treat the word it is glued to as a comment, discarding it. This allows for comments in the middle of long commands.

set x [list {#}{some words} hello world {#}{some numbers} 1 2 3]; # hello world 1 2 3

I don't think that any other protocol is necessary to be honest. I think that the issues with "Everything is a string" are best handled with improvements to TclOO, and not to the way that raw Tcl values are handled. Part of Tcl's appeal is its simplicity.


FM : TclOO is a big machinery. It's done for big objects that need encapsulation : methods are on the root of it. Notice althougth that it lacks the very basic concept of property. Anyway, let's take it. What if you want a list object ? That's what I did whith my typedlist package. I had to implement, in the class, every methods that are already existing as command into Tcl (list, lappend, lindex,...etc). Of course, those methods are very simple to write. But is that not extra work ?

As I said above, the biggest problem with « everything is ONE string », is that you can't build any 100% secure way to distinguish between unity and plurality. Most times, you can, but there will be few corner cases where you can't. Those corner cases are not a problem for a prototype. Then Tcl will appear to be an elegant, simple, clever way to handle a program logic. But, when you want to drive your prototype into a professionnal software, with a good user experience, those corner cases will manifest, and you will have to complicate a lot your code to deal with them. Then you will say : Tcl is not so simple as it first appears. You will have to investigate, to learn the internal of Tcl to avoid the falls into those corner cases. The wiki is full of those prototypes which were never finished.

In fact, a list is not one object. It's a collection. An object has an unity, it's like a body, an individual, an atom. A collection has an arity, it's like a town, a society, a molecule. If its members are indexed by a number, then it's a list. If its members are indexed by a string, then the collection is a dict. The collection can also be indexed differently, for instance when it's a tree, or when it's multi-dimensional, like a matrix

As Tcl took Lists as Strings, TclOO took Classes as Objects. I think this is philosophically wrong. This is confusing plurality into unity, what make Tcl fall into « Russel Paradox » class problems. This strange choice look like it was inspired by the EIAS initial confusion.

That's why I think there is no real choice. If Tcl want to be seen as a language able to create professional softwares, the minimum is to give Tcl an 100% secure way to distinguish between unicity and multiplicity.

But, this 100% secure way should be optional, to say, it should be allowed to be omited when writing a prototype at the first stage of software developpement, but it should be allowed to be inserted later. Ex :

# 1. into the prototype : generic case
set myList $value
# 2. Into the final software : ticket n°456465421 : myList must be a list 
set {list}myList $value

So I can't agree with what you wrote above. Of course, I think {#} and eventually {=} are very important and should be there. But I think it is also fundamental to be able to always save the arity of a value : At least, the braced-prefixes {string}, {list} and {dict} must be implemented. Other collection types could be convenient also, like {tree} or {matrix}, which would be great to have.

At least we need to be able to make those tiny, but essential, distinctions :

set S {string}{}; # Empty string : null element
set L {list}{}; # Empty list : indexed collection with no members
set D {dict}{}; # Empty dict : keyed collection with no members
expr {$S eq $L}; # should return 0
expr {$S eq $D}; # should return 0
expr {$L eq $D}; # should return 0
set S {string}{sentence with many words}
arity $S; # should return 1 : $S is one element.
set L {list}{sentence with many words}
arity $L; # should return 4 : $L is four elements list.
set D {dict}{sentence with many words}
arity $D; # should return 2 : $D is 2 elements dict.

As far as I thought about it, It could be implemented as a new Tcl_ObjType value : let's say TclMetaType, whose representation is to be two Tcl_Obj ptr (beyond the two strings model):

  • The first one, ptr1, would point to a Tcl_Obj value, whose bytes would be the name of the wished type.
    • most of the time, this value should be a Tcl_Obj string, for simple types.
    • Eventualy, ptr1, would be allowed to point to a Tcl_Obj list value, to take into account parameters (ex : {matrix(3,3)}{} or {tree(binary)}).
    • Maybe ptr1 could also be allowed to point to a Tcl_Obj dict value, if there is a need for named parameters like {list(arity=3)}.
  • The second one, ptr2, would point to the original value Tcl_Obj.
    • This original value Tcl_Obj would have a constant internal representation, which would be configured thanks to its braced-prefix.
    • This original Tcl_Obj value can never shimmer
    • its bytes representation should be always NULL.
    • its internal rep would have a constant type.
  • The byte representation of this encapsulated value would exists only in the metaType Object Value.
    • it can eventually change (shimmer) as usual, but leaving untouched the internal rep of the original value (the one in ptr2).
    • The byte representation of this metaType Object Value can be think as a « projection » of the original value along the type demanded by the caller.
    • The metaType Object Value will allways hold a reference to the original value, to avoid this original value to be automatically discarded.
  • Of course, this metaType Object Type won't be the default Object type, which will remain the string type.
    • It would be create only when there is a need to encapsulate an original value in order to protect it against unwanted shimmering.
    • This meta Object type will be only create when there is a braced-prefix before the Word.

Illustration :

# usual case :
set s {a b c}
tcl_obj {
bytes "a b c"
type &TclStringType
...
}
# braced-prefix cases :
set S {string}{a b c}
metatypeObj {
bytes "a b c"
type &TclMetaType
ptr1 [-> {Tcl_Obj *}{
bytes "string" 
type &tclStringType ...
}] {#}type\ object
ptr2 [-> {Tcl_Obj *}{
bytes "" 
type &tclStringType 
internalRep ...}] {#}value\ object
}

set L {list}{a b c}
metatypeOb {
bytes "a b c"
type &TclMetaType
ptr1 [-> {Tcl_Obj *}{
bytes "list" 
type &tclStringType 
internalRep ...}] {#}type\ object
ptr2 [-> {Tcl_Obj *}{
bytes "" 
type tclListType 
internalRep ...} {#}value\ object
}]

set L {list(3)}{a b c}
metatypeOb {
bytes "a b c"
type &TclMetaType
ptr1 [-> {Tcl_Obj *}{
bytes "list 3" 
type &tclListType 
internalRep ...}] {#}type\ object
ptr2 [-> {Tcl_Obj *}{
bytes "" 
type &tclListType 
internalRep ...}] {#}value\ object
}

AMB: I do agree that TclOO does add a lot of machinery that is not quite needed for primative types. Perhaps instead a type can be enforced upon a variable (or a Tcl array element) through an optional argument to the "set" command.

set varName ?-type type? ?value?

The optional "-type" setting would set the internal representation of a variable. By default, all variables would have type {}, which means that their internal representation can be changed on the fly. Setting a type on a variable would enforce its type and prevent shimmering unless manually shimmered through redefining its type.

The type of a variable would be accessible through "info type varName", which would return {} if no type is enforced.

info type varName

For completeness, types would also be able to be set within the arguments for a proc.

proc myproc {{a -type double} {b -type int 1} args} {
    # stuff goes here
}

The types that would be allowed would be all the types in the string is command. Any new types, such as your proposed "tree" or "matrix" types, should be added to string is as well.

I don't disagree with your "two-string" paradigm. I think it would be a great addition to Tcl. But I don't think that the braced prefix notation is the right approach to it. I think it can be handled without breaking the Dodekalogue.

FM : my proposal is not only relative to the type of a variable. Of course, it is, incidentally, through the set command. For instance, in

set {list}Var {...}

The set command recieve as 1st arg a word whose internal type is of &tclMetaType, and where prefix string is list. That's why set knows it will have to create the variable Var as a list.

The braced-prefix interpretation would be left to its command. There could be quoted-prefixes also, to allow substitution in it, so that they can have parameters. So, YES. If a command is waiting for a variable on a specific argument, the prefix can be seen as a type, but it exists many other situations : If a command is waiting for a script, the prefix can't be seen as a variable type, but as a way to modulate the script interpretation. For instance :

# a proc whose body is in c
protocol proc body {c} apply {{body} {critcl::cproc $body}}
proc cproc {} {c}{
int i;
for(i=0;i++;i<j) {
...
}
}

In this case, the braced-prefix {c} would indicate that the body is written in C language, and then proc can call critcl behind the scene (if it's made aware on what to do a when its body argument has a {c} header).

We can think as well about a « parsing argument protocol », that the proc command would understand to modulate the way it processes its arguments :

# use name arguments processing
protocol proc argument {named} apply {{args} {foreach {n v} $args {uplevel {set [string range $n 1 end] $v}}}}
proc myproc {named}args $body
myproc -option1 val1 -option2 val2 ... ...

In all those use cases, the prefix is not related to any variable type. Yes, we can say it exists a type of body compilation, or it exists a type of argument processing. But, then, we are not using the word « type » in its computing specific meaning. Type here doesn't rely to raw data types (int, char, double, boolean,..etc), nor to extended data types (string, list, dict,..etc).

So, the braced-prefixed data operates on Values, making it a 2-strings value, the first string beeing the header (a meta-data value), the second string beeing the data.

But, remains your question : Why with this syntax ? Because I expect the prefix to be detected during the Tcl parser step, right into Tcl_ParseCommand, which would create a TCL_TOKEN_PREFIX while meeting this syntax. That's only later, during the objectification step, that a &TclMetaType object will be created, as explained above, as two-object value.

Later, the commands will be left in charge to recognize the prefix-header (the meta data of the &TclMetaType Obj). If the command does recognize it, it can modulate its data processing consequently. If it doesn't recognized it, the command will just use the string data value and skip the string meta-data value. Also, I wished this recognizing not to be only written in the C-source, but also to be configurable at script level. That's why I'd like to have a protocol command to allow making this kind of configuration.

An illustration with set :

# protocol to extend « set » algorithm to accept a list of value
protocol set variable list apply {{var args} {uplevel set $var [list {*}$args]}}
set {list}L e0 e1 e2 e3 e4
# protocol to extend « set » algorithm to accept an array-like syntax for a list
protocol set variable list apply {{variable args} {
     regexp {([a-z]*[A-Z]*)\(([a-z]*[A-Z]*)\)} $variable -> var part
     uplevel [list lset $var $part [list {*}$args] ]
}}
set {list}L(3) e3'
# nb : if the list protocol has been unkown to `set`, this would have result into the following error :
# "can't set "L(3)": variable isn't array". 
# That's because `set` would have then used its normal processing algorithm on the string value ''L'', 
# ignoring the meta-data string value ''list''.

This way, the string power of Tcl can be use either on the string data, either on the string meta-data. An infinity of possibilities opens : like using tdom while setting a {xml} value, or using json parser while setting an {json} value.