Reputation: 2205
import Data.Monoid
newtype Constant a b =
Constant { getConstant :: a }
deriving (Eq, Ord, Show)
instance Functor (Constant a) where
fmap _ (Constant a) = Constant a
instance (Monoid a) => Applicative (Constant a) where
pure x = Constant (mempty x )
_ <*> (Constant a) = Constant a
--:t (<*>)
--(<*>) :: Applicative f => f (a -> b) -> f a -> f b
--:t Constant (Sum 1)
--Constant (Sum 1) :: Num a => Constant (Sum a) b
--Constant (Sum 1) <*> Constant (Sum 2)
--Constant { getConstant = Sum { getSum = 2 }}
My question is why does the last statement type check?
I expected the left side of <*> to be of type f ( a -> b)
Where
f = Constant
(a -> b) = Sum 1?
There's nothing I can apply to (Sum 1) because it is fully applied, yet this statement compiles.
Upvotes: 2
Views: 130
Reputation: 13600
For Const
:
(<*>) :: Const c (a -> b) -> Const c a -> Const c b
Your input was a Const c (a -> b)
a Const c a
. You gave a Const c b
in return -- if x :: c
, then Const x :: Const c a
, for any a
. It can be Const c Int
, Const c Bool
, etc.
For example:
Const "Hello" :: Const String Int
Const "Hello" :: Const String Bool
Const "Hello" :: Const String Double
Const "Hello" :: Const String a
Const "Hello" :: Const String (a -> b)
Const "Hello" :: Const String b
all typecheck, because the last parameter can be anything -- "Hello"
only constrains the first parameter.
However, you should note that your Applicative
instance doesn't obey the Applicative laws.
u <*> pure y = pure ($ y) <*> u
in particular is violated:
Const "hello" <*> pure 4
== Const "hello" <*> Const ""
== Const ""
but
pure ($ 4) <*> Const "hello"
== Const "" <*> Const "hello"
== Const "hello"
Upvotes: 4
Reputation: 213308
The left side is f (a -> b)
. This is correct.
Remember the type here (let's make 1 :: Int
for simplicity):
Constant (Sum 1) :: forall a. Constant (Sum Int) a
I've added the explicit forall
. The type checker can unify a
with anything.
Try this one:
Constant (Sum 1) :: Constant (Sum Int) (a -> b)
Works fine.
Upvotes: 4
Reputation: 22403
Because you have two type variables here:
newtype Constant a b =
Constant { getConstant :: a }
deriving (Eq, Ord, Show)
But you only "store" values of the first type, and the second is a phantom.
But your Applicative
instance is only concerned with the second type variable that you have -- the b
, the elusive phantom.
That means that values like Constant (Sum 1)
can have any sort of thing as their second type variable -- it doesn't matter! You can write:
foo :: Constant Sum (a -> b)
foo = Constant (Sum 1)
bar :: Constant Sum String
bar = Constant (Sum 1)
baz :: Constant Sum (IO [Maybe Int])
baz = Constant (Sum 1)
Because you never actually need a value of the phantom type.
Thus when you write Constant (Sum 1) <*> Constant (Sum 2)
, the type checker infers the correct types:
let foo = Constant (Sum 1) :: Constant Sum (a -> b)
bar = Constant (Sum 2) :: Constant Sum a
in foo <*> bar
So the left-hand side of <*>
does have the type f (a -> b)
, but f
is Constant Sum
, not just Constant
.
Upvotes: 3