On Fri, Aug 17, 2012 at 10:41 AM, john skaller <
skal...@users.sourceforge.net> wrote:

>
> 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.
>
> >  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.
>

Hmmm, all this strict typing and it's trivial to create a dangling pointer.
 Oops!


> > 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).
>

Is that desirable, or accidental?

> 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.
>

Well, who exactly benefits from this unpredictable behavior?

IMO if a val depends on anything that can change, that part of the
calculation should be "snapshotted" at the point of initialisation, or the
val should be eagerly evaluated.  When in doubt, eagerly evaluate.
 Predictable behavior trumps performance optimization ... this is my own
value system, where compiler optimisations should never impact runtime
behavior in any externally visible manner.  That includes the decision to
lazily evaluate something.

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).
>

Even in this case you must be able to prove that the runtime behavior is
not noticably changed by decisions made by the optimiser.  Otherwise people
are going to get hit with some really weird bugs caused by this.  Sure, you
could argue that they "should" know that vals are evaluated lazily
*sometimes* and account for that, but if they don't, bad things will happen
and it'll be really confusing.

What's point in spending all this effort with a strict type system that
prevents type errors, and yet add some lazy evaluation system that
introduces unexpected semantic errors?


> > 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?
>

A lexical approach seems fine - if the val is used in a statement that
comes earlier than it's declaration, report an error "val x used before it
was defined". Same for vars.

When a val depends on a var, function call, or really anything besides a
constant or another var, you can create a temporary variable to hold the
value of that part of the expression at the moment the val is initialized
and use that temporary variable to calculate the value lazily (if need be).
 When in doubt, or if the logic to do this gets to complicated, evaluate
eagerly.



> > 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.
>

This information came as a suprise to me, so I did a test:

proc b(i:int)() = {
  // i += 1;
  println$ "b, i = "+i;
}
proc a() {
  var i = 0;
  bb := b(i);
  i = 10;
  bb;
}
a();
// prints b, i = 10

You complain about the dangers of downcasting in other languages, and yet
this doesn't flabbergast you?

It's a programming language boobytrap!  It's actually breaking the contract
of how parameters and closures are understood to work.  That is, parameters
are your own private immutable copy, and creating a closure of them
captures the value at the time of the original call.

Parameters are not supposed to behave this way unless you tell them to ...
this is ridiculous.  If you don't think this is a bug, you are insane.

> 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 :)
>

Does it need to be beautiful?  It's only needed in those rare cases where
there is custom syntax.


> >
> > 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.
>

In C++ the extern "C" is just a declaration (a binding) without emitting
any code.

extern "C" might not be right, maybe a simpler format like "cbind { ... }"
would be more consistent with the way things are elsewhere.

The innards of the cbind would be a sort of C-like DSL that was compatible
enough with C that you can copy/paste some things in and just delete
extraneous stuff, or replace int with !ints here and there.

It might support renaming and template-like syntaxes, too, I haven't
designed too deeply into it.
------------------------------------------------------------------------------
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

Reply via email to