Exposed # kinded variables + polykinded Prelude classes?
Zemyla
zemyla at gmail.com
Sun Oct 27 13:47:09 UTC 2019
I'm wondering if there would be a benefit, if not to the average
programmer, then to the ones working on deeper/faster code, to allow some
of the # kinded types (mostly Int#, Word#, Char#, Float#, Double#) to be
used in Safe code, and to have typeclasses able to work with them.
For instance, the definition of Show would become:
class Show (a :: TYPE r) where
show :: a -> String
default show :: (r ~ 'LiftedRep) => a -> String
show x = showsPrec 0 x ""
showsPrec :: Int -> a -> ShowS
default showsPrec :: (r ~ 'LiftedRep) => Int -> a -> ShowS
showsPrec _ x s = show x ++ s
showList :: (r ~ 'LiftedRep) => [a] -> ShowS
showList ls s = showList__ shows ls s
The fact that the defaults only work when the type is a LiftedRep is a
nonissue, because there's only a finite number of non-lifted types we'd be
defining it for.
You could do the same with Eq, Ord, Num, Real, Integral, Fractional,
Floating, RealFrac, RealFloat, Semigroup, Monoid, Bits, FiniteBits, and
probably several others I can't think of right now. However, with the
functions that return pairs, you'd need a version that returns an unboxed
pair instead. Assuming you changed ReadPrec, you could even do the same
with Read:
newtype ReadP (a :: RuntimeRep r) = ReadP (forall b. (a -> R b) -> R b)
newtype ReadPrec (a :: RuntimeRep r) = ReadPrec (Int -> ReadP a)
IO, ST, and STM could be made polykinded the same way, and would open up
Storable. However, how to do a definition for Monad that works for
polykinded monads is an issue. I do know that RebindableSyntax handles it
easily when there's just one monad that can operate on multiple kinds,
though.
As for which # types could be exposed, I'm thinking that Char#, Int#,
Word#, Float#, Double#, and Proxy# wouldn't be able to break out of Safe
code. Int64# and Word64# would work as well, and for 64-bit machines would
just be type aliases for Int# and Word# respectively. For types which have
functions with undefined behavior for some arguments, you can just make
wrappers that check the arguments and error out for the bad values.
MutVar#, MVar#, TVar#, and StableName# don't open up any functions that
would be unsuitable for safe code, either. I'm pretty sure that Array# and
MutableArray# would also be safe, as long as all functions were
length-checked and threw errors instead of having undefined behavior.
As for why this would be a desirable thing? Mostly for the sake of
convenience and generality, I think. I find myself working with unboxed
values from time to time, and it's a pain to always remember to use (+#)
for Int# and plusWord# for Word#. In addition, having typeclasses that can
return unboxed values (like a hypothetical sized# :: Sized a => a -> Int#
vs sized :: Sized a => a -> Int) can improve the generated code when the
code using the typeclass doesn't get specialized.
The module to import these would have to explain the differences between #
kinded types and * kinded ones: # values aren't lazy; they can't be
top-level definitions; you can't use unboxed tuples or sums with GHCi; and
with a few exceptions, you can't place them in containers (you can't have
an [Int#], for instance).
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/libraries/attachments/20191027/00612299/attachment.html>
More information about the Libraries
mailing list