F# and BitC have a lot of similarities. Both are functional-imperative
languages that have adopted eager evaluation. At first glance, the main
difference in the languages is that F# traces its origins to O'Caml while
BitC is more closely traced to Haskell (though ironically we started with
something more like an imperative ML). The other big difference, of course,
is that F# is a CLR language. On the one hand, this gives F# the ability to
integrate with some very large bodies of code in very useful ways, and
that's a big plus. On the other hand, it means that F# has adopted some
unfortunate compromises to its design because of some things that CLI can't
express.

In a lot of ways I think that Don may have made the right decision on that,
and given how minor the "missing" bits in CLI are, I think it's possible to
imagine that CLI may evolve to close the gaps. The questions that I want to
explore for a moment here are (a) how are BitC and F# different, and (b) is
the difference enough to justify a new language given the available options
*today*?

Some obvious differences:

1. F# targets CLR. BitC (mostly) targets native code, though we're clearly
going to need a run time loadable and expandable intermediate form.
2. I believe that BitC is more committed to static compilation. While I
certainly don't *object* to a JIT, and will probably implement one, the
language design goal is to be able to do a fully static compilation no
later than application install time. This "late compile" approach is
regrettably necessary if we are to support generic code in libraries. and
retain the ability to version our libraries.
3. BitC is going to push region and effect typing, which F# hasn't tackled
yet.
4. As a matter of design bias, BitC probably favors unboxing more strongly.
E.g. we will implement an unboxed union type.
5. BitC's adoption of literal kinds gives us a bunch of forms of type
genericity that can't be expressed in other languages
6. BitC *may* enable mutability inference - we still have an ongoing
discussion about this.
7. *Because* BitC is not limited by CLI, we will be able to preserve
genericity in places where F# cannot. Notably: we probably won't need the
distinction between late-bound and early-bound type variables, and we will
be able to preserve genericity in a reasonable fashion over (e.g.) basic
arithmetic operations and algorithms that are built from this.
8. BitC's type constraint system is first class (type classes) and
user-extensible (the type classes aren't "baked in".
9. I expect that we will actually maintain type rules for BitC. I've been
looking, and I can't find those for F# anywhere. I may or may not have time
to do a formal semantics; that's something I would welcome help on.
10. One can imagine doing a native-compiling implementation of F#. Given
the expressiveness limitations inherent in CLI, and the fact that these are
inherited by F#, I don't believe that we can write a truly high-performance
kernel in F#. Unfortunately, that differentiator becomes rapidly less
compelling as we move up the software stack. Things that are flatly
unthinkable in a kernel become both necessary and commonplace just one or
two layers above that.
11. Unless we proceed merrily down the dependent type rathole, BitC will
adopt much of the "kind classes" ideas of Habit. I need to have an offline
talk with Mark Jones before making any real decision about that.
12. BitC's mixfix mechanism is more powerful than F#'s closest equivalent,
and blending it with an F#-style expression quoting mechanism will make it
more so.
13. BitC places a more careful emphasis on the value, use, and support of
unboxed types.


Because of the instance resolution and instance coherence issues that
plague type classes, it's clear that the BitC approach to type classes
needs to evolve away from Haskell a bit. BitC needs to pay more attention
to module import hygiene. At the very least, this means that *importing* a
type class instance and opening it into the current instance resolution
environment need to be different operations. It also means that instance
resolution needs to be performed w.r.t. an explicitly manipulable instance
environment. We're going to have to break some new ground here.

I also think that it has to be asked whether type classes are actually
buying us all that much. Multivariable type classes have a bunch of issues,
and in practice all of the useful cases we have seen to date are ones that
are baked in to the compiler. If it turns out to be true that all of the
useful cases are single variable type classes, then the marginal benefit of
type classes is much reduced. It ends up being the ability to retrofit
operators to types after the fact. That's useful, but perhaps not
overwhelmingly compelling. *IF* that's where we end up, then I think we
need to consider the possibility that we should drop the notion of post-hoc
operator binding and settle for a simpler, purely lexical mechanism. One of
the under-appreciated advantages of operator methods is that they have a
preferred affiliation with the 'this type. This affiliation offers at least
some of the benefits of type functions without much of the icky mess.

That being said, there seem to be numerous examples in the Habit report
where two-variable type classes are demonstrably useful. I'm not [yet]
inclined to throw this particular baby out with the bath water.


To summarize, yes, I *still* think there is value in pursuing a new
language, though I think that value is less clear than it may have been
before F# emerged onto the scene. Politically, I think that a non-CLR
language has a much higher chance of adoption in the Linux world, and that
is not a small thing.


shap
_______________________________________________
bitc-dev mailing list
[email protected]
http://www.coyotos.org/mailman/listinfo/bitc-dev

Reply via email to