Proposal: Add log1p and expm1 to GHC.Float.Floating

John Lato jwlato at gmail.com
Mon Apr 21 00:49:09 UTC 2014


On Sun, Apr 20, 2014 at 5:20 PM, Edward Kmett <ekmett at gmail.com> wrote:

> The proposal is worded the way it is to get a strict monotonic improvement
> over the status quo.
>
> With them in the class it becomes possible to get the instances fixed.
> With them outside of the class in some needless extra hair-splitting class
> added on later like we have to use today, then someone who would otherwise
> just use them is needlessly hoist on the dilemma of using a more
> restrictive class and just accepting the fact that they can't work with
> third party numeric types for the most part at all, or reverting to the
> poor version of the numerics to widen their audience.
>
> This leads to the equivalent of needless divisions between 'traverse' vs.
> 'mapM' forever.
>

I don't think a separate class is ideal, I just think it's better than your
original proposal.


>
> With defaults you are never worse off than you are today, but defaults you
> *always* have to worry about whether you should use them.
>

This isn't correct.  Today, I don't have exp1m.  I have no expectation that
any type will use an appropriate fused algorithm, nor that I won't lose
precision.  If exp1m is defined with defaults as proposed, and I use exp1m
for a type that doesn't define it, I may lose precision, leading to
compounding errors in my code, *even though I used the right function*.


> Let's look at it another way.
>
> By putting in defaults the costs of the proposal are borne by the people
> who want to use the new feature.
>

Yes.  When users use the new feature with types that don't implement it and
get an incorrect answer, there will certainly be high costs involved.

Let's look at it another way.

Do you want to track down bugs due to exp1m not implementing the
appropriate fused algorithm?

Or alternatively, do you want to implement a default function that's not
even guaranteed to work as documented?  With a silent failure mode?  So
library authors don't have to fix up their libraries?  That seems very
wrong to me.


> Moreover, if we should decide to adopt wren's half-suggestion of
> continuing to expand support for other numerical primitives that have broad
> support we could do so without  great deal of fanfare, and the handful of
> people who actually do numeric computation can talk to the handful of
> people who write numeric instances that high up the foodchain to get the
> important ones fixed in packages like vector-space, linear, diagrams, etc.
>

> Without defaults everyone who ever wrote a Floating instance by hand would
> need to know about log1p or wren's log1mexp and they would be forced into
> using CPP in their code to work around a feature they don't care about and
> if they couldn't be bothered then the user who wanted a bit of extra
> precision now just starts crashing. The risk averse would simply take the
> path with worse precision or get shoved back into the world of code
> duplication and 'mapM' vs 'traverse'.
>

Are you arguing for a separate class?  Because that's what it sounds like.
 Besides, if you aren't familiar with precision issues you have no business
writing a Floating instance by hand that does anything more than lift over
more fundamental types.

I think it's better that exp1m crash than that it not give extra precision,
since the extra precision is the whole point of the function.  When I call
a function, I want to get the function I mean.

I'm not actually arguing for a separate class.  I think these belong in
Floating as well.  I'm just arguing against a default that doesn't work as
specified.


>
> I know for me personally it would force me to double the amount of numeric
> code I write, just to maximize my audience. I really don't want to go
> there. I just want to be able to call the function I mean, and to be able
> to talk to the right people to make it do the right thing.
>

exp1m = error "Go bug some library author to implement exp1m"

would accomplish that even more efficiently, since it will directly point
users to the right people.

>
>
-Edward
>
>
> On Sun, Apr 20, 2014 at 7:32 PM, John Lato <jwlato at gmail.com> wrote:
>
>> On Sat, Apr 19, 2014 at 5:49 AM, Edward Kmett <ekmett at gmail.com> wrote:
>>
>>> With the defaults the code is never worse than it is forced to be right
>>> now and users do not need to create CPP blocked code to work around this
>>> addition.
>>>
>>
>> I usually like defaults, and avoiding CPP is good, however with the
>> defaults users will expect better code than they get.  We aren't doing
>> anyone any favors by introducing the possibility of silent floating-point
>> precision loss from 'exp1m'.  An "error" default would be better.
>>
>> Besides, the code would be worse than it's forced to be now.  At least
>> now users who care about this run headlong into the issue.  If we provide
>> exp1m and log1p, users who use those functions should get the advertised
>> behavior, not loss of precision (I realize not all types would lose
>> precision, but some will).
>>
>>
>>>
>>> Without the defaults this becomes a much bigger request, as I'd be
>>> asking _every_ author of Floating to add CPP to their packages for a
>>> feature they never heard of and probably will never use, and in that
>>> situation we'd have to export it from Prelude.
>>>
>>
>> It's perfectly fine to leave some methods blank; IMHO the resulting
>> run-time error is better than an incorrect default.  Plus, it's useful for
>> library authors to know that the class has changed; if a default is
>> provided everything will build properly and there is no compile-time
>> indication that library authors should adjust their code.
>>
>> Originally I was +1 for everything except the defaults, but I'm
>> reconsidering.  If this is something that most Floating instance authors
>> don't know about and probably won't ever use, do these functions really
>> belong in that class?  Why not make a separate class for fused algorithms?
>>
>> John L.
>>
>>
>>
>>>
>>> -Edward
>>>
>>>
>>> On Sat, Apr 19, 2014 at 5:42 AM, Scott Turner <2haskell at pkturner.org>wrote:
>>>
>>>>  On 2014-04-17 15:08, Edward Kmett wrote:
>>>>
>>>>  On Thu, Apr 17, 2014 at 2:48 PM, Henning Thielemann <
>>>> schlepptop at henning-thielemann.de> wrote:
>>>>
>>>>> I think one should add default implementations. They don't have an
>>>>> numerical advantage but they save programmers from code breakage.
>>>>
>>>>
>>>>  I included the default definitions in code snippet in the proposal,
>>>> so user code that remains unaware of them would be unaffected, while
>>>> packages like compensated, or a wrapper around libqd could implement them
>>>> as needed.
>>>>
>>>>  expm1 :: Floating a => a -> a
>>>> expm1 x = exp x - 1
>>>>
>>>>  log1p :: Floating a => a -> a
>>>> log1p x = log (1 + x)
>>>>
>>>> On the contrary, code that explicitly uses these functions is likely to
>>>> need the precision. Defaults would cause subtle breakage.
>>>>
>>>> -- Scott
>>>>
>>>
>>>
>>> _______________________________________________
>>> Libraries mailing list
>>> Libraries at haskell.org
>>> http://www.haskell.org/mailman/listinfo/libraries
>>>
>>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/libraries/attachments/20140420/7ebc1128/attachment.html>


More information about the Libraries mailing list