Chloe
Chloe

Reputation: 11

Parse error when creating function without parameters in Haskell

I am trying to create a function without parameters that create a list comprehension. When I try to run the code, I get a parse error and it fails to compile and I have no idea why.

I keep trying to run it on the terminal but it won't work.

This is my code:

outfits = let pants = ["jeans", "khaki", "leggings"]
              shirts = ["white shirt", "grey turtleneck", "pink polo", "green hoodie"]
              shoes = ["brogues", "converse", "sandals", "adidas"]

outfits1 = [ (x, y, z) | z <- pants, y <- shirts, x <- shoes ]

This is the error that comes up:

warmup.hs:7:11: error: parse error on input ‘outfits1’
  |
7 |           outfits1 = [ (x, y, z) | z <- pants, y <- shirts, x <- shoes ]
  |           ^^^^^^^^

Upvotes: 1

Views: 69

Answers (1)

chepner
chepner

Reputation: 531758

Without the let expression, you would simply write

outfits = [ (x, y, z) | z <- ["jeans", "khaki", "leggings"], y <- ["white shirt", "grey turtleneck", "pink polo", "green hoodie"], x <- ["brogues", "converse", "sandals", "adidas"] ]

A let expression requires two parts: the local bindings immediately following the keyword let, and the expression which uses those bindings, following the keyword in:

outfits = let pants = ["jeans", "khaki", "leggings"]
              shirts = ["white shirt", "grey turtleneck", "pink polo", "green hoodie"]
              shoes = ["brogues", "converse", "sandals", "adidas"]
          in [ (x, y, z) | z <- pants, y <- shirts, x <- shoes ]

Alternately, you can write this using a where clause, which puts the "main" expression first:

outfits = [ (x, y, z) | z <- pants, y <- shirts, x <- shoes ]
              where pants = ["jeans", "khaki", "leggings"]
                    shirts = ["white shirt", "grey turtleneck", "pink polo", "green hoodie"]
                    shoes = ["brogues", "converse", "sandals", "adidas"]

Applicative instead of list comprehension

I would write this using the Applicative instance for lists instead, using the 3-tuple constructor (,,), which lets you do away with boilerplate variables x, y, and z.

outfits = (,,) <$> ["jeans", "khaki", "leggings"]
               <*> ["white shirt", "grey turtleneck", "pink polo", "green hoodie"]
               <*> ["brogues", "converse", "sandals", "adidas"]

You can still use either a let expression or a where clause to provide names for each component of an outfit:

outfits = let pants = ["jeans", "khaki", "leggings"]
              shirts = ["white shirt", "grey turtleneck", "pink polo", "green hoodie"]
              shoes = ["brogues", "converse", "sandals", "adidas"]
          in (,,) <$> pants <*> shirts <*> shoes

or

outfits = (,,) <$> pants <*> shirts <*> shoes
              where pants = ["jeans", "khaki", "leggings"]
                    shirts = ["white shirt", "grey turtleneck", "pink polo", "green hoodie"]
                    shoes = ["brogues", "converse", "sandals", "adidas"]

If ... <$> ... <*> ... <*> ... looks too esoteric, you can give the pattern a more descriptive name:

outfits = mixAndMatch pants shirts shoes
           where pants = ["jeans", "khaki", "leggings"]
                 shirts = ["white shirt", "grey turtleneck", "pink polo", "green hoodie"]
                 shoes = ["brogues", "converse", "sandals", "adidas"]
                 mixAndMatch a b c = (,,) <$> a <*> b <*> c

Upvotes: 6

Related Questions