Here's an explanation I dreamed up that may make more sense. When you write:
val x = 1; in Felix this is the same as #define x 1 in C, except it is typed. It's the name of an expression. In particular if you write: var a = 1; var b = 2; val x = a + b; ++a; var c = x; it's similar to C again: int a = 1; int b = 2; #define x a + b ++a; int c = x; // expands to a + b which == 4 When you write var a = 1; var b = 2; fun f (x:int) => x + b; it's the same as in C except its typed properly: #define f(x) x + b so ++a; ++b; var y = f a; is the same as int y = f a; // 2 + 3 which = 5 In Felix when you write: fun f(x:int) = { var y = x + 1; return y; } the variable y is "qualified" on each expansion, unlike C, so it isn't duplicated. Felix actually adds a unique counter to the name to do this eg, x_123472 or something :) And if you write fun f (var x : int) => x + b; this is just short hand for fun f(x': int) = { var x = x'; return x + b; } Since var's are executed immediately and the var is at the top of the function, this has the effect of "eager evaluation" instead of the usual substitution (which is lazy evaluation). So Felix vals and functions are really macros. If you're able to understand C macros you can understand Felix vals and functions. Well there's a little white lie here: sometimes Felix will use eager evaluation anyhow. Also there's some magic: you can use a macro as a proper function, that is, you can create a function value or closure. For a C binding: fun f : int -> int = "$1+1"; always uses substitution so Felix actually generates a wrapper: fun wrapper (var x:int) : int => f x; In C++ this is a class: class wrapper { int x; public: int apply (int x) { return f (x); } }; and a closure is just an object: wrapper closure = wrapper (); int y = closure.apply(x); A function accepting a closure just takes a pointer to the class object. Its actually a bit more tricky of course! It's really: class int_to_int { public: virtual int apply(int) = 0; }; class wrapper : public int_to_int { ... } int_to_int *closure = new wrapper(); We do this, so the upcast to the public abstract base hides the function and leaves only a pointer to the type. This means a higher order function will work with any function of the required type as an argument. IN REALITY Felix also generates actual C functions, so there are THREE distinct possible implementations: macro like, C function like or C++ class like. Felix tries them in that order, because that's the order of fastest performance. The evaluation rules are specifically defined to allow this, that is, they just describe what the implementation actually does. The reason for this approach can be seen by examining Haskell or Ocaml, where the stricter semantics make it very hard to generate performant code. Haskell requires the *compiler* to prove a function is strict before using eager evaluation. Felix *assumes* the function is strict by default, and provides a way to override this. Consequently Felix generates much faster code. Performance matters in two cases in particular: inner loops, and high level optimisations. It's particular important when the value being manipulated are matrices or some other very large structure where even a single operation saved has a significant performance impact. My belief is that most of the time it doesn't matter much so indeterminate evaluation is a good default. It's also important to note that whilst the semantics of purely functional code is reasonably well understood, NO ONE has any idea about procedural programming. There isn't any good theory for it, just lots of broken theories. -- john skaller skal...@users.sourceforge.net http://felix-lang.org ------------------------------------------------------------------------------ This SF.net email is sponsored by Windows: Build for Windows Store. http://p.sf.net/sfu/windows-dev2dev _______________________________________________ Felix-language mailing list Felix-language@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/felix-language