From: skaller <skaller@us...>  20041027 03:49:34

On Wed, 20041027 at 11:54, Rhythmic Fistman wrote: > >It's the same problem more or less. There are lots of place > >in Felix where typing is checked, that I haven't fixed > >to handle lvalues. > > > >I actually can't reproduce this problem. The source code > > Ok, I've condensed it to this: > > include "std"; > > header 'typedef struct { int x, y; } apoint;'; > > ctypes apoint; > > //parameterized types might suck less here. > fun get_x : lvalue[apoint]>lvalue[int] = "$1.x"; > fun get_x : apoint>int = "$1.x"; > fun get_y : lvalue[apoint]>lvalue[int] = "$1.y"; > fun get_y : apoint>int = "$1.y"; > > > var zero: apoint; > zero.x = 0; zero.y = 0; > var ta = zero; > var tb : apoint; > tb.x = ta.x; > > and I'm now using flx 1.0.17 OK, I get the same problem now. And I'm sure I know what it is: the type of ta here is: lvalue[lvalue[apoint]] There's TWO lvalues prefixes. Change zero OR ta to a val, and it should work. But they're both vars. I'll try to fix it. This can happen in many places and may create the same problem. A simple fix is to use a function: let lvalifiy t = match t with  `TYP_lvalue t > t  t > `TYP_lvalue t and use that everywhere 'lvalue' is applied .. but I have to find all those places :) John Skaller, mailto:skaller@... voice: 061296600850, snail: PO BOX 401 Glebe NSW 2037 Australia Checkout the Felix programming language http://felix.sf.net 
From: Rhythmic Fistman <rfistman@ho...>  20041026 05:53:54

>CHANGE: line 2058 lpsrc/flx_lookup.ipk to read: > > if not (type_match syms.dfns d t2) then That's fixed the problem for me. Now I get CLIENT ERROR LHS must be lvalue In hw.flx: line 617, cols 3 to 40 616: var r: apoint; 617: r.x = a.x + (i*(b.x  a.x))/MOVE_STEPS; ************************************** 618: r.y = a.y + (i*(b.y  a.y))/MOVE_STEPS; I suspect this is my problem. I've been ignoring whole lvalue thing up until now. 
From: Rhythmic Fistman <rfistman@ho...>  20041026 07:30:11

> > LHS must be lvalue [...] >fun mkY: 1 > Y = "0,Y()"; >fun get_x: Y > int = "$1.x"; // value overload >fun get_x: lvalue[Y] > lvalue[int] = "$1.x"; // lvalue overload Ok, I've given myself lvalue versions for assignment: fun get_x : lvalue[apoint]>lvalue[int] = "$1.x"; fun get_x : apoint>int = "$1.x"; fun get_y : lvalue[apoint]>lvalue[int] = "$1.y"; fun get_y : apoint>int = "$1.y"; which fixes the last prob. Now I've got the following error: CLIENT ERROR apply method to nongenerative type In hw.flx: line 693, cols 11 to 11 692: var tb : apoint; 693: tb.x = ta.x; Both ta and tb are apoints. Shouldn't this generate lhs[get_x] = get_x? 
From: skaller <skaller@us...>  20041026 09:19:18

On Tue, 20041026 at 17:29, Rhythmic Fistman wrote: > > > LHS must be lvalue > > [...] > >fun mkY: 1 > Y = "0,Y()"; > >fun get_x: Y > int = "$1.x"; // value overload > >fun get_x: lvalue[Y] > lvalue[int] = "$1.x"; // lvalue overload > > Ok, I've given myself lvalue versions for assignment: > > fun get_x : lvalue[apoint]>lvalue[int] = "$1.x"; > fun get_x : apoint>int = "$1.x"; > fun get_y : lvalue[apoint]>lvalue[int] = "$1.y"; > fun get_y : apoint>int = "$1.y"; > > which fixes the last prob. > > Now I've got the following error: > > CLIENT ERROR > apply method to nongenerative type > In hw.flx: line 693, cols 11 to 11 > 692: var tb : apoint; > 693: tb.x = ta.x; > > > Both ta and tb are apoints. Shouldn't this generate lhs[get_x] = get_x? It's the same problem more or less. There are lots of place in Felix where typing is checked, that I haven't fixed to handle lvalues. I actually can't reproduce this problem. The source code translates; a.x into method_apply('get_x',a) Here's the actual code:  `AST_dot (sr,(e,id)) > let get_name = "get_" ^ id in be (`AST_method_apply (sr,(get_name,e))) A method application is like a normal application EXCEPT that it uses Koenig lookup  'get_x' is looked up in the module defining the type of expression 'a'. This type must be a generative type, meaning, it has to actually be *defined* somewhere, so the idea of looking up in the module defining that type makes sense. The actual code is:  `AST_method_apply (sra,(fn,e2)) > let be2,t2 = be e2 in let tbe1 = match t2 with  `BTYP_lvalue(`BTYP_inst (index,ts))  `BTYP_inst (index,ts) > begin match get_data syms.dfns index with {id=id; parent=parent;sr=sr;symdef=entry} > match parent with  None > clierr sra "Koenig lookup: No parent.."  Some index' > match get_data syms.dfns index' with {id=id';sr=sr';parent=parent';vs=vs';pubmap=name_map;dirs=dirs;symdef=entry'} > match entry' with  `SYMDEF_module > koenig_lookup syms sra id' name_map fn t2 ts  _ > clierr sra ("Koenig lookup: parent for method apply not module") end  _ > clierr sra "apply method to nongenerative type" in cal_apply syms sra tbe1 (be2, t2) which is saying: the type has to be an instance of some type with some entry in the symbol table OR an lvalue of such a type. You can see the special lookup code here too: we lookup the type's symbol table entry and then use its parent to lookup the function, which must be defined in the same module as the type is defined in. All this is so that in: module X { struct A { b:int; } }; var x = XX::A(1); x.b = 1; we want 'x.b' to find the generated 'get_x' method which is defined in module X. Otherwise you'd have to either open the module or write: x X::.b to tell the compiler operator '.b' == get_b of (X::A) is in module X. (That syntax isn't legal in Felix. In Ocaml and C++ you can do something like that ...) Anyhow, it would seem somehow the type isn't an instance, which is weird  both structs and abstract types are generative. Algebraic types are not. For example a pointer to something is an algebraic type, not a generative one (even if the something is a generative type). Same for tuples  they're algebraic not generative. I'll check your code .. you could try Felix 1.0.17, it has a couple of other fixes in it.  John Skaller, mailto:skaller@... voice: 061296600850, snail: PO BOX 401 Glebe NSW 2037 Australia Checkout the Felix programming language http://felix.sf.net 
From: Rhythmic Fistman <rfistman@ho...>  20041027 01:55:12

>It's the same problem more or less. There are lots of place >in Felix where typing is checked, that I haven't fixed >to handle lvalues. > >I actually can't reproduce this problem. The source code Ok, I've condensed it to this: include "std"; header 'typedef struct { int x, y; } apoint;'; ctypes apoint; //parameterized types might suck less here. fun get_x : lvalue[apoint]>lvalue[int] = "$1.x"; fun get_x : apoint>int = "$1.x"; fun get_y : lvalue[apoint]>lvalue[int] = "$1.y"; fun get_y : apoint>int = "$1.y"; var zero: apoint; zero.x = 0; zero.y = 0; var ta = zero; var tb : apoint; tb.x = ta.x; and I'm now using flx 1.0.17 
From: skaller <skaller@us...>  20041027 03:49:34

On Wed, 20041027 at 11:54, Rhythmic Fistman wrote: > >It's the same problem more or less. There are lots of place > >in Felix where typing is checked, that I haven't fixed > >to handle lvalues. > > > >I actually can't reproduce this problem. The source code > > Ok, I've condensed it to this: > > include "std"; > > header 'typedef struct { int x, y; } apoint;'; > > ctypes apoint; > > //parameterized types might suck less here. > fun get_x : lvalue[apoint]>lvalue[int] = "$1.x"; > fun get_x : apoint>int = "$1.x"; > fun get_y : lvalue[apoint]>lvalue[int] = "$1.y"; > fun get_y : apoint>int = "$1.y"; > > > var zero: apoint; > zero.x = 0; zero.y = 0; > var ta = zero; > var tb : apoint; > tb.x = ta.x; > > and I'm now using flx 1.0.17 OK, I get the same problem now. And I'm sure I know what it is: the type of ta here is: lvalue[lvalue[apoint]] There's TWO lvalues prefixes. Change zero OR ta to a val, and it should work. But they're both vars. I'll try to fix it. This can happen in many places and may create the same problem. A simple fix is to use a function: let lvalifiy t = match t with  `TYP_lvalue t > t  t > `TYP_lvalue t and use that everywhere 'lvalue' is applied .. but I have to find all those places :) John Skaller, mailto:skaller@... voice: 061296600850, snail: PO BOX 401 Glebe NSW 2037 Australia Checkout the Felix programming language http://felix.sf.net 
From: skaller <skaller@us...>  20041027 04:57:27

On Wed, 20041027 at 11:54, Rhythmic Fistman wrote: > Ok, I've condensed it to this: OK, i've posted new tarball to fix this. The fix isn't guarranteed: a pair of lvalue prefixes could creep in somewhere I didn't modify. There is a way to guarratee that the idempotent lvalue combinator is uniquely represented by a single lvalue term wherever a term is analysed  I can write a routine that fixes up such terms. However it is expensive to do the requisite recursive descent when it should be possible to prove such a term cannot be constructed. So I'm going to try to do the checking only when converting a term to its lvalue equivalent. When an unbound term is bound, this needs to be done too. For example: var x : lvalue [lvalue [ lvalue [int ] ] ]; however this is slightly messier .. you might also have fun f: lvalue[lvalue[int]] > int and now the 'lvalue' isn't at the top level. BTW: the current unification algorithm is almost certainly WRONG. The decay lvalue[int] > int in an argument is just like the decay derived_type > base_type in a C++ pointer, from a subtype to a supertype. When a *function* is used as an argument, however, the rule is that you can accept a function with a weaker precondition and stronger post condition, which means the function may accept a supertype as its argument, and return a subtype. For example: fun f (g: b > b') (x:b) => g x; If you have classes: b : a b' : a' c : b c' : c' Then you can pass f a function of type a > c' and everything will work. This is because x is type b, and every b is an a, and the result will be a c', and every c' is a b'. So you can see for a *subtype* a > c of b> b' the rule is that b must be a subtype of a, and b' must be a supertype of c'. We say this compactly by saying the domain is contravariant, and the codomain is covariant. This means that for the arrow t  subtype > t' the arrows for domain and codomain when t = b > b' go in the opposite and same direction, respectively, as the whole subtype arrow: b <subtype  a // contra variant     subtype >  V V b'  subtype > c' // co variant This is the rule that totally destroys Object Orientation as a useful general development paradigm. OO requires arguments to be covariant, and mathematics tells us that they have to be contravariant for the type system to be sound. In particular for addition: add: a > a > a where 'a' is an abstract type, the first a is the 'object', the second 'a' the method argument, and the final 'a' the result, we need to implement for a concrete subtype b : a the following: add: b > b > b which is covariant in all three terms. The problem is that the second 'b' must be *contravariant*, and the only way to be contra and co variant at once is to be invariant, so the only possible sound signature is actually: add : b > a > b which means the method argument has to be of the abstract type. But then, you cannot implement the method! You can't add a concrete b kind of number to an abstract one because addition is a primitive defining operation. What do people do? They cheat and use downcasts a > b inside the method, which means it isn't fully polymorphic: it won't work, if you add new subtypes b', b'', b''' of a, without invading the add method and adding a case for each one. The problem here is that if you forget, you will just get a run time error. The type system has failed to ensure soundness, by *requiring* the programmer to use casts to work around a flawed design concept .. namely OO itself. Well the bottom line is that the unification algorithm must respect covariance/contravariance rules for function subtypes. In particular, lvalue[t] is a subtype of t The current unification algorithm is unsound, because it actually allows a function of type lvalue[d] > lvalue[c] to be used where one of type d > c is required. I hope this will not cause any problems in the short term  the fix isn't entirely trivial. I will need to upgrade the unification algorithm to support subtyping. The rules and algorithm are well known for *functional* programs .. but Felix isn't entirely functional .. :) For example, I think in a purely functional FP using boxed representations, a struct is a subtype of another if (a) it has missing fields (some can be added though) and (b) each field is a supertype. Thus in C++ terms the extra field can be sliced off, since the function isn't using them, and we need the remaining fields to be contravariant  we're happy if the supplied field is a supertype, because any use of it will be a function application, which will accept any supertype of its specified argument type. However with *mutable* structures, the result type matters as well  when you modify something it is both an argument and a result of a functional interpretation which means it must be both contravariant and covariant, which implies invariance. lvalues are intended to represent mutable storage .. :) In addition, there is an issue for overload resolution. Specialisation of generics is a kind of subtyping, but it is different for plain subtyping, and handling both is hard, since it is never clear which of a set of overloads is 'most specialised'. C++ has to handle this already but I doubt many people know which is prefered .. a template accepting a more general type with a the argument being upcast, or a more specialised template with no cast .. especially when you need to do BOTH kinds of subtyping to get any kind of match at all. Felix currently avoids that by not implementing any subtyping, and requiring the client to do any casting needed to get the right 'exact' match... except in one case .. lvalues :)  John Skaller, mailto:skaller@... voice: 061296600850, snail: PO BOX 401 Glebe NSW 2037 Australia Checkout the Felix programming language http://felix.sf.net 
From: skaller <skaller@us...>  20041026 06:39:32

On Tue, 20041026 at 15:52, Rhythmic Fistman wrote: > >CHANGE: line 2058 lpsrc/flx_lookup.ipk to read: > > > > if not (type_match syms.dfns d t2) then > > That's fixed the problem for me. > > Now I get > > CLIENT ERROR > LHS must be lvalue > In hw.flx: line 617, cols 3 to 40 > 616: var r: apoint; > 617: r.x = a.x + (i*(b.x  a.x))/MOVE_STEPS; > ************************************** > 618: r.y = a.y + (i*(b.y  a.y))/MOVE_STEPS; > > I suspect this is my problem. I've been ignoring whole lvalue thing up > until now. This code works:  struct X { m:int; }; var x = X(1); print x.m; endl; x.m = 2; print x.m; endl;  because the x.m notation desugars to get_m x where: get_m : lvalue[X] > lvalue[int] Actually this seems wrong, since this should fail: print (X 1).m; but it doesn't, it works. It should be necessary to also generate: get_m : X > int for get_m to accept a nonlvalue. Hmmm. In any case, it would seem you have written your own get_x method for apoint. You'll need to modify it to return an lvalue, here's an example:  type Y = "Y" requires header "struct Y { int x; }; "; fun mkY: 1 > Y = "0,Y()"; fun get_x: Y > int = "$1.x"; // value overload fun get_x: lvalue[Y] > lvalue[int] = "$1.x"; // lvalue overload var y = mkY(); get_x y = 1; print (get_x y); endl; y.x = 3; print y.x; endl;   John Skaller, mailto:skaller@... voice: 061296600850, snail: PO BOX 401 Glebe NSW 2037 Australia Checkout the Felix programming language http://felix.sf.net 