Deprecating fromIntegral

Edward Kmett ekmett at gmail.com
Wed Aug 12 18:00:01 UTC 2020


On Mon, Aug 10, 2020 at 12:15 AM Bardur Arantsson <spam at scientician.net>
wrote:

> Why would a massive overhaul be necessary for deprecation? If that's the
> case then there's a deeper more serious underlying issue around
> deprecation, IMO.
>

Deprecation would at a minimum first require us to offer an alternative,
let that percolate through code that uses base and only then apply the
deprecation pragma under the 3 release policy. Otherwise there isn't a path
for users to write code that compiles without warnings without pragmas.

Keep in mind fromIntegral is a Haskell Report facing change, so it is the
sort of thing that the CLC tends to hold to a higher bar still. Changes to
it will per force invalidate a lot of training material. I'm not really
weighing in on if this is a good or a bad change with that, just that there
is a non-trivial amount of process to enacting it if the community does
decide to move forward. That isn't "a serious underlying issue", so much as
establishing a baseline of stability and usability.

I'm personally +1 on the *idea* that it'd be good to find a solution that
allows you to safely identify whether you want a coercion that can truncate
or not.

The issue re-raised is a good one. But I don't happen to like the solution
that was offered when the corpse of this issue was disinterred.

Now for where I think this proposal at least insofar as it references a
concrete plan of attack falls down, fromIntegral leans on toInteger and
fromInteger as a common intermediary. This allows O(n) instances where the
modules linked by Niklas would require O(n^2). But it is even worse than
that. As a library author I don't need to know your integral type to be
able to fromIntegral from mine to yours today, but I really would in a
world where that became unavailable. Both of the modules linked use
fundep-less multi-parameter typeclasses, which means type inference
for them is terrible, (and even in the single parameter type class case we
have, remember, with Num, defaulting can kick in, we're even farther
removed from such a safety net here) and instances will easily accidentally
overlap the moment you go to define something like From a (Forward a),
making this problem even worse for any non-trivial numeric type.

So in light of that I'm personally strongly -1 on the concrete set of
actions being proposed untless a decent API can be found that scales,
doesn't have those problems, and could be written into something like a
Haskell Report without dipping into language extensions we haven't
formalized in a Report.

That isn't to say there isn't some variant of a proposal that can't be
found, but I'm having a hard time satisfying all the constraints that a
Prelude facing change really should meet.

Now, that isn't to say something can't be done, for instance, weaker
compromises like providing a module in base with safer casts and the like
could perhaps use whatever language extensions were suited to the task, as
there you have a lot more freedom to use more modern Haskell.

But even there I'd still like to see a way that factors the O(n^2) cases
into O(n) and which doesn't block any decently polymorphic numeric types
behind overlapping instances and don't make type inference go to hell.

So, let's see if we can't find a proposal that doesn't violate the gauntlet
of constraints imposed above. Off the cuff:

Add a variant of fromInteger to Num that returns Maybe a.

class Num a where
   ...
   fromIntegerMaybe :: Integer -> Maybe a
   fromIntegerMaybe a = Just (fromInteger a)

Modify the existing instances of Num to implement this extra member, and
having it return Nothing if the Integer is out of bounds.

As a concrete point in the proposal design space, keep the existing
fromInteger/fromIntegral as a wrapping conversion. Why? It dodges the
Haskell Report change. Others might disagree. I'm just trying to offer fuel
for the debate that takes it in a productive direction.

fromIntegralMaybe :: (Integral a, Num b) => a -> Maybe b
fromIntegralMaybe = fromIntegerMaybe . toInteger

can now be used for safe conversions.

There are 3 target semantics one might reasonably want to see in their code:

1.) Wrapping (existing fromIntegral)
2.) throwing an exception on overflow (via some wrapping combinator that
just handles the above Maybe)
3.) Return Nothing so the error can be handled in pure code.

Each can be build on top of fromIntegral and fromIntegralMaybe.

Room for variation:

* fromIntegralMaybe could be switched to something like Integer -> Either
String a, which would let you give back an error message saying why you
didn't like the Integer.

* fromIntegralWrapped could be added explicitly, and then after a suitable
period fromIntegral could be deprecated, but this would require an annoying
dance where users would have to switch what they define, which is
non-trivial to the point of ner impossibility under the 3 release policy,
so I'd personally not go that way, but hey, it is not my call.

* Shorter names might be nice. fromIntegral is long enough that we get
mocked by other language communities. Adding more words to the combinator
name here compounds that issue.

I mention this because something along these lines would address the
substance of the issue here without inducing the horrible unusability that
I feel the concrete proposal offered here would create.

-Edward
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/libraries/attachments/20200812/dbbfc975/attachment.html>


More information about the Libraries mailing list