'Implementing applicative functor for custom data type with two identical type classes
I'm trying to make Twice a member of applicative, but I am getting "Conflicting definitions for `a'" in the functor implementation: Also I'm not sure how to implement the <*> correctly :/
data Twice a = Twice a a deriving (Show)
instance Functor Twice where
fmap f (Twice a a) = Twice (f a) (f a)
instance Applicative Twice where
pure x = (Twice x) x
Twice f f <*> Twice x x = Twice ((f x) (f x))
Solution 1:[1]
The errors are more syntactical than semantical I guess. In your definition:
Twice f f <*> Twice x x = Twice ((f x) (f x))
the same in the Functor
defintion:
fmap f (Twice a a) = Twice (f a) (f a)
You write two f
s and two x
's in the head. Haskell does not allows that (Prolog does, but even then it is probably not what you mean).
Furthermore in the body you write:
((f x) (f x))
This means that you would make a function call with f x
the function, and f x
the argument, which is again probably not what you intended.
We can fix it syntactically to:
instance Functor Twice where
fmap f (Twice a b) = Twice (f a) (f b)
instance Applicative Twice where
pure x = Twice x x
Twice f g <*> Twice x y = Twice (f x) (g y)
Note that you still need to prove that this is a valid Functor
and Applicative
instance by taking the constraints documented for these typeclasses into account.
Solution 2:[2]
Here are the problems and their corrections:
instance Functor Twice where
-- fmap f (Twice a a) = Twice (f a) (f a)
-- ^^^ you define `a` twice here. Replace this with:
fmap f (Twice a b) = Twice (f a) (f b)
instance Applicative Twice where
pure x = Twice x x -- You don't need brackets here so I removed them.
-- Twice f f <*> Twice x x = Twice ((f x) (f x))
-- ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^
-- You define f and x twice.
-- You are also misusing brackets - this is not how to apply functions.
-- A corrected version:
Twice f1 f2 <*> Twice x1 x2 = Twice (f1 x1) (f2 x2)
Firstly, if you don't give seperate arguments seperate names, this will cause an error. Imagine me writing f a a = a + 6
, and then asking you to evaluate f 1 2
.
Secondly, you misunderstand how Haskell functions are applied. Let me make this clear: in most languages you write f(a,b,c)
, but in Haskell, you write f a b c
. If you write f (a b c)
, this is read as f(a(b,c))
.
Solution 3:[3]
The confusion stems from using the same name for data and type; a common and accepted, even recommended practice for all but the very novice Haskellers.
For now, you can use different names, to reduce the cognitive load while learning and getting accustomed to Haskell's data type declarations:
data Twice a = MkTwice a a deriving (Show) -- 'Mk' is for "make"
-- Twice a -- a type
-- MkTwice :: a -> a -> Twice a -- data constructor
instance Functor Twice where
-- fmap (f :: a -> b) :: Twice a -> Twice b
fmap f (MkTwice x y) = MkTwice (f x) (f y) -- transform both fields
instance Applicative Twice where
pure x = MkTwice x x -- create two fields from one value
MkTwice f g <*> MkTwice x y = MkTwice (f x) (g y) -- combine both fields
Twice a
is a type, with a
a type variable.
MkTwice
is the data constructor for this type. It makes values of type Twice a
, from two values of type a
(say, x :: a
and y :: a
). Values appear to the right of =
in Haskell definitions (roughly).
It is also used in patterns for values of the type Twice a
. Patterns appear to the left of =
in Haskell definitions. There can be no duplicate pattern variables in Haskell's patterns. Each pattern variable stands for some value.
Each type variable stands for a type.
I bet you wouldn't make those mistakes if you were using the different names for the type and data constructor.
Solution 4:[4]
It can be derived with base 4.17.0.0, using Generically1
from GHC.Generics
:
{-# Language DerivingStrategies #-}
{-# Language DerivingVia #-}
import GHC.Generics (Generic1, Generically1(..))
data Twice a = Twice a a
deriving
stock (Show, Generic1)
deriving (Functor, Applicative)
via Generically1 Twice
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 | Daniel Wagner |
Solution 3 | |
Solution 4 | Iceland_jack |