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

John Lato jwlato at gmail.com
Mon Apr 21 03:46:39 UTC 2014


On Sun, Apr 20, 2014 at 8:12 PM, Carter Schonwald <
carter.schonwald at gmail.com> wrote:

> doesn't no instance vs error "foo" defaults both only trigger an error at
> runtime?
>

Yes, but with no instance you get better error messages, since it will
specify the offending type.  It also triggers a compile-time warning for
the library author, which is the real reason I think it's better.


>
>
> On Sun, Apr 20, 2014 at 10:49 PM, John Lato <jwlato at gmail.com> wrote:
>
>> On Sun, Apr 20, 2014 at 6:37 PM, Edward Kmett <ekmett at gmail.com> wrote:
>>
>>>
>>>
>>> Sent from my iPad
>>>
>>> On Apr 20, 2014, at 8:49 PM, John Lato <jwlato at gmail.com> wrote:
>>>
>>> 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.
>>>
>>>
>>>
>>> I think reasonable people can disagree and come down on either side of
>>> this issue.
>>>
>>
>> Sure.
>>
>>>
>>>
>>> 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*.
>>>
>>>
>>> I'm coming at this from the perspective that I should never be worse off
>>> having called expm1 than I would be in the world before it existed. Your
>>> way I just crash making me much worse off. I'm asking for extra bits of
>>> precision if the type I'm using can offer them. Nothing more.
>>>
>>
>> And I'm saying that if you ask for extra bits of precision, and the type
>> could offer them but doesn't, a crash is better than not giving extra
>> precision.  FP algorithms can be highly sensitive to precision, and it's a
>> good bet that if somebody is asking for specialized behavior there's a
>> reason why.  I think it's better to fail loudly and point a finger than to
>> fail silently.
>>
>> If I'm using log1p because my algorithm requires that precision,
>> replacing log1p with log (1+x) is not a safe transformation.  But that's
>> what your default instance does.
>>
>>
>>>
>>>
>>>> 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.
>>>
>>>
>>> If expm1 crashes I'm back to duplicating code and this proposal does
>>> nothing to improve my life over doing exactly what I can do now, but do not
>>> wish to continue doing, which is maintain a separate code path entirely
>>> with no effective way to transparently switch when greater precision is
>>> available.
>>>
>>
>> You wouldn't duplicate code.  You would go to the author of the type that
>> doesn't implement that function and ask them to implement it.  Isn't that
>> exactly what you said you wanted?  To get the function you mean and know
>> who to talk to in order to get it implemented?  Your proposal doesn't even
>> provide the function you mean!
>>
>> Also, I note you neglected to answer my rhetorical question :)
>> Introducing bugs whereby functions don't behave according to standards is
>> really, really poor design.  I don't see how saving some library authors
>> some work is worth that cost to users.
>>
>>
>>>
>>>
>>>> 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.
>>>
>>>
>>> And in exchange, ever library author even the vast majority of whom will
>>> never have a user who cares about this feature needs to care or get a
>>> warning or we silently cover up a real error that should be a warning
>>> behind their back, and no user can trust that it is safe to call the
>>> function.
>>>
>>
>> I think just providing implementations for Float/Double will cover >90%
>> of use cases and convince users that it's safe to call the function.  GND
>> will probably cover another 5-8% of uses.  I think it's a quite small tail
>> we're discussing here.  And I'll even admit that, since for *some* types
>> log1p x = log (1+x) will work correctly, it's an even smaller group of
>> users I'm concerned about.  But I still think it's an unreasonable price to
>> pay.
>>
>>
>>>
>>> I don't want to duplicate all my code and I don't want to randomly
>>> crash, I want to eke out a few bits of mantissa if they are available and
>>> not be worse off than I am today for that privilege.
>>>
>>
>> If you wrote code that crashed under an error default, that same code
>> would be worse off than it is today because users would expect that it does
>> the right thing and it fails silently.
>>
>> The point of these functions isn't just to provide convenient algebraic
>> shortcuts.  It's to provide extra precision for numerically-sensitive
>> computations.  If users don't know about it, they'll just use exp/log and
>> be ok.  But users who require that extra precision should either get it or
>> be informed that it's not available.  Ideally by a compile-time error, but
>> I don't know a reasonable way to implement that, so a run-time error is the
>> next best thing.
>>
>> I simply do not understand why you think it's appropriate to provide a
>> function that explicitly doesn't do what it's supposed to.  But we're
>> unlikely to sway each other here without further input, so I guess
>>
>> +0.1 to the OP
>> +0.5 for error defaults
>> +1 for no defaults
>>
>> John L.
>>
>>
>>
>>
>>>
>>> -Edward
>>>
>>>
>>>  -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
>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
>> _______________________________________________
>> 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/ea16e340/attachment-0001.html>


More information about the Libraries mailing list