Complicated but interesting problem. I never quite realized how exactly Felix
gets rid of lvalues until now, and I like it.
As for getters/setters, I can agree they're not the best. Whenever I write C++
code, I just make all the attributes I'll likely need to access public. That
has the issue that getters and setters try to resolve where you can no longer
change some implementation details because you'll break everything.
Python and Objective C solve this with properties. Which I like.
But Felix doesn't have this problem since `x.y` is a function call anyway. Cool.
john skaller <skal...@users.sourceforge.net> wrote:
>Hi, I am still grappling with this problem:
>
>Felix has a rule for concrete data types, in particular products.
>We have quite a few product type: tuples, records, structs, arrays.
>
>Products are characterised by projection functions.
>Each product type has a different way of naming them.
>We like for a record type:
>
> typedef r_t = (p1:int, p2:int);
> val r : r_t = (p1=1, p2=2);
>
>to express projections like:
>
> r.p1
>
>in the OO tradition. Felix says p1 is an ordinary function thingo,
>and
>
> x . f
>
>means exactly the same as
>
> f x
>
>so we can also write:
>
> p1 r
>
>Consequently you can write this too:
>
> 1 (1,2) == 2
>
>because (1,2).1 == 2. Of course 1 isn't a projection but an integer,
>but Felix translates the application of an integer to a tuple to the
>application of the corresponding projection.
>
>So far so good. Now we also have this notation:
>
> p <- v;
>
>which means, store the value v into the location p.
>We require the type of p and v to be &T and T respectively for some T.
>
>So now if you have
>
> var x : int;
>
>you can assigned to x like:
>
> x = 1;
>
>but this is just sugar: Felix translates it to:
>
> &x <- 1;
>
>It is important to note here that & is NOT an operator. Rather
>
> &x
>
>is the real name of the variable, that it, it is the machine address
>of the associated storage. &x is a name. The syntax
>
> x
>
>is actually short hand for
>
> *(&x)
>
>Note we could have made "x" the name of the address, but to make Felix
>look like C we don't. Nevertheless there is something very important
>going
>on here:
>
> WE HAVE GOTTEN RID OF THE CONCEPT OF LVALUES
>
>[Except in C bindings where we still need them but lets gloss over that
>:]
>
>Now, in C you can write:
>
> s.x = 1;
>
>where s is of a struct type with a field x. In Felix this would
>translate to
>
> &(s.x) <- 1;
>
>which is nonsense!! Remember, & is not an operator. It is just part of
>the name of a variable. So this would be OK:
>
> (&s).x <-1
>
>But how do we make that work? Isn't the "x" here a projection?
>And don't projections apply to product types? But &s is not of a
>product
>type, it's a POINTER to a product.
>
>So the solution? Make projections apply to pointers to products as
>well!
>
>In particular, if
>
> p: P -> V
>
>then
>
> P: &P -> &V
>
>as well, in other words, if you apply a projection to a pointer to a
>product
>you get a pointer to the corresponding field.
>
>This in turn makes it possible to store a value into a PART of a
>product.
>
>In particular, this makes a GOLDEN RULE (at least for products):
>
> VALUES ARE IMMUTABLE
>
>If you want to store into a value, stick it into a variable!
>
> VARIABLES HOLD MUTABLE OBJECTS
>
>The key thing is ALL product values can be made mutable by simply
>sticking them into a variable, then using the required chain of
>projections
>on the variable's address:
>
> var x = (1,2);
> 1 &x <- 42;
>
>Yep. That works! It has to. The projection 1 applied to the tuple
>object x
>returns a pointer to the second component which can then be stored
>into.
>
>The symmetry is very nice!
>
>THE PROBLEM
>============
>
>This all works just great! For concrete data types. That is,
>one where the compiler knows about the projections.
>
>But for an ABSTRACT data type, it doesn't work automatically.
>
>Suppose you have an array abstraction A, and some value a
>and a method:
>
> a.get 1;
>
>to get the second component. So find we can write:
>
> fun apply (p:int, a:A) => a.get p;
>
>and NOW we can indeed write
>
> a.1
>
>because that means 1 a, and "apply" methods are a way to teach
>the compiler how to apply non-functions to values. We can even
>do this: provide a method:
>
> (&a).get 1; // returns a POINTER
>
>and then an apply function can be used again to allow this:
>
> (&a).1 <- 42;
>
>But there's a problem! That's not how a varray works.
>
>A varray is literally a pointer underneath. So it is intrinsically
>mutable. Like many mutable data structures, you don't need to
>store it in a variable to store into its parts.
>
>A varray acts like a value AND like an object.
>
>There's also a major safety problem. With a concrete data type
>
> var x = 1,2;
> var p = &x.1;
>
>we have a pointer interior to the tuple, which can hang about.
>In this case that's just fine, it cannot dangle, and it can only point
>into a specific variable.
>
>But that's not true for mutable data types. In particular for a varray,
>a pointer into one cannot dangle, but it certainly can point past
>the end of the used part of the array (just point to the last element,
>then pop it off the end with pop_back).
>
>It's much worse if the data structure is hiding a C++ one!
>
>It is essential to use a pointer to store a value.
>
>get/set methods DO NOT WORK.
>This is an OO stupidity. Assignment isn't the only mutator.
>Increment is another one. And if you have say, an array then
>
> set (a, index, value)
>
>looks fine until you consider that the value could be another product
>which a component that is another product... you then get a complete
>mess of sets and gets because you have to do a modification to
>a temporary and store it back (because a get returns a value ..).
>
>What works is get/ref: get a value, get a reference. But the reference
>must be transient (linearly typed, so it must only be used once, and
>immediately). Note that get/set DOES enforce this (by hiding the store
>address).
>
>So there's the conundrum. In particular in my recent code I had to
>write:
>
> set (a,index,v)
>
>even for a varray. The old notation
>
> a&.index <- v
>
>doesn't work because the &, function is never invoked because now
>the PARSER translates
>
> x*.y ==> (*x).y // which equals *(x.y)
> x&.y ==> (&x).y
>
>to enforce the interpretation above. The problem is i want both
>
> a . 1 // second component of varray a
> a . 1 <- 42; // assign 42 to first component
>
>to work, the first because that's how ordinary arrays work and the
>second because a is really a pointer already so we do NOT want
>to have to take the address of "a" to store into it.
>
>--
>john skaller
>skal...@users.sourceforge.net
>http://felix-lang.org
>
>
>
>--
>You received this message because you are subscribed to the Google
>Groups "Felix Language" group.
>To unsubscribe from this group and stop receiving emails from it, send
>an email to felix-language+unsubscr...@googlegroups.com.
>To post to this group, send email to felix-langu...@googlegroups.com.
>Visit this group at http://groups.google.com/group/felix-language.
>For more options, visit https://groups.google.com/d/optout.
--
Sent from my Android phone with K-9 Mail. Please excuse my brevity.
Check out my website: http://kirbyfan64.github.io/
------------------------------------------------------------------------------
Dive into the World of Parallel Programming The Go Parallel Website, sponsored
by Intel and developed in partnership with Slashdot Media, is your hub for all
things parallel software development, from weekly thought leadership blogs to
news, videos, case studies, tutorials and more. Take a look and join the
conversation now. http://goparallel.sourceforge.net/
_______________________________________________
Felix-language mailing list
Felix-language@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/felix-language