Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I create an 2-D array in Haskell?

Tags:

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?

like image 438
Annie Hua Avatar asked Mar 28 '17 17:03

Annie Hua


1 Answers

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.

like image 172
Benjamin Hodgson Avatar answered Sep 25 '22 10:09

Benjamin Hodgson