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

Carter Schonwald carter.schonwald at gmail.com
Mon Apr 21 03:12:15 UTC 2014


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


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/943b56d5/attachment-0001.html>


More information about the Libraries mailing list