hi,
while we are chatting on irc, martin is having a stroll through the pike core and he reported some interresting findings:
he found that the typesystem has types that have not been implemented, like: ring, scope and tuple.
now tuple i am familiar with from python, but the others don't ring ;-) a bell.
hubbe or someone else who might know, can you enlighten us as to what the properties of these types would be?
also, which properties does a tuple have, that i can't get from an array with two elements?
greetings, martin.
he found that the typesystem has types that have not been implemented, like: ring, scope and tuple.
hubbe or someone else who might know, can you enlighten us as to what the properties of these types would be?
I guess I'm someone... :-)
As you've noticed, none of the three is fully implemented yet.
Intended use:
RING: Ring-operator. Intended for sequencing types. (f°g)(x) === f(g(x))
SCOPE: Type variable allocator. Intended for avoiding type variable clashes.
TUPLE: Ordered sequence of typed values.
also, which properties does a tuple have, that i can't get from an array with two elements?
With the current typing of arrays, you can't specify different types for different indices in the array.
How do they use tuples in practise? Do you have to declare the types for the whole tuple when the tuple is declared? If so, is there a practical use for it where you would be better of using a tuple instead of an array hidden in an object with accessor methods that does the typechecking?
None of the three is used in practise.
You don't declare a tuple; you declare an array.
The type would mainly be used compile-time, and thus wouldn't have the overhead of accessor functions.
On Thu, Jan 27, 2005 at 10:50:01AM +0100, Henrik Grubbström (Lysator) @ Pike (-) developers forum wrote:
RING: (f°g)(x) === f(g(x))
what would be the advantage of this? other than seeming harder to read? i tried to read up on rings in math refereces and didn't really understand anything. most confusing is the fact you are using ° on functions. it implies that f°g yields some kind of result, like f(g) which is completely different from f(g(x)).
SCOPE: Type variable allocator. Intended for avoiding type variable clashes.
please elaborate.
With the current typing of arrays, you can't specify different types for different indices in the array.
sounds usefull, and is kinda like what i have been asking for in mappings (named tuples? :-)
how might the syntax look like? tuple(string:int:int) foo = (x "foo", 1, 3 x) where x is to be replaced with a yet to be chosen character?
however this does not appear the same as tuples in python, which appear to be just defining the values immutable. (i have not actually used tuples in python yet, so i am unsure of their properties.
greetings, martin.
If I'm not mistaken, h=f°g creates a new function h(x) which is the same thing as h(x)=f(g(x)).
RING: (f°g)(x) === f(g(x))
what would be the advantage of this?
It's usefull for converting a left recursive expression into a right recursive expression.
other than seeming harder to read?
The °-operator would usually not be written explicitly.
most confusing is the fact you are using ° on functions.
Yes, that's the normal mathematical use.
SCOPE: Type variable allocator. Intended for avoiding type variable clashes.
please elaborate.
Type meta variables are used to bind types during type derivation. eg:
typeof(_typeof);
(1) Result: function((0=mixed) : type(0))
In the above 0 is a type meta variable. Scopes would be used to define the validity for 0, and to avoid clashes. Currently this is broken eg:
typeof(_typeof(_typeof));
(2) Result: type(function((0=mixed) : type(zero)))
The above result should have been
type(function((0=mixed) : type(0)))
With the current typing of arrays, you can't specify different types for different indices in the array.
sounds usefull, and is kinda like what i have been asking for in mappings (named tuples? :-)
how might the syntax look like? tuple(string:int:int) foo = (x "foo", 1, 3 x) where x is to be replaced with a yet to be chosen character?
It'd probably be something like
array(string,int,int:void)
It'd probably be something like
array(string,int,int:void)
What'd the ":void" part do?
It was intended for the many field. Otherwise it would be hard to differentiate between the array with a single element, and the array with all elements of the same type.
Array with single element:
array(int:void)
Array with a variable number of integers:
array(int)
or
array(void:int)
Array with at least two integers:
array(int,int:int)
But the elipsis operator is used to indicate many in the function case, so it should probably be used here too.
I think it'd be more useful if the whole tuple is repeated, rather than a function-style "many" argument at the end. I.e. like this:
array(int) -> Zero or more ints. array(string,int) -> Zero or more (string,int) tuples, i.e. the array always has an even number of elements.
Then there could be another extension to handle arrays of specified lengths. Something like this, perhaps:
array(int)(..1) -> Zero or one int but no more. array(string,int)(1..2) -> One or two (string,int) tuples, i.e. either two or four elements in total. array(int)(3) -> Perhaps a shortcut for array(int)(3..3).
This scheme wouldn't allow for specifying arrays where the first couple of elements have some specified types and all the rest have the same type, but I can't think of any real world cases where such typing would be useful. Theoretically it could be used when splicing arrays to function arguments. I've very rarely spliced arrays into arguments that were declared to different types, though; the whole array is typically spliced into the "many" argument at the end.
I can't point to any place in particular, but I know I've written code more than once that takes a few parameters for itself and feeds the rest to a user-provided callback, of some sort, much like call_out. It might not be a very uncommon case to want to type the remainder rather than always assuming the first types repeat infinitely throughout the array.
Then there could be another extension to handle arrays of specified lengths. Something like this, perhaps:
array(int)(..1) -> Zero or one int but no more. array(string,int)(1..2) -> One or two (string,int) tuples, i.e. either two or four elements in total. array(int)(3) -> Perhaps a shortcut for array(int)(3..3).
And perhaps array(string,int)(*) for any number of string,int tuples?
RING: (f°g)(x) === f(g(x))
what would be the advantage of this? other than seeming harder to read? i tried to read up on rings in math refereces and didn't really understand anything.
If you look up "ring" in mathematical references you have to be a bit careful, since there is a term "ring" in group theory that refers to a set with some particular properties. That kind of "ring" has nothing to do with the "ring operator" (which is also known as the "composition operator", and does just that: compose two functions into one).
Is there any typical case where you want repeating tupels?
I think it's much more common to have a few arguments of varying types and then the same type with random number of elements. Ponder for instance
int foo(string x,int y,mixed ...misc);
foo(@tupel);
tupel here should match the function arguments (right?).
ah, thank you,
now i understand why i didn't understand anything :-)
greetings, martin.
i am half thinking that your suggestion is ironic, because array(string,int)(*) is very similar to the old: type * for an array of types.
since that was changed to : array(type) it seems abvious that the other should be: array(array(string,int))
greetings, martin.
i am half thinking that your suggestion is ironic, because array(string,int)(*) is very similar to the old: type * for an array of types.
I didn't make that connection myself, but were more thinking of some kind of similarity to the automap syntax. Which, granted, also might be a horrible syntactic idea.
since that was changed to : array(type) it seems abvious that the other should be: array(array(string,int))
({ ({ "I think that ", 1 }), ({ "is already taken. ;-)" }) })
True, but in that case you typically don't have a good type in the array to begin with. It'll look something like this:
static void create (string coolness, mixed... args) { MyFoo::coolness = coolness; ::create (@args); }
And args is a boring array(mixed) with no nice type info. Here you'd like to somehow do:
static void create (string coolness, <insert type of inherited create args> args) { MyFoo::coolness = coolness; ::create (@args); }
But since that isn't possible your only choice if you want to retain good typing is to repeat all the args of the overridden create.
Is there any typical case where you want repeating tupels?
The case that comes first to my mind is when you're conscious about keeping down the size of the memory structures, so instead of having an array where each element is a small object with a couple of variables, you have the variables directly in the array in a specific order. I've written such code on several occasions.
Another specific case is to be able to fully describe the result of flatten operations.
A third interesting case is in parsers where you have arrays of some parsed info that are separated by less significant text, e.g. even numbered elements are objects representing parsed xml elements, and the odd numbered elements contain the text separating them. That would however require a variant where the array is allowed to end in the middle of a tuple.
The typical case where there are just unknown types in the first place isn't very interesting to this line of thought, I think. I agree that it might be an indication of how often that situation occurs, but not something supporting how often the reverse situation does not.
I may be hoping too much about being able to interleave levels who do know about types and levels who don't without losing type info. If one call passes along a richly typed value, which some generic middleman broker such as call_out or map (itself) isn't interested in to some third method which does have a strictly typed argument set, is the typing lost when passing through the middleman? On splicing a richly typed array into a similarly richly typed method being called?
I seem to recall having handled quite a lot of that kind of code when using Protocols.BitTorrent, though the details have fled me since. It more or less boiled down to passing bits of data of your own among a set of NIH callbacks to communicate state between your program parts, mediated by the Protocols.BitTorrent asynchronous prototocol driver. I suppose an easier approach for doing such code in general is the Roxen way, wrapping everything in some kind of state object, but I kind of like methods to have an intelligble argument signature.
When it comes to the problem of passing types over unaware middleware, the solution is rather along the lines parameterized types, like templates, or maybe more advanced metaprogramming. It's far from clear if and how tuples would tie into that, so designing them first with that in mind seems a bit backward.
Besides, it'd be straightforward to extend the repeating tuples approach with a "many" syntax, but the opposite is more difficult.
(Btw, philosophically I think the repeating is in the array and not in the tuple - it's in the nature of arrays to make a sequence by repeating something. Thus it's logical that an array over a tuple repeats it, as opposed to a function whose argument list can be viewed as a tuple.)
I think I first misunderstood your stance as being against keeping the option of a nonrepeated type chain at all. (Not that the past exchange came to revolve much around that topic, but either way I'm relieved it was a misconception of mine.)
So in order to get a "normal" tuple you would always have to suffix "(1)"? I.e.
array(int,int,int)(1) -> A triple of ints
Actually, maybe the example wasn't so bad after all. It illustrates the problem of whether to consider the types array(int,int)(1) and array(int)(2) equal or not.
Yes, when you want to use an array as carrier for the tuple. It's also consistent with the current arrays in the case of singleton tuples.
Other carriers can be considered. A kind of lightweight object could often be more suitable in the case of single tuples. That would allow accessing the members through names instead of indices. E.g.
object(int a, string b)
could be a shorthand for adding a class
class (int a, string b) {}
but with the addition of built-in support in sprintf, encode_value etc. I've often wanted something like that to make it easy to use objects as simple data carriers.
If the members of the tuple are nameless they would be accessed through numeric indices. (And yes, if "object" is used as keyword like above there'd be an ambiguity in the case of a single nameless member, so it's not ideal in that regard.)
pike-devel@lists.lysator.liu.se