Haskell - Pattern matching with data types










6















I have a data type and function like this:



data Expr = Num Int | Add Expr Expr | Mult Expr Expr | Neg Expr | If Expr Expr Expr deriving (Show, Read)

prettyPrint :: Expr -> IO ()
prettyPrint expr = prettyPrint' expr 0


prettyPrint' :: Expr -> Int -> IO ()
prettyPrint' (Num x) i = putStrLn $ concat (replicate i " ") ++ "Num " ++ show x

prettyPrint' (Add x y) i = do
putStrLn $ concat (replicate i " ") ++ "Add"
prettyPrint' x (i+1)
prettyPrint' y (i+1)

prettyPrint' (Mult x y) i = do
putStrLn $ concat (replicate i " ") ++ "Mult"
prettyPrint' x (i+1)
prettyPrint' y (i+1)

prettyPrint' (Neg x) i = do
putStrLn $ concat (replicate i " ") ++ "Neg"
prettyPrint' x (i+1)

prettyPrint' (If x y z) i = do
putStrLn $ concat (replicate i " ") ++ "If"
prettyPrint' x (i+1)
prettyPrint' y (i+1)
prettyPrint' z (i+1)


In the function I am using pattern matching. The problem is that their is a lot of reuse of code. For example, the case for Mult and Add is basically the same code. Same goes for Num and Neg. Is there a way to write this based on how many variables the expression have? Like one for Num and Neg, since they have only one variable. One case for Mult and Add, since they have two variables. And a last case for If, since that expression have three variables.



NOTE:



I landed on this answer, I think it's a better solution than I started with:



prettyPrint :: Expr -> IO () 
prettyPrint expr = putStrLn (prettyPrint' 1 expr)

prettyPrint' :: Int -> Expr -> String
prettyPrint' i (Num x) = "Num " ++ show x
prettyPrint' i expr =
let indent x = concat (replicate i " ") ++ x
(op, args) = case expr of
Add x y -> ("Add", [x,y])
Mult x y -> ("Mult", [x,y])
Neg x -> ("Neg", [x])
If x y z -> ("If", [x,y,z])
in intercalate "n" (op : map (indent . prettyPrint' (i + 1)) args)









share|improve this question




























    6















    I have a data type and function like this:



    data Expr = Num Int | Add Expr Expr | Mult Expr Expr | Neg Expr | If Expr Expr Expr deriving (Show, Read)

    prettyPrint :: Expr -> IO ()
    prettyPrint expr = prettyPrint' expr 0


    prettyPrint' :: Expr -> Int -> IO ()
    prettyPrint' (Num x) i = putStrLn $ concat (replicate i " ") ++ "Num " ++ show x

    prettyPrint' (Add x y) i = do
    putStrLn $ concat (replicate i " ") ++ "Add"
    prettyPrint' x (i+1)
    prettyPrint' y (i+1)

    prettyPrint' (Mult x y) i = do
    putStrLn $ concat (replicate i " ") ++ "Mult"
    prettyPrint' x (i+1)
    prettyPrint' y (i+1)

    prettyPrint' (Neg x) i = do
    putStrLn $ concat (replicate i " ") ++ "Neg"
    prettyPrint' x (i+1)

    prettyPrint' (If x y z) i = do
    putStrLn $ concat (replicate i " ") ++ "If"
    prettyPrint' x (i+1)
    prettyPrint' y (i+1)
    prettyPrint' z (i+1)


    In the function I am using pattern matching. The problem is that their is a lot of reuse of code. For example, the case for Mult and Add is basically the same code. Same goes for Num and Neg. Is there a way to write this based on how many variables the expression have? Like one for Num and Neg, since they have only one variable. One case for Mult and Add, since they have two variables. And a last case for If, since that expression have three variables.



    NOTE:



    I landed on this answer, I think it's a better solution than I started with:



    prettyPrint :: Expr -> IO () 
    prettyPrint expr = putStrLn (prettyPrint' 1 expr)

    prettyPrint' :: Int -> Expr -> String
    prettyPrint' i (Num x) = "Num " ++ show x
    prettyPrint' i expr =
    let indent x = concat (replicate i " ") ++ x
    (op, args) = case expr of
    Add x y -> ("Add", [x,y])
    Mult x y -> ("Mult", [x,y])
    Neg x -> ("Neg", [x])
    If x y z -> ("If", [x,y,z])
    in intercalate "n" (op : map (indent . prettyPrint' (i + 1)) args)









    share|improve this question


























      6












      6








      6








      I have a data type and function like this:



      data Expr = Num Int | Add Expr Expr | Mult Expr Expr | Neg Expr | If Expr Expr Expr deriving (Show, Read)

      prettyPrint :: Expr -> IO ()
      prettyPrint expr = prettyPrint' expr 0


      prettyPrint' :: Expr -> Int -> IO ()
      prettyPrint' (Num x) i = putStrLn $ concat (replicate i " ") ++ "Num " ++ show x

      prettyPrint' (Add x y) i = do
      putStrLn $ concat (replicate i " ") ++ "Add"
      prettyPrint' x (i+1)
      prettyPrint' y (i+1)

      prettyPrint' (Mult x y) i = do
      putStrLn $ concat (replicate i " ") ++ "Mult"
      prettyPrint' x (i+1)
      prettyPrint' y (i+1)

      prettyPrint' (Neg x) i = do
      putStrLn $ concat (replicate i " ") ++ "Neg"
      prettyPrint' x (i+1)

      prettyPrint' (If x y z) i = do
      putStrLn $ concat (replicate i " ") ++ "If"
      prettyPrint' x (i+1)
      prettyPrint' y (i+1)
      prettyPrint' z (i+1)


      In the function I am using pattern matching. The problem is that their is a lot of reuse of code. For example, the case for Mult and Add is basically the same code. Same goes for Num and Neg. Is there a way to write this based on how many variables the expression have? Like one for Num and Neg, since they have only one variable. One case for Mult and Add, since they have two variables. And a last case for If, since that expression have three variables.



      NOTE:



      I landed on this answer, I think it's a better solution than I started with:



      prettyPrint :: Expr -> IO () 
      prettyPrint expr = putStrLn (prettyPrint' 1 expr)

      prettyPrint' :: Int -> Expr -> String
      prettyPrint' i (Num x) = "Num " ++ show x
      prettyPrint' i expr =
      let indent x = concat (replicate i " ") ++ x
      (op, args) = case expr of
      Add x y -> ("Add", [x,y])
      Mult x y -> ("Mult", [x,y])
      Neg x -> ("Neg", [x])
      If x y z -> ("If", [x,y,z])
      in intercalate "n" (op : map (indent . prettyPrint' (i + 1)) args)









      share|improve this question
















      I have a data type and function like this:



      data Expr = Num Int | Add Expr Expr | Mult Expr Expr | Neg Expr | If Expr Expr Expr deriving (Show, Read)

      prettyPrint :: Expr -> IO ()
      prettyPrint expr = prettyPrint' expr 0


      prettyPrint' :: Expr -> Int -> IO ()
      prettyPrint' (Num x) i = putStrLn $ concat (replicate i " ") ++ "Num " ++ show x

      prettyPrint' (Add x y) i = do
      putStrLn $ concat (replicate i " ") ++ "Add"
      prettyPrint' x (i+1)
      prettyPrint' y (i+1)

      prettyPrint' (Mult x y) i = do
      putStrLn $ concat (replicate i " ") ++ "Mult"
      prettyPrint' x (i+1)
      prettyPrint' y (i+1)

      prettyPrint' (Neg x) i = do
      putStrLn $ concat (replicate i " ") ++ "Neg"
      prettyPrint' x (i+1)

      prettyPrint' (If x y z) i = do
      putStrLn $ concat (replicate i " ") ++ "If"
      prettyPrint' x (i+1)
      prettyPrint' y (i+1)
      prettyPrint' z (i+1)


      In the function I am using pattern matching. The problem is that their is a lot of reuse of code. For example, the case for Mult and Add is basically the same code. Same goes for Num and Neg. Is there a way to write this based on how many variables the expression have? Like one for Num and Neg, since they have only one variable. One case for Mult and Add, since they have two variables. And a last case for If, since that expression have three variables.



      NOTE:



      I landed on this answer, I think it's a better solution than I started with:



      prettyPrint :: Expr -> IO () 
      prettyPrint expr = putStrLn (prettyPrint' 1 expr)

      prettyPrint' :: Int -> Expr -> String
      prettyPrint' i (Num x) = "Num " ++ show x
      prettyPrint' i expr =
      let indent x = concat (replicate i " ") ++ x
      (op, args) = case expr of
      Add x y -> ("Add", [x,y])
      Mult x y -> ("Mult", [x,y])
      Neg x -> ("Neg", [x])
      If x y z -> ("If", [x,y,z])
      in intercalate "n" (op : map (indent . prettyPrint' (i + 1)) args)






      haskell






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 13 '18 at 18:05







      Christian

















      asked Nov 13 '18 at 15:34









      ChristianChristian

      334




      334






















          3 Answers
          3






          active

          oldest

          votes


















          2














          First, I would stay out of the IO monad for as long as possible. Have prettyPrint' return a string to be printed.



          prettyPrint :: Expr -> IO ()
          prettyPrint = putStrLn . prettyPrint'


          Now, the only job of prettyPrint' is to create a (possibly multiline) string to be printed. For numbers, that's easy: just use the show instance.



          prettyPrint' :: Expr -> String
          prettyPrint' e@(Num _) = show e
          -- or, ignoring the Show instance for Expr altogether
          -- prettyPrint' (Num x) = "Num " ++ show x


          For the rest, there is a pattern:



          1. Identify the constructor

          2. Identify its arguments

          3. Join the constructor name and its pretty-printed arguments with newlines. Each argument will be indented one level relative to its operator; the recursion will take care of multiple levels of indentation.

          That will look like



          prettyPrint' expr = let indent x = " " ++ x
          (op, args) = case expr of
          Add x y -> ("Add", [x,y])
          Mult x y -> ("Mult", [x,y])
          Neg x -> ("Neg", [x])
          If x y z -> ("If", [x,y,z])
          in intercalate "n" (op : map (indent . prettyPrint') args)


          As an example, consider what prettyPrint' will do with the expression Add (Num 3) (Num 5). First, it sets op to "Add" and args to [Num 3, Num 5]. Next, it maps indent . prettyPrint' over the argument list, to get [" Num 3", " Num 5"]. Putting the operator on the front of the list yields ["Add", " Num 3", " Num 3"], then joining them with intercalate produces "Addn Num 3n Num 5".




          The only remaining boilerplate is in the case expression. I think it's possible to eliminate that, but it requires a level of generic programming I'm not familiar with. I'm sure someone else could probably run with my answer to fix that.






          share|improve this answer
































            1














            In general, when addressing duplication in code, it pays to keep the rule of three in mind. Two occurrences of a block of code isn't necessarily a problem.



            That said, Haskell is a (very) strongly-typed language, so you generally can't pattern-match on arity like you can in, say, Erlang or Clojure.



            If you really want to abstract away the recursion part of a recursive data structure, you can define the catamorphism for it. People often also call this a fold, so let's keep that slightly more friendly name:



            data Expr =
            Num Int | Add Expr Expr | Mult Expr Expr | Neg Expr | If Bool Expr Expr deriving (Show, Read)

            foldExpr ::
            (Int -> a) -> (a -> a -> a) -> (a -> a -> a) -> (a -> a) -> (Bool -> a -> a -> a) -> Expr -> a
            foldExpr num _ _ _ _ (Num x) = num x
            foldExpr num add mul neg iff (Add x y) =
            add (foldExpr num add mul neg iff x) (foldExpr num add mul neg iff y)
            foldExpr num add mul neg iff (Mult x y) =
            mul (foldExpr num add mul neg iff x) (foldExpr num add mul neg iff y)
            foldExpr num add mul neg iff (Neg x) = neg (foldExpr num add mul neg iff x)
            foldExpr num add mul neg iff (If b x y) =
            iff b (foldExpr num add mul neg iff x) (foldExpr num add mul neg iff y)


            This is an entirely generic function that enables you turn turn any Expr value into any value of the type a, without worrying about reimplementing recursion every time. You just have to supply functions that deal with each of the cases.



            You can, for example, easily write an evaluator:



            evaluate :: Expr -> Int
            evaluate = foldExpr id (+) (*) negate (p x y -> if p then x else y)


            (Notice, BTW, that I changed the definition of If, because I couldn't see how the OP definition would work.)



            You can also write a function to turn an Expr value into a string, although this one is just a sketch; it needs indentation or bracket logic to work correctly:



            prettyPrint :: Expr -> String
            prettyPrint =
            foldExpr
            show -- Num
            (x y -> x ++ "+" ++ y) -- Add
            (x y -> x ++ "*" ++ y) -- Mult
            (x -> "(-" ++ x ++ ")") -- Neg
            (p x y -> "if " ++ show p ++ " then " ++ x ++ " else " ++ y) -- If


            You can try it out in GHCi:



            *Q53284410> evaluate (Num 42)
            42
            *Q53284410> evaluate (Add (Num 40) (Num 2))
            42
            *Q53284410> evaluate (Add (Mult (Num 4) (Num 10)) (Num 2))
            42
            *Q53284410> prettyPrint $ Num 42
            "42"
            *Q53284410> prettyPrint $ Mult (Num 6) (Num 7)
            "6*7"
            *Q53284410> prettyPrint $ Add (Mult (Num 2) (Num 3)) (Num 7)
            "2*3+7"





            share|improve this answer






























              1














              Yes, just create a function to print list of Expr:



              import Control.Monad (forM_)

              printExprList::[Expr]->Int->String->IO ()
              printExprList exprs i desc = do
              putStrLn $ concat (replicate i " ") ++ desc
              forM_ (zip exprs [i..]) $ (e, j)-> prettyPrint' e (j+1)


              and then call it to print:



              prettyPrint' :: Expr -> Int -> IO () 
              prettyPrint' (Add x y) i = printExprList [x, y] i "Add"
              prettyPrint' (Mult x y) i = printExprList [x, y] i "Mult"
              prettyPrint' (Neg x) i = printExprList [x] i "Neg"
              prettyPrint' (If x y z) i = printExprList [x, y, z] i "If"

              prettyPrint' (Num x) i = putStrLn $ concat (replicate i " ")
              ++ "Num " ++ show x





              share|improve this answer
























                Your Answer






                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: "1"
                ;
                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',
                autoActivateHeartbeat: false,
                convertImagesToLinks: true,
                noModals: true,
                showLowRepImageUploadWarning: true,
                reputationToPostImages: 10,
                bindNavPrevention: true,
                postfix: "",
                imageUploader:
                brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
                contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
                allowUrls: true
                ,
                onDemand: true,
                discardSelector: ".discard-answer"
                ,immediatelyShowMarkdownHelp:true
                );



                );













                draft saved

                draft discarded


















                StackExchange.ready(
                function ()
                StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53284410%2fhaskell-pattern-matching-with-data-types%23new-answer', 'question_page');

                );

                Post as a guest















                Required, but never shown

























                3 Answers
                3






                active

                oldest

                votes








                3 Answers
                3






                active

                oldest

                votes









                active

                oldest

                votes






                active

                oldest

                votes









                2














                First, I would stay out of the IO monad for as long as possible. Have prettyPrint' return a string to be printed.



                prettyPrint :: Expr -> IO ()
                prettyPrint = putStrLn . prettyPrint'


                Now, the only job of prettyPrint' is to create a (possibly multiline) string to be printed. For numbers, that's easy: just use the show instance.



                prettyPrint' :: Expr -> String
                prettyPrint' e@(Num _) = show e
                -- or, ignoring the Show instance for Expr altogether
                -- prettyPrint' (Num x) = "Num " ++ show x


                For the rest, there is a pattern:



                1. Identify the constructor

                2. Identify its arguments

                3. Join the constructor name and its pretty-printed arguments with newlines. Each argument will be indented one level relative to its operator; the recursion will take care of multiple levels of indentation.

                That will look like



                prettyPrint' expr = let indent x = " " ++ x
                (op, args) = case expr of
                Add x y -> ("Add", [x,y])
                Mult x y -> ("Mult", [x,y])
                Neg x -> ("Neg", [x])
                If x y z -> ("If", [x,y,z])
                in intercalate "n" (op : map (indent . prettyPrint') args)


                As an example, consider what prettyPrint' will do with the expression Add (Num 3) (Num 5). First, it sets op to "Add" and args to [Num 3, Num 5]. Next, it maps indent . prettyPrint' over the argument list, to get [" Num 3", " Num 5"]. Putting the operator on the front of the list yields ["Add", " Num 3", " Num 3"], then joining them with intercalate produces "Addn Num 3n Num 5".




                The only remaining boilerplate is in the case expression. I think it's possible to eliminate that, but it requires a level of generic programming I'm not familiar with. I'm sure someone else could probably run with my answer to fix that.






                share|improve this answer





























                  2














                  First, I would stay out of the IO monad for as long as possible. Have prettyPrint' return a string to be printed.



                  prettyPrint :: Expr -> IO ()
                  prettyPrint = putStrLn . prettyPrint'


                  Now, the only job of prettyPrint' is to create a (possibly multiline) string to be printed. For numbers, that's easy: just use the show instance.



                  prettyPrint' :: Expr -> String
                  prettyPrint' e@(Num _) = show e
                  -- or, ignoring the Show instance for Expr altogether
                  -- prettyPrint' (Num x) = "Num " ++ show x


                  For the rest, there is a pattern:



                  1. Identify the constructor

                  2. Identify its arguments

                  3. Join the constructor name and its pretty-printed arguments with newlines. Each argument will be indented one level relative to its operator; the recursion will take care of multiple levels of indentation.

                  That will look like



                  prettyPrint' expr = let indent x = " " ++ x
                  (op, args) = case expr of
                  Add x y -> ("Add", [x,y])
                  Mult x y -> ("Mult", [x,y])
                  Neg x -> ("Neg", [x])
                  If x y z -> ("If", [x,y,z])
                  in intercalate "n" (op : map (indent . prettyPrint') args)


                  As an example, consider what prettyPrint' will do with the expression Add (Num 3) (Num 5). First, it sets op to "Add" and args to [Num 3, Num 5]. Next, it maps indent . prettyPrint' over the argument list, to get [" Num 3", " Num 5"]. Putting the operator on the front of the list yields ["Add", " Num 3", " Num 3"], then joining them with intercalate produces "Addn Num 3n Num 5".




                  The only remaining boilerplate is in the case expression. I think it's possible to eliminate that, but it requires a level of generic programming I'm not familiar with. I'm sure someone else could probably run with my answer to fix that.






                  share|improve this answer



























                    2












                    2








                    2







                    First, I would stay out of the IO monad for as long as possible. Have prettyPrint' return a string to be printed.



                    prettyPrint :: Expr -> IO ()
                    prettyPrint = putStrLn . prettyPrint'


                    Now, the only job of prettyPrint' is to create a (possibly multiline) string to be printed. For numbers, that's easy: just use the show instance.



                    prettyPrint' :: Expr -> String
                    prettyPrint' e@(Num _) = show e
                    -- or, ignoring the Show instance for Expr altogether
                    -- prettyPrint' (Num x) = "Num " ++ show x


                    For the rest, there is a pattern:



                    1. Identify the constructor

                    2. Identify its arguments

                    3. Join the constructor name and its pretty-printed arguments with newlines. Each argument will be indented one level relative to its operator; the recursion will take care of multiple levels of indentation.

                    That will look like



                    prettyPrint' expr = let indent x = " " ++ x
                    (op, args) = case expr of
                    Add x y -> ("Add", [x,y])
                    Mult x y -> ("Mult", [x,y])
                    Neg x -> ("Neg", [x])
                    If x y z -> ("If", [x,y,z])
                    in intercalate "n" (op : map (indent . prettyPrint') args)


                    As an example, consider what prettyPrint' will do with the expression Add (Num 3) (Num 5). First, it sets op to "Add" and args to [Num 3, Num 5]. Next, it maps indent . prettyPrint' over the argument list, to get [" Num 3", " Num 5"]. Putting the operator on the front of the list yields ["Add", " Num 3", " Num 3"], then joining them with intercalate produces "Addn Num 3n Num 5".




                    The only remaining boilerplate is in the case expression. I think it's possible to eliminate that, but it requires a level of generic programming I'm not familiar with. I'm sure someone else could probably run with my answer to fix that.






                    share|improve this answer















                    First, I would stay out of the IO monad for as long as possible. Have prettyPrint' return a string to be printed.



                    prettyPrint :: Expr -> IO ()
                    prettyPrint = putStrLn . prettyPrint'


                    Now, the only job of prettyPrint' is to create a (possibly multiline) string to be printed. For numbers, that's easy: just use the show instance.



                    prettyPrint' :: Expr -> String
                    prettyPrint' e@(Num _) = show e
                    -- or, ignoring the Show instance for Expr altogether
                    -- prettyPrint' (Num x) = "Num " ++ show x


                    For the rest, there is a pattern:



                    1. Identify the constructor

                    2. Identify its arguments

                    3. Join the constructor name and its pretty-printed arguments with newlines. Each argument will be indented one level relative to its operator; the recursion will take care of multiple levels of indentation.

                    That will look like



                    prettyPrint' expr = let indent x = " " ++ x
                    (op, args) = case expr of
                    Add x y -> ("Add", [x,y])
                    Mult x y -> ("Mult", [x,y])
                    Neg x -> ("Neg", [x])
                    If x y z -> ("If", [x,y,z])
                    in intercalate "n" (op : map (indent . prettyPrint') args)


                    As an example, consider what prettyPrint' will do with the expression Add (Num 3) (Num 5). First, it sets op to "Add" and args to [Num 3, Num 5]. Next, it maps indent . prettyPrint' over the argument list, to get [" Num 3", " Num 5"]. Putting the operator on the front of the list yields ["Add", " Num 3", " Num 3"], then joining them with intercalate produces "Addn Num 3n Num 5".




                    The only remaining boilerplate is in the case expression. I think it's possible to eliminate that, but it requires a level of generic programming I'm not familiar with. I'm sure someone else could probably run with my answer to fix that.







                    share|improve this answer














                    share|improve this answer



                    share|improve this answer








                    edited Nov 13 '18 at 19:50

























                    answered Nov 13 '18 at 16:28









                    chepnerchepner

                    252k34238331




                    252k34238331























                        1














                        In general, when addressing duplication in code, it pays to keep the rule of three in mind. Two occurrences of a block of code isn't necessarily a problem.



                        That said, Haskell is a (very) strongly-typed language, so you generally can't pattern-match on arity like you can in, say, Erlang or Clojure.



                        If you really want to abstract away the recursion part of a recursive data structure, you can define the catamorphism for it. People often also call this a fold, so let's keep that slightly more friendly name:



                        data Expr =
                        Num Int | Add Expr Expr | Mult Expr Expr | Neg Expr | If Bool Expr Expr deriving (Show, Read)

                        foldExpr ::
                        (Int -> a) -> (a -> a -> a) -> (a -> a -> a) -> (a -> a) -> (Bool -> a -> a -> a) -> Expr -> a
                        foldExpr num _ _ _ _ (Num x) = num x
                        foldExpr num add mul neg iff (Add x y) =
                        add (foldExpr num add mul neg iff x) (foldExpr num add mul neg iff y)
                        foldExpr num add mul neg iff (Mult x y) =
                        mul (foldExpr num add mul neg iff x) (foldExpr num add mul neg iff y)
                        foldExpr num add mul neg iff (Neg x) = neg (foldExpr num add mul neg iff x)
                        foldExpr num add mul neg iff (If b x y) =
                        iff b (foldExpr num add mul neg iff x) (foldExpr num add mul neg iff y)


                        This is an entirely generic function that enables you turn turn any Expr value into any value of the type a, without worrying about reimplementing recursion every time. You just have to supply functions that deal with each of the cases.



                        You can, for example, easily write an evaluator:



                        evaluate :: Expr -> Int
                        evaluate = foldExpr id (+) (*) negate (p x y -> if p then x else y)


                        (Notice, BTW, that I changed the definition of If, because I couldn't see how the OP definition would work.)



                        You can also write a function to turn an Expr value into a string, although this one is just a sketch; it needs indentation or bracket logic to work correctly:



                        prettyPrint :: Expr -> String
                        prettyPrint =
                        foldExpr
                        show -- Num
                        (x y -> x ++ "+" ++ y) -- Add
                        (x y -> x ++ "*" ++ y) -- Mult
                        (x -> "(-" ++ x ++ ")") -- Neg
                        (p x y -> "if " ++ show p ++ " then " ++ x ++ " else " ++ y) -- If


                        You can try it out in GHCi:



                        *Q53284410> evaluate (Num 42)
                        42
                        *Q53284410> evaluate (Add (Num 40) (Num 2))
                        42
                        *Q53284410> evaluate (Add (Mult (Num 4) (Num 10)) (Num 2))
                        42
                        *Q53284410> prettyPrint $ Num 42
                        "42"
                        *Q53284410> prettyPrint $ Mult (Num 6) (Num 7)
                        "6*7"
                        *Q53284410> prettyPrint $ Add (Mult (Num 2) (Num 3)) (Num 7)
                        "2*3+7"





                        share|improve this answer



























                          1














                          In general, when addressing duplication in code, it pays to keep the rule of three in mind. Two occurrences of a block of code isn't necessarily a problem.



                          That said, Haskell is a (very) strongly-typed language, so you generally can't pattern-match on arity like you can in, say, Erlang or Clojure.



                          If you really want to abstract away the recursion part of a recursive data structure, you can define the catamorphism for it. People often also call this a fold, so let's keep that slightly more friendly name:



                          data Expr =
                          Num Int | Add Expr Expr | Mult Expr Expr | Neg Expr | If Bool Expr Expr deriving (Show, Read)

                          foldExpr ::
                          (Int -> a) -> (a -> a -> a) -> (a -> a -> a) -> (a -> a) -> (Bool -> a -> a -> a) -> Expr -> a
                          foldExpr num _ _ _ _ (Num x) = num x
                          foldExpr num add mul neg iff (Add x y) =
                          add (foldExpr num add mul neg iff x) (foldExpr num add mul neg iff y)
                          foldExpr num add mul neg iff (Mult x y) =
                          mul (foldExpr num add mul neg iff x) (foldExpr num add mul neg iff y)
                          foldExpr num add mul neg iff (Neg x) = neg (foldExpr num add mul neg iff x)
                          foldExpr num add mul neg iff (If b x y) =
                          iff b (foldExpr num add mul neg iff x) (foldExpr num add mul neg iff y)


                          This is an entirely generic function that enables you turn turn any Expr value into any value of the type a, without worrying about reimplementing recursion every time. You just have to supply functions that deal with each of the cases.



                          You can, for example, easily write an evaluator:



                          evaluate :: Expr -> Int
                          evaluate = foldExpr id (+) (*) negate (p x y -> if p then x else y)


                          (Notice, BTW, that I changed the definition of If, because I couldn't see how the OP definition would work.)



                          You can also write a function to turn an Expr value into a string, although this one is just a sketch; it needs indentation or bracket logic to work correctly:



                          prettyPrint :: Expr -> String
                          prettyPrint =
                          foldExpr
                          show -- Num
                          (x y -> x ++ "+" ++ y) -- Add
                          (x y -> x ++ "*" ++ y) -- Mult
                          (x -> "(-" ++ x ++ ")") -- Neg
                          (p x y -> "if " ++ show p ++ " then " ++ x ++ " else " ++ y) -- If


                          You can try it out in GHCi:



                          *Q53284410> evaluate (Num 42)
                          42
                          *Q53284410> evaluate (Add (Num 40) (Num 2))
                          42
                          *Q53284410> evaluate (Add (Mult (Num 4) (Num 10)) (Num 2))
                          42
                          *Q53284410> prettyPrint $ Num 42
                          "42"
                          *Q53284410> prettyPrint $ Mult (Num 6) (Num 7)
                          "6*7"
                          *Q53284410> prettyPrint $ Add (Mult (Num 2) (Num 3)) (Num 7)
                          "2*3+7"





                          share|improve this answer

























                            1












                            1








                            1







                            In general, when addressing duplication in code, it pays to keep the rule of three in mind. Two occurrences of a block of code isn't necessarily a problem.



                            That said, Haskell is a (very) strongly-typed language, so you generally can't pattern-match on arity like you can in, say, Erlang or Clojure.



                            If you really want to abstract away the recursion part of a recursive data structure, you can define the catamorphism for it. People often also call this a fold, so let's keep that slightly more friendly name:



                            data Expr =
                            Num Int | Add Expr Expr | Mult Expr Expr | Neg Expr | If Bool Expr Expr deriving (Show, Read)

                            foldExpr ::
                            (Int -> a) -> (a -> a -> a) -> (a -> a -> a) -> (a -> a) -> (Bool -> a -> a -> a) -> Expr -> a
                            foldExpr num _ _ _ _ (Num x) = num x
                            foldExpr num add mul neg iff (Add x y) =
                            add (foldExpr num add mul neg iff x) (foldExpr num add mul neg iff y)
                            foldExpr num add mul neg iff (Mult x y) =
                            mul (foldExpr num add mul neg iff x) (foldExpr num add mul neg iff y)
                            foldExpr num add mul neg iff (Neg x) = neg (foldExpr num add mul neg iff x)
                            foldExpr num add mul neg iff (If b x y) =
                            iff b (foldExpr num add mul neg iff x) (foldExpr num add mul neg iff y)


                            This is an entirely generic function that enables you turn turn any Expr value into any value of the type a, without worrying about reimplementing recursion every time. You just have to supply functions that deal with each of the cases.



                            You can, for example, easily write an evaluator:



                            evaluate :: Expr -> Int
                            evaluate = foldExpr id (+) (*) negate (p x y -> if p then x else y)


                            (Notice, BTW, that I changed the definition of If, because I couldn't see how the OP definition would work.)



                            You can also write a function to turn an Expr value into a string, although this one is just a sketch; it needs indentation or bracket logic to work correctly:



                            prettyPrint :: Expr -> String
                            prettyPrint =
                            foldExpr
                            show -- Num
                            (x y -> x ++ "+" ++ y) -- Add
                            (x y -> x ++ "*" ++ y) -- Mult
                            (x -> "(-" ++ x ++ ")") -- Neg
                            (p x y -> "if " ++ show p ++ " then " ++ x ++ " else " ++ y) -- If


                            You can try it out in GHCi:



                            *Q53284410> evaluate (Num 42)
                            42
                            *Q53284410> evaluate (Add (Num 40) (Num 2))
                            42
                            *Q53284410> evaluate (Add (Mult (Num 4) (Num 10)) (Num 2))
                            42
                            *Q53284410> prettyPrint $ Num 42
                            "42"
                            *Q53284410> prettyPrint $ Mult (Num 6) (Num 7)
                            "6*7"
                            *Q53284410> prettyPrint $ Add (Mult (Num 2) (Num 3)) (Num 7)
                            "2*3+7"





                            share|improve this answer













                            In general, when addressing duplication in code, it pays to keep the rule of three in mind. Two occurrences of a block of code isn't necessarily a problem.



                            That said, Haskell is a (very) strongly-typed language, so you generally can't pattern-match on arity like you can in, say, Erlang or Clojure.



                            If you really want to abstract away the recursion part of a recursive data structure, you can define the catamorphism for it. People often also call this a fold, so let's keep that slightly more friendly name:



                            data Expr =
                            Num Int | Add Expr Expr | Mult Expr Expr | Neg Expr | If Bool Expr Expr deriving (Show, Read)

                            foldExpr ::
                            (Int -> a) -> (a -> a -> a) -> (a -> a -> a) -> (a -> a) -> (Bool -> a -> a -> a) -> Expr -> a
                            foldExpr num _ _ _ _ (Num x) = num x
                            foldExpr num add mul neg iff (Add x y) =
                            add (foldExpr num add mul neg iff x) (foldExpr num add mul neg iff y)
                            foldExpr num add mul neg iff (Mult x y) =
                            mul (foldExpr num add mul neg iff x) (foldExpr num add mul neg iff y)
                            foldExpr num add mul neg iff (Neg x) = neg (foldExpr num add mul neg iff x)
                            foldExpr num add mul neg iff (If b x y) =
                            iff b (foldExpr num add mul neg iff x) (foldExpr num add mul neg iff y)


                            This is an entirely generic function that enables you turn turn any Expr value into any value of the type a, without worrying about reimplementing recursion every time. You just have to supply functions that deal with each of the cases.



                            You can, for example, easily write an evaluator:



                            evaluate :: Expr -> Int
                            evaluate = foldExpr id (+) (*) negate (p x y -> if p then x else y)


                            (Notice, BTW, that I changed the definition of If, because I couldn't see how the OP definition would work.)



                            You can also write a function to turn an Expr value into a string, although this one is just a sketch; it needs indentation or bracket logic to work correctly:



                            prettyPrint :: Expr -> String
                            prettyPrint =
                            foldExpr
                            show -- Num
                            (x y -> x ++ "+" ++ y) -- Add
                            (x y -> x ++ "*" ++ y) -- Mult
                            (x -> "(-" ++ x ++ ")") -- Neg
                            (p x y -> "if " ++ show p ++ " then " ++ x ++ " else " ++ y) -- If


                            You can try it out in GHCi:



                            *Q53284410> evaluate (Num 42)
                            42
                            *Q53284410> evaluate (Add (Num 40) (Num 2))
                            42
                            *Q53284410> evaluate (Add (Mult (Num 4) (Num 10)) (Num 2))
                            42
                            *Q53284410> prettyPrint $ Num 42
                            "42"
                            *Q53284410> prettyPrint $ Mult (Num 6) (Num 7)
                            "6*7"
                            *Q53284410> prettyPrint $ Add (Mult (Num 2) (Num 3)) (Num 7)
                            "2*3+7"






                            share|improve this answer












                            share|improve this answer



                            share|improve this answer










                            answered Nov 13 '18 at 16:30









                            Mark SeemannMark Seemann

                            184k33326564




                            184k33326564





















                                1














                                Yes, just create a function to print list of Expr:



                                import Control.Monad (forM_)

                                printExprList::[Expr]->Int->String->IO ()
                                printExprList exprs i desc = do
                                putStrLn $ concat (replicate i " ") ++ desc
                                forM_ (zip exprs [i..]) $ (e, j)-> prettyPrint' e (j+1)


                                and then call it to print:



                                prettyPrint' :: Expr -> Int -> IO () 
                                prettyPrint' (Add x y) i = printExprList [x, y] i "Add"
                                prettyPrint' (Mult x y) i = printExprList [x, y] i "Mult"
                                prettyPrint' (Neg x) i = printExprList [x] i "Neg"
                                prettyPrint' (If x y z) i = printExprList [x, y, z] i "If"

                                prettyPrint' (Num x) i = putStrLn $ concat (replicate i " ")
                                ++ "Num " ++ show x





                                share|improve this answer





























                                  1














                                  Yes, just create a function to print list of Expr:



                                  import Control.Monad (forM_)

                                  printExprList::[Expr]->Int->String->IO ()
                                  printExprList exprs i desc = do
                                  putStrLn $ concat (replicate i " ") ++ desc
                                  forM_ (zip exprs [i..]) $ (e, j)-> prettyPrint' e (j+1)


                                  and then call it to print:



                                  prettyPrint' :: Expr -> Int -> IO () 
                                  prettyPrint' (Add x y) i = printExprList [x, y] i "Add"
                                  prettyPrint' (Mult x y) i = printExprList [x, y] i "Mult"
                                  prettyPrint' (Neg x) i = printExprList [x] i "Neg"
                                  prettyPrint' (If x y z) i = printExprList [x, y, z] i "If"

                                  prettyPrint' (Num x) i = putStrLn $ concat (replicate i " ")
                                  ++ "Num " ++ show x





                                  share|improve this answer



























                                    1












                                    1








                                    1







                                    Yes, just create a function to print list of Expr:



                                    import Control.Monad (forM_)

                                    printExprList::[Expr]->Int->String->IO ()
                                    printExprList exprs i desc = do
                                    putStrLn $ concat (replicate i " ") ++ desc
                                    forM_ (zip exprs [i..]) $ (e, j)-> prettyPrint' e (j+1)


                                    and then call it to print:



                                    prettyPrint' :: Expr -> Int -> IO () 
                                    prettyPrint' (Add x y) i = printExprList [x, y] i "Add"
                                    prettyPrint' (Mult x y) i = printExprList [x, y] i "Mult"
                                    prettyPrint' (Neg x) i = printExprList [x] i "Neg"
                                    prettyPrint' (If x y z) i = printExprList [x, y, z] i "If"

                                    prettyPrint' (Num x) i = putStrLn $ concat (replicate i " ")
                                    ++ "Num " ++ show x





                                    share|improve this answer















                                    Yes, just create a function to print list of Expr:



                                    import Control.Monad (forM_)

                                    printExprList::[Expr]->Int->String->IO ()
                                    printExprList exprs i desc = do
                                    putStrLn $ concat (replicate i " ") ++ desc
                                    forM_ (zip exprs [i..]) $ (e, j)-> prettyPrint' e (j+1)


                                    and then call it to print:



                                    prettyPrint' :: Expr -> Int -> IO () 
                                    prettyPrint' (Add x y) i = printExprList [x, y] i "Add"
                                    prettyPrint' (Mult x y) i = printExprList [x, y] i "Mult"
                                    prettyPrint' (Neg x) i = printExprList [x] i "Neg"
                                    prettyPrint' (If x y z) i = printExprList [x, y, z] i "If"

                                    prettyPrint' (Num x) i = putStrLn $ concat (replicate i " ")
                                    ++ "Num " ++ show x






                                    share|improve this answer














                                    share|improve this answer



                                    share|improve this answer








                                    edited Nov 13 '18 at 16:33

























                                    answered Nov 13 '18 at 16:08









                                    assembly.jcassembly.jc

                                    2,0891214




                                    2,0891214



























                                        draft saved

                                        draft discarded
















































                                        Thanks for contributing an answer to Stack Overflow!


                                        • Please be sure to answer the question. Provide details and share your research!

                                        But avoid


                                        • Asking for help, clarification, or responding to other answers.

                                        • Making statements based on opinion; back them up with references or personal experience.

                                        To learn more, see our tips on writing great answers.




                                        draft saved


                                        draft discarded














                                        StackExchange.ready(
                                        function ()
                                        StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53284410%2fhaskell-pattern-matching-with-data-types%23new-answer', 'question_page');

                                        );

                                        Post as a guest















                                        Required, but never shown





















































                                        Required, but never shown














                                        Required, but never shown












                                        Required, but never shown







                                        Required, but never shown

































                                        Required, but never shown














                                        Required, but never shown












                                        Required, but never shown







                                        Required, but never shown







                                        Popular posts from this blog

                                        How to how show current date and time by default on contact form 7 in WordPress without taking input from user in datetimepicker

                                        Syphilis

                                        Darth Vader #20