grids

[ bsd3, data-structures, library ] [ Propose Tags ]

Arbitrary sized type-safe grids with useful combinators


[Skip to Readme]
Versions [faq] 0.1.0.0, 0.1.1.0, 0.2.0.0, 0.3.0.0, 0.4.0.0, 0.5.0.0, 0.5.0.1
Change log ChangeLog.md
Dependencies adjunctions, base (>=4.7 && <5), comonad, deepseq, distributive, singletons, vector [details]
License BSD-3-Clause
Copyright Chris Penner
Author Chris Penner
Maintainer christopher.penner@gmail.com
Category Data Structures
Home page https://github.com/ChrisPenner/grids#readme
Bug tracker https://github.com/ChrisPenner/grids/issues
Source repo head: git clone https://github.com/ChrisPenner/grids
Uploaded by ChrisPenner at Sun Mar 10 21:18:09 UTC 2019
Distributions NixOS:0.5.0.1
Downloads 473 total (132 in the last 30 days)
Rating (no votes yet) [estimated by rule of succession]
Your Rating
  • λ
  • λ
  • λ
Status Hackage Matrix CI
Docs uploaded by user
Build status unknown [no reports yet]

Modules

[Index] [Quick Jump]

Downloads

Maintainer's Corner

For package maintainers and hackage trustees


Readme for grids-0.5.0.1

[back to package description]

Grids

HACKAGE

Note this lib is still pretty new and relatively experimental, as such it doesn't have great performance characteristics. Maybe don't use it in performance critical applications.

Grids can have an arbitrary amount of dimensions, specified by a type-level list of Nats.

Each grid has Functor, Applicative, and Representable instances making it easy to do Matlab-style matrix programming. The Applicative instance operates piecewise making it easy to do most math operations. There's a Num instance for grids holding numbers, so piecewise addition is just grid1 + grid2, the fromInteger implementation allows things like myGrid * 5 to perform multiplication (although it's likely less efficient than using fmap!)

By combining with Control.Comonad.Representable.Store you can do context-wise linear transformations for things like Image Processing or Cellular Automata.

All in a typesafe package!

Grids backed by a single contiguous Vector and gain the associated performance benefits. Currently only boxed immutable vectors are supported, but let me know if you need other variants.

Here's how we might represent a Tic-Tac-Toe board which we'll fill with alternating X's and O's:

data Piece = X | O deriving Show
toPiece :: Int -> Piece
toPiece n = if even n then X
                      else O

ticTacToe :: Grid [3, 3] Piece
ticTacToe = generate toPiece

You can collapse the grid down to nested lists! The output type of toNestedLists depends on your dimensions, e.g.:

  • Grid [3, 3] Piece will generate: [[Piece]]
  • Grid [2, 2, 2] Char will generate: [[[Char]]]
  • ...etc
λ> toNestedLists ticTacToe
[ [X,O,X]
, [O,X,O]
, [X,O,X]]

You can even create a grid from nested lists! fromNestedLists returns a grid if possible, or Nothing if the provided lists don't match the structure of the grid you specify:

λ> fromNestedLists [[1, 2], [3, 4]] :: Maybe (Grid '[2, 2] Int)
Just (Grid [[1,2]
           ,[3,4]])
λ> fromNestedLists [[1], [2]] :: Maybe (Grid '[2, 2] Int)
Nothing

Grids are Representable Functors, Applicatives, Foldable, and are Traversable!

You can do things like piecewise addition using their applicative instance:

λ> let g = generate id :: Grid '[2, 3] Int
λ> g
(Grid [[0,1,2]
      ,[3,4,5]])
λ> liftA2 (+) g g
(Grid [[0,2,4]
      ,[6,8,10]])
λ> liftA2 (*) g g
(Grid [[0,1,4]
      ,[9,16,25]])

Indexing

You can index into a grid using the Coord type. The number of coordinates you need depends on the shape of the grid. Coord is really just a wrapping over a list of integers. It's recommended that you use coord to safely construct Coord values, but you can cheat and use the Coord constructor or even OverLoadedLists if you want to.

You can get a value out of a grid for a particular index out using index from Data.Functor.Rep:

λ> let g = generate id :: Grid '[2, 3] Int
λ> g
(Grid [[0,1,2]
      ,[3,4,5]])
λ> g `index` Coord [1 , 1]
4
λ> g `index` Coord [1, 0]
3
λ> g `index` Coord [0,  2]
2

You can also use the cell Lens from Data.Grid.Lens to access and mutate indices:

λ> g ^. cell (Coord [0, 1])
1
λ> g & cell (Coord [0, 1]) *~ 1000
(Grid [[0,1000,2]
      ,[3,4,5]])

Creation

You can generate a grid by providing a function over the integer position in the grid (generate) or by providing a function over the coordinate position of the cell (tabulate). Or of course you can just use pure.

You can also use the fromList and fromNestedLists functions which return a Maybe (Grid dims a) depending on whether the input list is well formed.

  • fromList :: [a] -> Maybe (Grid dims a)
  • fromNestedLists :: NestedLists dims a -> Maybe (Grid dims a)
  • generate :: (Int -> a) -> Grid dims a
  • tabulate :: (Coord dims -> a) -> Grid dims a
  • pure :: a -> Grid dims a

Updating

Use either the cell lens, or fmap, applicative, traversable. For batch updates using the underlying Vector implementation use (//)

  • (//) :: Grid dims a -> [(Coord dims, a)] -> Grid dims a

Cellular Automata

See the haddock docs for a guide on building Conway's game of life in only a few lines.

Convolution

Coming soon