Haskell Either Applicative where the Left is a Monoid

The name of the pictureThe name of the pictureThe name of the pictureClash 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?







share|improve this question

























    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?







    share|improve this question





















      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?







      share|improve this question











      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?









      share|improve this question










      share|improve this question




      share|improve this question









      asked Apr 17 at 13:20









      Børge André Jensen

      162




      162




















          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.






          share|improve this answer





















            Your Answer




            StackExchange.ifUsing("editor", function ()
            return StackExchange.using("mathjaxEditing", function ()
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            );
            );
            , "mathjax-editing");

            StackExchange.ifUsing("editor", function ()
            StackExchange.using("externalEditor", function ()
            StackExchange.using("snippets", function ()
            StackExchange.snippets.init();
            );
            );
            , "code-snippets");

            StackExchange.ready(function()
            var channelOptions =
            tags: "".split(" "),
            id: "196"
            ;
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function()
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled)
            StackExchange.using("snippets", function()
            createEditor();
            );

            else
            createEditor();

            );

            function createEditor()
            StackExchange.prepareEditor(
            heartbeatType: 'answer',
            convertImagesToLinks: false,
            noModals: false,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            );



            );








             

            draft saved


            draft discarded


















            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






























            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.






            share|improve this answer

























              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.






              share|improve this answer























                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.






                share|improve this answer













                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.







                share|improve this answer













                share|improve this answer



                share|improve this answer











                answered Apr 17 at 14:50









                Zeta

                14.3k23267




                14.3k23267






















                     

                    draft saved


                    draft discarded


























                     


                    draft saved


                    draft discarded














                    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













































































                    Popular posts from this blog

                    Greedy Best First Search implementation in Rust

                    Function to Return a JSON Like Objects Using VBA Collections and Arrays

                    C++11 CLH Lock Implementation