user811416
user811416

Reputation: 205

How to use a global variable within a function in Haskell

In section Incorrectly matching against a variable from chapter 3 of real world haskell, there is an example as follows:

-- file: ch03/BogusPattern.hs
data Fruit = Apple | Orange

apple = "apple"

orange = "orange"        

whichFruit :: String -> Fruit

whichFruit f = case f of
                 apple  -> Apple
                 orange -> Orange

The explanation says in case f of part, apple and orange are not treated as global variables as defined before the function declaration. They are local variables.I thought if there is no local variable owning the same name as a global variable, the global variable is not hidden.

Upvotes: 0

Views: 2298

Answers (3)

shang
shang

Reputation: 24802

The main thing here is that variables in pattern matches always introduce new variables instead of referring to existing ones. Your problem has nothing to do with global vs local variables.

If you want to match the value of f to the value of some variable like apple in a pattern you need to use pattern guards and equality tests. E.g.

whichFruit f
    | f == apple  = Apple
    | f == orange = Orange

Upvotes: 4

leftaroundabout
leftaroundabout

Reputation: 120711

Right. But there is a local variable apple here owning the same name as the global variable apple, namely apple (duh).

The crucial thing about patterns is that they don't so much compare variables as look out for specific distinguishing features, while simultaneously re-packaging all other information into new variables. The "distinguishing features" are constructor matches (always uppercase1), which don't appear in case f of { apple -> ... }, so simply all the information is passed on in the variable apple.

A more idiomatic example

data Vegetable = Tomato | Potato

data Edible = Fruit Fruit | Vegetable Vegetable

If you want a function to take an Edible argument, it's often useful to deconstruct the type. This might look thus:

canJuice :: Edible -> Bool
canJuice (Fruit Apple) = False
canJuice (Fruit Orange) = True
canJuice (Vegetable Tomato) = True
canJuice (Vegetable Potato) = False

Now, for more complicated data it's awkward to write such lots of canJuice clauses. An alternative would be to first match only on the outmost constructor, and delegate the further work elsewhere:

canJuice :: Edible -> Bool
canJuice (Fruit fruit) = fruitJuicy fruit
canJuice (Vegetable veg) = vegetableJuicy veg

vegetableJuicy :: Vegetable -> Bool
vegetableJuicy Tomato = True
vegetableJuicy Potato = False

For this to work, we needed Haskell's feature of treating any lowercase name appearing in a pattern as a new variable, which takes on the value in the "hole" of our pattern match.


1 There are also infix constructors, the best-known is list-cons (:) (they all start with a colon, like all named constructors start uppercase).

Upvotes: 1

Sibi
Sibi

Reputation: 48664

You have hit upon irrefutable patterns. As the book mentions, plain variable names and the wild card _ are examples of irrefutable patterns. Another example which will demonstrate irrefutable patterns more clearly:

data Fruit = Apple | Orange deriving (Show)

patternMatch f = case f of
  something -> Apple

Now the above program typechecks with an warning. In ghci:

ghci> patternMatch 2
Apple
ghci> patternMatch "hi"
Apple

So basically the variable something is an irrefutable pattern which matches anything.

Now,coming back to your example:

whichFruit :: String -> Fruit
whichFruit f = case f of
                 apple  -> Apple
                 orange -> Orange

Here the variable apple and orange are irrefutable patterns. They doesn't refer to the global function which you have already created. In fact you can remove the global definition of apple and orange and compile them in order to get an idea. Whatever input you give, you will always get Apple as an answer for the above code (since it is an irrefutable pattern):

ghci > whichFruit "apple"
Apple
ghci > whichFruit "orange"
Apple
ghci > whichFruit "pineApple"
Apple

How to use a global variable within a function in Haskell ?

That's pretty easy actually. Just use them in your function definition.

data Fruit = Apple | Orange deriving (Show)

apple = "apple"
orange = "orange"

giveFruit :: Fruit -> String
giveFruit Apple = apple
giveFruit Orange = orange

In ghci:

ghci> giveFruit Apple
"apple"
ghci> giveFruit Orange
"orange"

Using the variable in the function definition is straight forward.


What should be done if I want the variable to refer to a global variable owning the same name?

One way would be to use the entire module name to refer to it. Example:

module Fruit where

data Fruit = Apple | Orange deriving (Show)

apple = "apple"
orange = "orange"

giveFruit2 :: Fruit -> String
giveFruit2 apple = Fruit.apple

Upvotes: 1

Related Questions