'Making instance of Applicative

Still not a hundred percent shure how to make instances of the more complex types. Have this:

data CouldBe a = Is a | Lost deriving (Show, Ord) 

Made an instance of Functor, using Maybe as an example:

instance Functor CouldBe where 
  fmap f (Is x) = Is (f x) 
  fmap f Lost   = Lost 

For doing something like this:

tupleCouldBe :: CouldBe a -> CouldBe b -> CouldBe (a,b)
tupleCouldBe x y = (,) <$> x <*> y

CouldBe needs to be an instance of Applicative, but how would you go about that? Sure I can look it up and copy it, but I want to learn the process behind it and finally end up with the instance declaration of CouldBe.



Solution 1:[1]

You just write it out, following the types:

instance Applicative CouldBe where
   {- 
        Minimal complete definition:
          pure, ((<*>) | liftA2)

      pure :: a -> f a 
      pure :: a -> CouldBe a

      liftA2 :: (a -> b -> c) -> f a -> f b -> f c 
      liftA2 :: (a -> b -> c) -> CouldBe a -> CouldBe b -> CouldBe c 
   -}
    pure a = fa
        where
        fa = ....

    liftA2 abc fa fb = fc
        where
        fc = ....

According to

data CouldBe a = Is a | Lost

our toolset is

Is   :: a -> CouldBe a
Lost :: CouldBe a

but we can also use pattern matching, e.g.

couldBe   is   lost  (Is a)    = is a
couldBe   is   lost  (Lost)    = lost
couldBe :: ? -> ? -> CouldBe a -> b
couldBe :: ? -> b -> CouldBe a -> b
couldBe :: (a -> b) -> b -> CouldBe a -> b

So,

    -- pure :: a -> f a 
    pure :: a -> CouldBe a     

matches up with

    Is   :: a -> CouldBe a

so we define

    pure a = Is a

Then, for liftA2, we follow the data cases:

    -- liftA2 :: (a -> b -> c) -> f a -> f b -> f c 
    -- liftA2 :: (a -> b -> c) -> CouldBe a -> CouldBe b -> CouldBe c
    liftA2 abc Lost    _     = ...
    liftA2 abc  _     Lost   = ...
    liftA2 abc (Is a) (Is b) = fc
        where
        c = abc a b
        fc = ....     -- create an `f c` from `c`: 
                      -- do we have a `c -> CouldBe c` ?
                      -- do we have an `a -> CouldBe a` ? (it's the same type)

But in the first two cases we don't have an a or a b; so we have to come up with a CouldBe c out of nothing. We do have this tool in our toolset as well.

Having completed all the missing pieces, we can substitute the expressions directly into the definitions, eliminating all the unneeded interim values / variables.

Solution 2:[2]

With my new idiomatic package you can derive Applicative for sums.

{-# Language DataKinds                #-}
{-# Language DeriveGeneric            #-}
{-# Language DerivingVia              #-}
{-# Language StandaloneKindSignatures #-}

import Data.Kind
import GHC.Generics
import Generic.Applicative

type CouldBe :: Type -> Type
data CouldBe a = Is a | Lost
  deriving
  stock (Eq, Ord, Show, Generic1)

  -- > pure @CouldBe 10
  -- Is 10
  -- > liftA2 (+) (Is 1) Lost
  -- Lost
  -- > liftA2 (+) Lost (Is 10)
  -- Lost
  deriving (Functor, Applicative)
  via Idiomatically CouldBe '[LeftBias Terminal]

-- > tupleCouldBe Lost Lost
-- Lost
-- > tupleCouldBe (Is 1) Lost
-- Lost
-- > tupleCouldBe Lost (Is 20)
-- Lost
-- > tupleCouldBe (Is 1) (Is 20)
-- Is (1,20)
tupleCouldBe :: CouldBe a -> CouldBe b -> CouldBe (a, b)
tupleCouldBe = liftA2 (,)

Why does this work? Being left-biased means we choose the Is constructor to be the pure constructor.

This means we "defect" from that constructor, when lifting over different constructors.

Terminal describes how we transform any Applicative into Const mempty

data Terminal

instance (Applicative f, Monoid m) => Idiom Terminal f (Const m) where
  idiom :: f ~> Const m
  idiom = mempty

which in this case discards the argument of Is mapping to Lost.

Note there is no way to define a right-biased definition of CouldBe, since it would require an applicative morphism that produces an a out of nothing

  via Idiomatically CouldBe '[RightBias ..]
  idiom :: Const () ~> Identity
  idiom (Const ()) = Identity ??

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Iceland_jack