Cforall

Cforall is a language design extending ISO C.

Cforall extends the C type-system using overloading, parametric polymorphism, and type generators. … [The] Cforall type system is based on parametric polymorphism, the ability to declare functions with type parameters, rather than an object-oriented type system.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

I like it!

Especially the labeled gotos, the arrays, and the new way to declare variables.

It seems though like C is going to be fragmented: we have C99, Cyclone and now Cforall. I wonder if that is going to be good for the language.

Many good ideas, but is it viable?

Many good ideas in these papers, especially the revised declarations. But I am not sold on the need for these. Most experienced C programmers routinely solve many of the problems CforAll addresses, (including parsing complex declarations, resolving the goto/break/continue destinations, and returning multiple values from a function (using structs). The question is whether the added convenience proposed by CforAll is worth the many changes in syntax. Hard to say at this point. But an interesting proposal!

It's Also Several Years Old...

...and as others have pointed out elsewhere, it sounds an awful lot like Cyclone, which you can download and use today.

Bertrand Meyer had an interesting argument on this.

His 20-year old paper, Genericity vs Inheritance deals with which is "better": Subtype polymorphism (which he calls "inheritance", although inheritance is one of many ways to implement subtyping), or parametrice polymorphism ("genericity").

"Better" is in quotes as there are many possible metrics to determining which is more useful, and it probably depends on context; Meyer chooses a few particulars to examine.

His conclusion? Subtype polymorphism is "better" because it can simulate generic polymorphism, although in a type-unsafe manner. The reverse, he claims, isn't always true--some things, such as heterogenous collections, are rather difficult to accomplish in a language with only generic polymorphism. However, Meyer also believes that a language with both types of polymoprhism is further enhanced over a language with only one; it is probably no accident that all the major statically-typed OO languages (C++, Java, C#, Scala) support both generics. Likewise for things like O'Caml which add subtyping to a functional core.

If you want a language with C-like syntax and semantics (manual memory allocation, the ability to abuse pointers left and right, etc.) and which supports parametric polymorphism... just use C++ and avoid typing the words "virtual" and "dynamic_cast". (Or deriving from any library classes which contain the word "virtual" in their definitions). Of course, the C++ template syntax remains the ugliest thing in existence :), but it works.

His conclusion?

Subtype polymorphism (which he calls "inheritance", although inheritance is one of many ways to implement subtyping)

But he is really comparing inheritance - this is important to note, because some of his main arguments have nothing to do whatsoever with subtyping (in particular, dynamic dispatch).

The fact that he does not properly distinguish here (which probably would be a bit much to expect from a '86 paper) implies that his conclusions have to be taken carefully.

His conclusion? Subtype polymorphism is "better" because it can simulate generic polymorphism, although in a type-unsafe manner.

Excuse some lecturing, but...

Stated this way the claim is a non-sequitur. Anything can certainly be simulated "in a type-unsafe manner". So you have to refine that statement to restrict type-unsafe operation to down-casts.

But then you still make at least two silent assumption: first, that there is a Top type ("Object" or whatever) in your subtyping hierarchy, which is not necessarily the case (e.g. C++). And second, that you actually have down-casts, which is not necessarily the case either (e.g. OCaml), and certainly not a defining property of subtyping.

Obviously, with Top and down-casts, you can make a program almost completely untyped, which brings us back to the first point. So you want to restrict the statement further to allowing only "certain" uses of down-casts. How to qualify these is non-obvious.

Now, even considering these important restrictions the statement is nonsensical, because "polymorphism" is a type system property, and it does not make the least sense to say you "simulate" a type system feature in a "type-unsafe" way.

So let's assume the statement actually is about what programs can be expressed. Then it might still be false. In particular, heterogeneous collections can be expressed with parametric polymorphism, using existential types.

Formally, this fairly theoretic paper essentially shows that parametric polymorphism can encode subsumption. The inverse certainly is not true. So on a more theoretic level, the statement has it exactly backwards.

Lecturing doesn't bother me :)

Good lectures teach you things. At any rate, I consider Meyer's conclusions to be interesting rather than conclusive--if for no other reason than the paper is 20 years old, and many advances in the theory have happened since then. Meyer's role as a language designer (and peddler of same) sometimes appears to taint his contributions as a theorist--that said, I consider OOSC still to be one of the seminal works on OOP--despite the drumbeat of Eiffel evangelism it contains.

On the other hand, there remains an interesting question: In my view, the defining characteristic of (typed) OO languages is potentially unrestricted subtyping and subsumption, however implemented. (By unrestricted; I refer to the fact that classes or what-have-you are typically not closed abstractions; another module or even another program may introduce a subclass which then can be substituted for the class, often in surprising ways.). Many authors in various contexts have expressed the opinion that this is a bad thing; Date and Darwen suggest limiting subtyping to strengthening of invariants; and the Cforall proposal seems to view a lack of subtyping as a good thing. Thoughts or papers which address this topic?

Many authors in various

Many authors in various contexts have expressed the opinion that this is a bad thing; Date and Darwen suggest limiting subtyping to strengthening of invariants; and the Cforall proposal seems to view a lack of subtyping as a good thing. Thoughts or papers which address this topic?

It can be, as it can leak authority. For instance, if you subclass a type, then introduce new authority in the subclass, then provide the reference to some hostile code which expects the superclass, the hostile code can cast it to the concrete type, and extract more authority than you had intended to grant, and this type of code is very hard to audit. As Dean Tribble states in the above message:

Capability Design Rule #1: All distinctions in authority are represented by different capabilities. (Note that I'm rephrasing on the fly here to not confuse the people that still believe that objects are something you point to as opposed to the emergent phenomena of having pointed.

These sorts of unintended subtyping side-effects are pervasive, so in general, I'm starting to lean away from subtyping (at least OO forms of it).

Actually, you can get top and downcasts in ML

This rather mind-blowing trick was shown to my be Steven Weeks. The way it's written here it's not threadsafe, but that can be fixed by adding a mutex.
module Univ = struct
 type t = { clear : unit -> unit;
            store : unit -> unit; }

 let embed () =
   let r = ref None in
   let inject a =
     { clear = (fun () -> r := None);
       store = (fun () -> r := Some a); }
   in
   let project u =
     u.store ();
     let res = !r in
     u.clear ();
     res
   in
     (inject,project)
end
You can then use the module as follows:
let (inject_float,project_float) = Univ.embed ()
let (inject_int,project_int) = Univ.embed ()

let heterogenous_list = [ inject_float 4.; inject_int 5 ]
let just_floats = (* a list containing just the float 4. *)
   List.filter_map project_float heterogenous_list
let just_ints = (* a list containing just the int 5 *)
   List.filter_map project_int heterogenous_list

Universal Type

That is a universal type. Also note the MLton wiki page on UniversalType.

open heterogeneous lists and downcasts in ML

Yaron Minsky showed the example of the open universal type in OCaml,
implemented using reference cells. Indeed, reference cells is one way
of obtaining open unions. There are other ways. First, in Ocaml, one
can use open polymorphic variants:

let lst1 = [`Int 1; `Float 2.0];;
 val lst1 : [> `Float of float | `Int of int ] list = [`Int 1; `Float 2.]
let lst2 = lst1 @ [`Char 'x'; `Int 3];;
 val lst2 : [> `Char of char | `Float of float | `Int of int ] list =
  [`Int 1; `Float 2.; `Char 'x'; `Int 3]

(* increment only integers in an HList *)
let rec incr = function
  | [] -> []
  | ((`Int x)::rest) -> `Int (x+1) :: incr rest
  | (y::rest) -> y :: incr rest;;
 val incr : ([> `Int of int ] as 'a) list -> 'a list = &ltfun>

let test = incr lst2;;
 val test : [> `Char of char | `Float of float | `Int of int ] list =
  [`Int 2; `Float 2.; `Char 'x'; `Int 4]

The above transcript (with ocaml toplevel replies slightly indented)
shows absolutely no type declarations. One may add new `tags' at any
time, and the function incr, for example, does not have to be changed
at all, as its type clearly indicates.

The most portable solution however is to use exceptions. The following
code is trivially portable to SML. The code is thread-safe by
construction. The code is too robust: one may add new prj/inj pairs
without any need to modify the existing functions.

module REPR(S: sig type t end) = struct
   exception E of S.t
   let inj x = E x
   let prj = function E x -> Some x | _ -> None
end;;

module REPR_INT   = REPR(struct type t = int end);;
module REPR_FLOAT = REPR(struct type t = float end);;

let lst1 = [REPR_INT.inj 1; REPR_FLOAT.inj 2.0];;

(* new REPR may be added at any time *)	
module REPR_CHAR = REPR(struct type t = char end);;
let lst2 = lst1 @ [REPR_CHAR.inj 'x'; REPR_INT.inj 3];;

let incr lst = 
   let inc1 v = match REPR_INT.prj v with
      | Some x -> REPR_INT.inj (x+1)
      | _      -> v
 in List.map inc1 lst;;

let test = incr lst2;;

Is there an actual download

Is there an actual download available?