It does handle the diamond inheritance problem as best as can be expected—the renaming feature is quite nice. Though related, this isn’t what I’m concerned with. AFAICT, it really doesn’t handle it in a completely general way. (Given the type-system you can drive a bus through (covariant vs contravariant arguments), I prefer Sather, though the renaming feature there is more persnickety—harder to use in some common cases.)
Consider a lattice). It is a semilattice in two separate dual ways, with the join operation, and the meet operation. If we have generalized semi-lattice code, and we want to pass it a lattice, which one should be used? How about if we want to use the other one?
In practice, we can call these a join-semilattice, and a meet-semilattice, have our function defined on one, and create a dual view function or object wrapper to use the meet-semilattice instead. But, of course, a given set of objects could be a lattice in multiple ways, or implement a monad in multiple ways, or …
There is a math abstraction called a monoid, for an associative operator with identity. Haskell has a corresponding typeclass, with such things as lists as instances, with catenation as the operator, and the empty list as identity. I don’t have the time and energy to give examples, but having this as an abstraction is actually useful for writing generic code.
So, suppose we want to make Integers an instance. After all, (+, 0) is a perfectly good monoid. On the other hand, so is (*, 1). Haskell does not let you make a type an instance of a typeclass in two separate ways. Their is no natural duality here we can take advantage of (as we could with the lattice example.) The consensus in the community has been to not make Integer a monoid, but rather to provide newtypes Product and Sum that are explicitly the same representation as Integer, with thus trivial conversion costs. There is also a newtype for dual monoids, formalizing a particular duality idea similar to the lattice case (this switches left and right—monoids need not be commutative, as the list example should show). There are also ones that label bools as using the operation and or or; this is actually a case of the lattice duality above.
For this simple case, it’d be easy enough to just explicitly pass in the operation. But for more complicated typeclasses, we can bundle a whole lump of operations in a similar manner.
I’m not entirely happy with this either. If you’re only using one of the interfaces, then that wrapper is damn annoying. Thankfully, e.g.Sum Integer can also be made an instance of Num, so that you can continue to use * for multiplication, + for addition, and so forth.
I don’t think Sather is a viable language at this point, unfortunately.
Yes, C is useful for that, though c—and LLVM are providing new paths as well.
I personally think C will stick around for a while because getting it running on a given architecture provides a “good enough” ABI that is likely to be stable enough that HLLs FFIs can depend on it.
It does handle the diamond inheritance problem as best as can be expected—the renaming feature is quite nice. Though related, this isn’t what I’m concerned with. AFAICT, it really doesn’t handle it in a completely general way. (Given the type-system you can drive a bus through (covariant vs contravariant arguments), I prefer Sather, though the renaming feature there is more persnickety—harder to use in some common cases.)
Consider a lattice). It is a semilattice in two separate dual ways, with the join operation, and the meet operation. If we have generalized semi-lattice code, and we want to pass it a lattice, which one should be used? How about if we want to use the other one?
In practice, we can call these a join-semilattice, and a meet-semilattice, have our function defined on one, and create a dual view function or object wrapper to use the meet-semilattice instead. But, of course, a given set of objects could be a lattice in multiple ways, or implement a monad in multiple ways, or …
There is a math abstraction called a monoid, for an associative operator with identity. Haskell has a corresponding typeclass, with such things as lists as instances, with catenation as the operator, and the empty list as identity. I don’t have the time and energy to give examples, but having this as an abstraction is actually useful for writing generic code.
So, suppose we want to make Integers an instance. After all, (+, 0) is a perfectly good monoid. On the other hand, so is (*, 1). Haskell does not let you make a type an instance of a typeclass in two separate ways. Their is no natural duality here we can take advantage of (as we could with the lattice example.) The consensus in the community has been to not make Integer a monoid, but rather to provide
newtype
sProduct
andSum
that are explicitly the same representation as Integer, with thus trivial conversion costs. There is also anewtype
for dual monoids, formalizing a particular duality idea similar to the lattice case (this switches left and right—monoids need not be commutative, as the list example should show). There are also ones that label bools as using the operationand
oror
; this is actually a case of the lattice duality above.For this simple case, it’d be easy enough to just explicitly pass in the operation. But for more complicated typeclasses, we can bundle a whole lump of operations in a similar manner.
I’m not entirely happy with this either. If you’re only using one of the interfaces, then that wrapper is damn annoying. Thankfully, e.g.
Sum Integer
can also be made an instance ofNum
, so that you can continue to use * for multiplication, + for addition, and so forth.Sather looks interesting but I haven’t taken the time to explore it. (And yes, covariance vs contravariance is a tricky one.)
Both these languages also demonstrate the real (everyday) use for C… you compile your actual code into it.
I don’t think Sather is a viable language at this point, unfortunately.
Yes, C is useful for that, though c—and LLVM are providing new paths as well.
I personally think C will stick around for a while because getting it running on a given architecture provides a “good enough” ABI that is likely to be stable enough that HLLs FFIs can depend on it.