Recursion with a self-defined type

I'm trying to translate *The Little MLer *into Haskell. I've got this data Shishkebab = Skewer | Onion Shishkebab | Lamb Shishkebab | Tomato Shishkebab deriving Show Then I have this which works veggieKebab :: Shishkebab -> Bool veggieKebab Skewer = True veggieKebab (Onion (shk)) = veggieKebab shk veggieKebab (Tomato (shk)) = veggieKebab shk veggieKebab (Lamb (shk)) = False
veggieKebab (Tomato (Onion (Tomato (Onion Skewer)))) True
but I'm wondering if I could do something like this veggieKebab :: Shishkebab -> Bool veggieKebab Skewer = True veggieKebab (shkb (sk)) | (shkb == Onion) || (shkb == Tomato) = veggieKebab sk | otherwise = False This doesn't work, giving a "Parse error in pattern: shkb". I've been advised that I'm trying to treat what is a data constructor like a variable, but I can't fathom what that means in this case. What I'm trying to leverage is what I've learned from dealing with lists and recursion through the consed list. So if effect I'm trying to recurse through a consed Shishkebab object. It works in the first case, but hyow could I do this in this more generic way like the second try does? LB

Pattern matches in Haskell are based on matching specific data
constructors, with underscores `_` as a "match anything" mechanism. So one
way to achieve something like what you want is
veggieKebab :: Shishkebab -> Bool
veggieKebab Skewer = True
veggieKebab (Onion (shk)) = veggieKebab shk
veggieKebab (Tomato (shk)) = veggieKebab shk
veggieKebab _ = False
This works because the matches are considered in top-to-bottom order, so
the last case only matches if all the others fail to.
I'm not sure if it helps to build insight or not, but if you look at the
the types of your data constructors in GHCI, you get, for example:
λ> :t Onion
Onion :: Shishkebab -> Shishkebab
So even if you could pattern match as you wanted (veggieKebab (shkb (sk)) |
(shkb == Onion)), you'd still be stuck with the problem of trying to
compare two functions for equality, which isn't easy (and not something
Haskell lets you do for arbitrary functions). You could get close to what
you originally wrote by using a few more helper functions:
startsWithOnion :: Shishkebab -> Bool
startsWithOnion (Onion _) = True
startsWithOnion _ = False
startsWithTomato :: Shishkebab -> Bool
startsWithTomato (Tomato _) = True
startsWithTomato _ = False
restOfKebab :: Shishkebab -> Shishkebab
restOfKebab Skewer = Skewer
restOfKebab (Onion rst) = rst
restOfKebab (Tomato rst) = rst
restOfKebab (Lamb rst) = rst
veggieKebab :: Shishkebab -> Bool
veggieKebab Skewer = True
veggieKebab kebab | startsWithOnion kebab || startsWithTomato kebab =
veggieKebab (restOfKebab kebab)
| otherwise = False
On Fri, Mar 12, 2021 at 9:19 AM Galaxy Being
I'm trying to translate *The Little MLer *into Haskell. I've got this
data Shishkebab = Skewer | Onion Shishkebab | Lamb Shishkebab | Tomato Shishkebab deriving Show
Then I have this which works
veggieKebab :: Shishkebab -> Bool veggieKebab Skewer = True veggieKebab (Onion (shk)) = veggieKebab shk veggieKebab (Tomato (shk)) = veggieKebab shk veggieKebab (Lamb (shk)) = False
veggieKebab (Tomato (Onion (Tomato (Onion Skewer)))) True
but I'm wondering if I could do something like this
veggieKebab :: Shishkebab -> Bool veggieKebab Skewer = True veggieKebab (shkb (sk)) | (shkb == Onion) || (shkb == Tomato) = veggieKebab sk | otherwise = False
This doesn't work, giving a "Parse error in pattern: shkb". I've been advised that I'm trying to treat what is a data constructor like a variable, but I can't fathom what that means in this case. What I'm trying to leverage is what I've learned from dealing with lists and recursion through the consed list. So if effect I'm trying to recurse through a consed Shishkebab object. It works in the first case, but hyow could I do this in this more generic way like the second try does?
LB
_______________________________________________ Beginners mailing list Beginners@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners

So because Onion Shishkebab in the type definition is technically a data
constructor *function, *the (shkb == Onion) in the code is comparing
function-to-function, i.e., won't work. Thanks. The light finally went on.
On Fri, Mar 12, 2021 at 6:28 PM Matthew Low
Pattern matches in Haskell are based on matching specific data constructors, with underscores `_` as a "match anything" mechanism. So one way to achieve something like what you want is
veggieKebab :: Shishkebab -> Bool veggieKebab Skewer = True veggieKebab (Onion (shk)) = veggieKebab shk veggieKebab (Tomato (shk)) = veggieKebab shk veggieKebab _ = False
This works because the matches are considered in top-to-bottom order, so the last case only matches if all the others fail to.
I'm not sure if it helps to build insight or not, but if you look at the the types of your data constructors in GHCI, you get, for example:
λ> :t Onion Onion :: Shishkebab -> Shishkebab
So even if you could pattern match as you wanted (veggieKebab (shkb (sk)) | (shkb == Onion)), you'd still be stuck with the problem of trying to compare two functions for equality, which isn't easy (and not something Haskell lets you do for arbitrary functions). You could get close to what you originally wrote by using a few more helper functions:
startsWithOnion :: Shishkebab -> Bool startsWithOnion (Onion _) = True startsWithOnion _ = False
startsWithTomato :: Shishkebab -> Bool startsWithTomato (Tomato _) = True startsWithTomato _ = False
restOfKebab :: Shishkebab -> Shishkebab restOfKebab Skewer = Skewer restOfKebab (Onion rst) = rst restOfKebab (Tomato rst) = rst restOfKebab (Lamb rst) = rst
veggieKebab :: Shishkebab -> Bool veggieKebab Skewer = True veggieKebab kebab | startsWithOnion kebab || startsWithTomato kebab = veggieKebab (restOfKebab kebab) | otherwise = False
On Fri, Mar 12, 2021 at 9:19 AM Galaxy Being
wrote: I'm trying to translate *The Little MLer *into Haskell. I've got this
data Shishkebab = Skewer | Onion Shishkebab | Lamb Shishkebab | Tomato Shishkebab deriving Show
Then I have this which works
veggieKebab :: Shishkebab -> Bool veggieKebab Skewer = True veggieKebab (Onion (shk)) = veggieKebab shk veggieKebab (Tomato (shk)) = veggieKebab shk veggieKebab (Lamb (shk)) = False
veggieKebab (Tomato (Onion (Tomato (Onion Skewer)))) True
but I'm wondering if I could do something like this
veggieKebab :: Shishkebab -> Bool veggieKebab Skewer = True veggieKebab (shkb (sk)) | (shkb == Onion) || (shkb == Tomato) = veggieKebab sk | otherwise = False
This doesn't work, giving a "Parse error in pattern: shkb". I've been advised that I'm trying to treat what is a data constructor like a variable, but I can't fathom what that means in this case. What I'm trying to leverage is what I've learned from dealing with lists and recursion through the consed list. So if effect I'm trying to recurse through a consed Shishkebab object. It works in the first case, but hyow could I do this in this more generic way like the second try does?
LB
_______________________________________________ Beginners mailing list Beginners@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
_______________________________________________ Beginners mailing list Beginners@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
participants (2)
-
Galaxy Being
-
Matthew Low