haskellari / strict

8 stars 7 forks source link

Applicative instances #25

Open int-index opened 3 years ago

int-index commented 3 years ago

In Data.Strict.Maybe, I see the following argument for omitting the Applicative instance:

-- Note that in contrast to the standard lazy 'L.Maybe' type, the strict
-- 'Maybe' type is not an applicative functor, and therefore also not a monad.
-- The problem is the /homomorphism/ law, which states that
--
--      @'pure' f '<*>' 'pure' x = 'pure' (f x)  -- must hold for all f@
--
-- This law does not hold for the expected applicative functor instance of
-- 'Maybe', as this instance does not satisfy @pure f \<*\> pure _|_ = pure (f
-- _|_)@ for @f = const@.

However, I don’t think it holds water, because by the same token Haskell doesn’t even have the State monad, see http://www.cse.chalmers.se/~nicsma/no-state-monad.html

Instead, I think the right thing to do is to acknowledge that type class laws typically hold only if we ignore bottom, and implement the usual Applicative and Monad instances for the strict Maybe (and other types in the library).

infinity0 commented 3 years ago

Sounds good to me, does @phadej have an opinion? A PR would be very welcome.

phadej commented 3 years ago

I do see a benefit of having functions with S.Maybe a -> (a -> S.Maybe b) -> S.Maybe b signature. And there is an obvious way. However I'm unsure whether it's wise to use strict Maybe as control structure, yet it's probably handy to be able to try to swap lazy Maybe to strict Maybe, as an experiment.

A PR adding the same instances https://hackage.haskell.org/package/base-4.14.1.0/docs/Data-Maybe.html#t:Maybe has is fine. As long as there is disclaimer which would allow us to close issues re. bottoms with "you have been warned".

phadej commented 3 years ago

TL;DR I'm quite sure there would be issues like "I swapped lazy maybe to strict maybe, and my program started to crash instead of running faster".

infinity0 commented 3 years ago

That would only affect new users that attempt to take advantage of this feature, though - it doesn't cause any backwards-compat issues as the instance doesn't exist currently so nobody would be using it.

phadej commented 3 years ago

Yes, I it won't break any existing code. I didn't mean that.

phadej commented 3 years ago

Let us wait for a patch. I'd expect an instances come with a documentation of their partial lawfulness.

tomjaguarpaw commented 2 years ago

I'm confused by this because there is a Functor instance, but I don't believe Data.Strict.Maybe is technically a Functor:

> let f = const ()
> let g = const undefined
> fmap (f . g) (Data.Strict.Just ())
Just ()
> (fmap f . fmap g) (Data.Strict.Just ())
*** Exception: Prelude.undefined

Or did I misinterpret something?

phadej commented 2 years ago

@tomjaguarpaw we agreed to add instances. We are waiting for a patch.