On 18/08/2012, at 12:56 AM, Dobes Vandermeer wrote: > > Yes, I do think this is a bit troublesome. The compiler should not allow you > to use a val or var before it is initialized.
Its hard to stop. And they are initialised, just not by the initialiser you give. Semantically variables are initialised by their C++ default constructor, and the user "initialisation" is always an assignment -- if the val/var or whatever is represented by storage that is. > What if the val is a reference type? Does that let you dereference a > dangling or null pointer, or does it just give you a reference to some kind > of placeholder object? If the representation is a pointer, it will be initialised by C++ by the default initialiser, which is trivial, and does nothing, so it will be garbage bits. This kind of thing is an unfortunate consequence of pandering the "block structured programming style" of C and C++. FPL's use a better but less familiar syntax: let x = y in ... let a = b and c= d in .. let rec ... in Ocaml is much more powerful than blocks: var x : int; { val a = 1; val b = 2; x = a + b; }; However, the Ocaml system has some pain too: sometimes you're forced to initialise things to dummy values. Underneath, it has even worse problems: you're basically forced to heap allocate every variable separately, which in turn gives closures fine control of what they access at the cost of garbage collecting individual variables instead of frames. This is reasonable using a high performance generational incremental copying collector. Felix uses a naive mark-sweep collector because we cannot copy primitive objects (i.e. unknown C++ data can't be copied). Hate to say it .. but the Java rule would be appropriate: you can declare something wherever you like, you just can't use it until after it is *manifestly initialised*. It is hard to do properly in Felix though, because Felix has pointers. Hence aliasing. Hence you'd need sophisticated data flow analysis to catch many errors and probably you still couldn't be sure in all cases. > Your tutorial says "vals are constants. They cannot be changed." It isn't correct. They cannot be assigned to. They can be changed if control flows through the initialisation again. It's even wuss dan dat! Consider: val x = if a then b else c endif That reduces (internally) to val x; if a then x = b; else x = c; so the optimiser actually sees two initialisations of the one variable. Only one gets executed (on a single control sweep). > This made sense to me, now you're saying they're not really constant and they > might change, and furthermore you don't want to fix problem that but instead > introduce "const" as the REALLY constant version of val. > > Why not make val stricter instead? Take all the parts of the val expression > that "might" change (vars, function calls) and evaluate at least those parts > where the val is initialized. Because the point of initialisation is not well defined, deliberately. If you write: var x = 1; val y = x; ++x; println$ y; you might get 1 or 2. You will get 1 with eager evaluation and 2 with lazy evaluation. Straight line code like this is analysed and usually results in eager evaluation if Felix can detect the dependence on a variable. However if its a function argument, and the function is inlined, it is usually lazily evaluated. The moral of the story is: you are free (at the moment) to allow vals to depend on vars. It may or may not matter, depending on when the val might be initialised. (well if it is inlined, it isn't initialised -- its *eliminated* and replaced by its initialiser). To put this another way: the semantics of "val" at present, are specifically designed to allow the compiler to choose eager or lazy evaluation, which every it thinks is faster (or easier to implement .. :) If your val only depends on constants it cannot make a difference. If it depends on variables it may or may not make a difference, depending on when the val is used and when the var is changed. For example: var x = 1; fun f() { val a = x + 1; return a + 2 * a; } Here, even though "a" depends on x, it makes no difference how it is evaluated because the variable x cannot change during the lifetime of the function f, because functions aren't allowed to have side-effects. So I don't want to ban vals for depending on variables. [note this is just one example where you can prove, even with the current semantics, that the value of a val is deterministic, even if it depends on variables or dereferences pointers] > Do not allow a val to be initialized more than once or be used before it is > initialized. How do i stop it? Don't allow vals at all after a label? > Hmmm that seems like a bug. Shouldn't inlining create a copy of the > parameters? Nope. If the parameter is a var parameter, it is evaluated eagerly. But even that may not be enough. > I think inlining should not change behavior from the non-inlined version, > given that inlining is supposed to be a transparent optimization. In C++ it is supposed to be. In Felix it turns out to have actual semantics. I would bet it matters in C++ too. But then C++ isn't very good at optimisation. Just about the only thing equivalent in C++ is elision or introduction of copy constructors. Which means C++ is typically almost impossible to optimise. > If we are changing the way C bindings are done perhaps we could start to move > in another direction with it and use some kind of prefix to transition into > "C" land, like the way things were being done in "cgram.flxh": > > extern "C" { > int x; > } Yes, ideally this is horrible: fun f: int -> int = "HG" GAK! Its only a trick that assigning a string to a function name is interpreted as a C binding. val x = "1" really IS a string so if you want to bind C a new invention is required: const x : int = "1' there now its C again not a string. Arggg .. it works but its not really all that consistent. > There is a certain amount of flexibility with the C template system that is > there now, but cases where that flexiblity is used one continue to use the > code keyword: > > fun + : int * int -> int = "$1+$2"; > proc puts : string = "puts($1.c_str())" > > becomes: > > fun +(a:int, b:int) => code[int]"$1+$2"; > proc puts(s:string) = { code 'puts($1.c_str())'; } That's pretty ugly :) > > For now it might be fine to initially support the extern "C" syntax only for > constants (since they're losing their keyword) and add the rest later. As mentioned some time ago .. if you want to write some Scheme to decode C/C++ type specification, at least to get C/C++ typedef to work, that would be a start. extern "C" { .. } is universally useless until it can parse abstract types and type declarators. ALSO : be aware there are several possible meanings of "C code in Felix". You could just be emitting that code. OR you could be binding to something. Just saying "extern C" doesn't make it clear which you mean. There's a big difference between: header "void f(); "; and proc f: unit = "f();"; One emits C code which Felix ignores, the other binds to existing C code. There's THIRD option too: proc f() {} That's Felix code. That makes a binding to Felix AND emits C code. So also note: struct X { .. } Binding for Felix, emist C code. whereas cstruct X { .. } just creates a binding. Now again .. exactly what do you want extern "C" to do?? It is a binding only, or does it generate something? -- john skaller skal...@users.sourceforge.net http://felix-lang.org ------------------------------------------------------------------------------ Live Security Virtual Conference Exclusive live event will cover all the ways today's security and threat landscape has changed and how IT managers can respond. Discussions will include endpoint security, mobile security and the latest in malware threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/ _______________________________________________ Felix-language mailing list Felix-language@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/felix-language