I'm trying to learn Haskell by creating a board game. I currently have a game that is [[Char]] and I'm trying to create another board of the same columns and rows filled with the character "a". How do I go about doing so? Also can you explain how storing a value and accessing would work?
Rather than creating a new board the same size but with different content, it may be helpful to think of this operation as replacing the content of an existing board. Of course, because Haskell is an immutable language, it amounts to the same thing - the only way to change something is to produce a new version of it - but it should help you to see that this is fundamentally a mapping operation.
replaceWithA :: [[a]] -> [[Char]]
replaceWithA xss = map (map (const 'a')) xss
-- or, point-free:
replaceWithA = map (map (const 'a'))
-- or, as a list comprehension:
replaceValues xss = [['a' | x <- xs] | xs <- xss]
If you wanna go deep you can get the compiler to write this code for you. The Functor
type class generalises map
to structures that aren't simple lists:
class Functor f where
fmap :: (a -> b) -> f a -> f b
In what sense does this generalise map
? If you replace f
with []
you can see that fmap
has the same signature as map
:
fmap :: (a -> b) -> [a] -> [b]
Indeed, this is how []
's Functor
instance is implemented:
instance Functor [] where
fmap = map
Anyway, GHC knows how to write Functor
instances all by itself. I'm going to define a newtype
wrapper for 2D lists and utter the magic words deriving Functor
!
{-# LANGUAGE DeriveFunctor #-}
import Data.Functor
newtype TwoDimensional a = TwoDimensional { getTwoDimensional :: [[a]] } deriving Functor
The generated fmap
will have the following signature:
fmap :: (a -> b) -> TwoDimensional a -> TwoDimensional b
Now you can use a standard bit of machinery for replacing elements with a fixed value:
replaceWithA :: TwoDimensional a -> TwoDimensional Char
replaceWithA = ('a' <$)
As you gain experience with Haskell and its standard abstractions like Functor
you'll get better at spotting when a given operation is an instance of a more general pattern. Setting up your types carefully allows you to delegate lots of boilerplate to the compiler, allowing you to tersely and declaratively solve the interesting part of your problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With