Haskell Either Applicative where the Left is a Monoid
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
3
down vote
favorite
I'm currently learning Haskell and are starting to understand applicatives.
I wanted an Either Applicative where the left side is a monoid, so I created this as a learning exercise:
module EitherLeftMonoid where
data Error = InvalidName | UnderAge deriving (Eq, Show)
data Person = Person name :: String, age :: Int deriving (Eq, Ord, Show)
data EitherLeftMonoid a b = Lefty a
| Righty b
deriving (Eq, Show)
instance Monoid a => Functor (EitherLeftMonoid a) where
fmap _ (Lefty a) = Lefty a
fmap f (Righty b) = Righty (f b)
instance Monoid a => Applicative (EitherLeftMonoid a) where
pure = Righty
Lefty a <*> Lefty a' = Lefty (a `mappend` a')
Lefty a <*> _ = Lefty a
_ <*> (Lefty a) = Lefty a
Righty f <*> Righty x = Righty (f x)
mkName :: String -> EitherLeftMonoid [Error] String
mkName "" = Lefty [InvalidName]
mkName s = Righty s
mkAge :: Int -> EitherLeftMonoid [Error] Int
mkAge n | n < 13 = Lefty [UnderAge]
| otherwise = Righty n
mkPerson :: String -> Int -> EitherLeftMonoid [Error] Person
mkPerson n a = Person <$> mkName n <*> mkAge a
Output:
Prelude> mkPerson "" 12
Lefty [InvalidName,UnderAge]
Prelude> mkPerson "" 13
Lefty [InvalidName]
Prelude> mkPerson "Gerd" 13
Righty (Person name = "Gerd", age = 13)
What could I have done different to make the code better?
haskell
add a comment |Â
up vote
3
down vote
favorite
I'm currently learning Haskell and are starting to understand applicatives.
I wanted an Either Applicative where the left side is a monoid, so I created this as a learning exercise:
module EitherLeftMonoid where
data Error = InvalidName | UnderAge deriving (Eq, Show)
data Person = Person name :: String, age :: Int deriving (Eq, Ord, Show)
data EitherLeftMonoid a b = Lefty a
| Righty b
deriving (Eq, Show)
instance Monoid a => Functor (EitherLeftMonoid a) where
fmap _ (Lefty a) = Lefty a
fmap f (Righty b) = Righty (f b)
instance Monoid a => Applicative (EitherLeftMonoid a) where
pure = Righty
Lefty a <*> Lefty a' = Lefty (a `mappend` a')
Lefty a <*> _ = Lefty a
_ <*> (Lefty a) = Lefty a
Righty f <*> Righty x = Righty (f x)
mkName :: String -> EitherLeftMonoid [Error] String
mkName "" = Lefty [InvalidName]
mkName s = Righty s
mkAge :: Int -> EitherLeftMonoid [Error] Int
mkAge n | n < 13 = Lefty [UnderAge]
| otherwise = Righty n
mkPerson :: String -> Int -> EitherLeftMonoid [Error] Person
mkPerson n a = Person <$> mkName n <*> mkAge a
Output:
Prelude> mkPerson "" 12
Lefty [InvalidName,UnderAge]
Prelude> mkPerson "" 13
Lefty [InvalidName]
Prelude> mkPerson "Gerd" 13
Righty (Person name = "Gerd", age = 13)
What could I have done different to make the code better?
haskell
add a comment |Â
up vote
3
down vote
favorite
up vote
3
down vote
favorite
I'm currently learning Haskell and are starting to understand applicatives.
I wanted an Either Applicative where the left side is a monoid, so I created this as a learning exercise:
module EitherLeftMonoid where
data Error = InvalidName | UnderAge deriving (Eq, Show)
data Person = Person name :: String, age :: Int deriving (Eq, Ord, Show)
data EitherLeftMonoid a b = Lefty a
| Righty b
deriving (Eq, Show)
instance Monoid a => Functor (EitherLeftMonoid a) where
fmap _ (Lefty a) = Lefty a
fmap f (Righty b) = Righty (f b)
instance Monoid a => Applicative (EitherLeftMonoid a) where
pure = Righty
Lefty a <*> Lefty a' = Lefty (a `mappend` a')
Lefty a <*> _ = Lefty a
_ <*> (Lefty a) = Lefty a
Righty f <*> Righty x = Righty (f x)
mkName :: String -> EitherLeftMonoid [Error] String
mkName "" = Lefty [InvalidName]
mkName s = Righty s
mkAge :: Int -> EitherLeftMonoid [Error] Int
mkAge n | n < 13 = Lefty [UnderAge]
| otherwise = Righty n
mkPerson :: String -> Int -> EitherLeftMonoid [Error] Person
mkPerson n a = Person <$> mkName n <*> mkAge a
Output:
Prelude> mkPerson "" 12
Lefty [InvalidName,UnderAge]
Prelude> mkPerson "" 13
Lefty [InvalidName]
Prelude> mkPerson "Gerd" 13
Righty (Person name = "Gerd", age = 13)
What could I have done different to make the code better?
haskell
I'm currently learning Haskell and are starting to understand applicatives.
I wanted an Either Applicative where the left side is a monoid, so I created this as a learning exercise:
module EitherLeftMonoid where
data Error = InvalidName | UnderAge deriving (Eq, Show)
data Person = Person name :: String, age :: Int deriving (Eq, Ord, Show)
data EitherLeftMonoid a b = Lefty a
| Righty b
deriving (Eq, Show)
instance Monoid a => Functor (EitherLeftMonoid a) where
fmap _ (Lefty a) = Lefty a
fmap f (Righty b) = Righty (f b)
instance Monoid a => Applicative (EitherLeftMonoid a) where
pure = Righty
Lefty a <*> Lefty a' = Lefty (a `mappend` a')
Lefty a <*> _ = Lefty a
_ <*> (Lefty a) = Lefty a
Righty f <*> Righty x = Righty (f x)
mkName :: String -> EitherLeftMonoid [Error] String
mkName "" = Lefty [InvalidName]
mkName s = Righty s
mkAge :: Int -> EitherLeftMonoid [Error] Int
mkAge n | n < 13 = Lefty [UnderAge]
| otherwise = Righty n
mkPerson :: String -> Int -> EitherLeftMonoid [Error] Person
mkPerson n a = Person <$> mkName n <*> mkAge a
Output:
Prelude> mkPerson "" 12
Lefty [InvalidName,UnderAge]
Prelude> mkPerson "" 13
Lefty [InvalidName]
Prelude> mkPerson "Gerd" 13
Righty (Person name = "Gerd", age = 13)
What could I have done different to make the code better?
haskell
asked Apr 17 at 13:20
Børge André Jensen
162
162
add a comment |Â
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
2
down vote
Your Functor
instance doesn't need a
to be a Monoid
. We can remove that constraint to make fmap
on EitherLeftMonoid
more general.
Your Applicative
instance could be made a little bit more readable if you provide a fromLefty :: Monoid a => EitherLeftMonoid a b -> a
function:
instance Functor (EitherLeftMonoid a) where
fmap _ (Lefty a) = Lefty a
fmap f (Righty b) = Righty (f b)
instance Monoid a => Applicative (EitherLeftMonoid a) where
pure = Righty
Righty f <*> Righty x = Righty (f x)
x <*> y = Lefty $ fromLefty x `mappend` fromLefty y
fromLefty :: Monoid a => EitherLeftMonoid a b -> a
fromLefty (Left x) = x
fromLefty _ = mempty
With fromLefty
we can take advantage of mappend mempty x = x = mappend x mempty
.
In this case everything is in a single module and everything gets exported. This enables a user to use the Person
constructor immediately. If you want to prevent that you need to make sure that only the type, but neither the records nor the data constructor get exported. Otherwise mkPerson
can get circumvented.
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
2
down vote
Your Functor
instance doesn't need a
to be a Monoid
. We can remove that constraint to make fmap
on EitherLeftMonoid
more general.
Your Applicative
instance could be made a little bit more readable if you provide a fromLefty :: Monoid a => EitherLeftMonoid a b -> a
function:
instance Functor (EitherLeftMonoid a) where
fmap _ (Lefty a) = Lefty a
fmap f (Righty b) = Righty (f b)
instance Monoid a => Applicative (EitherLeftMonoid a) where
pure = Righty
Righty f <*> Righty x = Righty (f x)
x <*> y = Lefty $ fromLefty x `mappend` fromLefty y
fromLefty :: Monoid a => EitherLeftMonoid a b -> a
fromLefty (Left x) = x
fromLefty _ = mempty
With fromLefty
we can take advantage of mappend mempty x = x = mappend x mempty
.
In this case everything is in a single module and everything gets exported. This enables a user to use the Person
constructor immediately. If you want to prevent that you need to make sure that only the type, but neither the records nor the data constructor get exported. Otherwise mkPerson
can get circumvented.
add a comment |Â
up vote
2
down vote
Your Functor
instance doesn't need a
to be a Monoid
. We can remove that constraint to make fmap
on EitherLeftMonoid
more general.
Your Applicative
instance could be made a little bit more readable if you provide a fromLefty :: Monoid a => EitherLeftMonoid a b -> a
function:
instance Functor (EitherLeftMonoid a) where
fmap _ (Lefty a) = Lefty a
fmap f (Righty b) = Righty (f b)
instance Monoid a => Applicative (EitherLeftMonoid a) where
pure = Righty
Righty f <*> Righty x = Righty (f x)
x <*> y = Lefty $ fromLefty x `mappend` fromLefty y
fromLefty :: Monoid a => EitherLeftMonoid a b -> a
fromLefty (Left x) = x
fromLefty _ = mempty
With fromLefty
we can take advantage of mappend mempty x = x = mappend x mempty
.
In this case everything is in a single module and everything gets exported. This enables a user to use the Person
constructor immediately. If you want to prevent that you need to make sure that only the type, but neither the records nor the data constructor get exported. Otherwise mkPerson
can get circumvented.
add a comment |Â
up vote
2
down vote
up vote
2
down vote
Your Functor
instance doesn't need a
to be a Monoid
. We can remove that constraint to make fmap
on EitherLeftMonoid
more general.
Your Applicative
instance could be made a little bit more readable if you provide a fromLefty :: Monoid a => EitherLeftMonoid a b -> a
function:
instance Functor (EitherLeftMonoid a) where
fmap _ (Lefty a) = Lefty a
fmap f (Righty b) = Righty (f b)
instance Monoid a => Applicative (EitherLeftMonoid a) where
pure = Righty
Righty f <*> Righty x = Righty (f x)
x <*> y = Lefty $ fromLefty x `mappend` fromLefty y
fromLefty :: Monoid a => EitherLeftMonoid a b -> a
fromLefty (Left x) = x
fromLefty _ = mempty
With fromLefty
we can take advantage of mappend mempty x = x = mappend x mempty
.
In this case everything is in a single module and everything gets exported. This enables a user to use the Person
constructor immediately. If you want to prevent that you need to make sure that only the type, but neither the records nor the data constructor get exported. Otherwise mkPerson
can get circumvented.
Your Functor
instance doesn't need a
to be a Monoid
. We can remove that constraint to make fmap
on EitherLeftMonoid
more general.
Your Applicative
instance could be made a little bit more readable if you provide a fromLefty :: Monoid a => EitherLeftMonoid a b -> a
function:
instance Functor (EitherLeftMonoid a) where
fmap _ (Lefty a) = Lefty a
fmap f (Righty b) = Righty (f b)
instance Monoid a => Applicative (EitherLeftMonoid a) where
pure = Righty
Righty f <*> Righty x = Righty (f x)
x <*> y = Lefty $ fromLefty x `mappend` fromLefty y
fromLefty :: Monoid a => EitherLeftMonoid a b -> a
fromLefty (Left x) = x
fromLefty _ = mempty
With fromLefty
we can take advantage of mappend mempty x = x = mappend x mempty
.
In this case everything is in a single module and everything gets exported. This enables a user to use the Person
constructor immediately. If you want to prevent that you need to make sure that only the type, but neither the records nor the data constructor get exported. Otherwise mkPerson
can get circumvented.
answered Apr 17 at 14:50
Zeta
14.3k23267
14.3k23267
add a comment |Â
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f192289%2fhaskell-either-applicative-where-the-left-is-a-monoid%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password