# Indexable: Containers Subsetting and Slicing

This is a Haskell package mainly used for subsetting and slicing all kinds of containers or nested containers that may have different types.

This package defined a typeclass called "indexable", whose main operator is slicing operator `(&)`. The folllowing is a small example (in ghci)

```haskell ghci
import Indexable    -- import the package

lst = [0..10]       -- create a list
lst&[""]            -- select all elements
lst&["0,3,8"]       -- select elements of index 0, 3 and 8
lst&["2:"]          -- select all elements from index 2 to the end
lst&[":-1"]         -- select all elements except the last one
lst&["1:-1:2"]      -- select elements in range with step size 2
lst&["1:3,4:6,8"]   -- elements 1:3, 4:6 and the 8th.

```
the outputs in ghci are:

```haskell ghci
[0,1,2,3,4,5,6,7,8,9,10]
[0,3,8]
[2,3,4,5,6,7,8,9,10]
[0,1,2,3,4,5,6,7,8,9]
[1,3,5,7,9]
[1,2,4,5,8]
```

# One Dimension Slicing
A slicing request is a string with multiple slicing terms connected by separator ",", like

```haskell ghci
slicing_request1 = "1,3,5"                -- has three terms "1", "3" and "5"
slicing_request2 = "1:10:2,11:20,78,90"   -- has four terms "1:10:2", "11:20", "78", "90"
```

Each term is a string represents a list of keys or indexes in numbers. 

### Index Slicing (for list-like container)
The general version of a term is `"a:b:c"` where `a` is the starting index, `b` is the ending index, `c` is the step. The defualt value (when term is `":b:c"`) is `0`, default for `b` is the length of the container, default value for `c` is `1`.

Like in python, value of b can be negative, in this case it means how many elements you want to dissmiss at the end of the container.

### Key Slicing (for map-like container)
Slicing for map-like containers has only one requirement on the type of key, that is readable. Each slicing term is composed in the form

```haskell ghci
key_slice_term1 = "xxxx[1:10:2]xxxxx"
key_slice_term2 = "xxxx"
```
i.e. it can be a string represent the key, or a string with "[slicing term]" (not request, just term) inside. You can still connect terms together as a key slicing requrest like below

```haskell ghci
key_slice_request = "key[0:5], newkey1, oldkey2, other[3:8:2]key"
```

## Multiple Dimension Slicing

### Lift Nested Container into Composed Type
The multiple dimensional containers need to be constructed as a composed type. Below is an example of map of lists (in ghci).

```haskell ghci
import Indexable                        -- import indexable package
import Data.Map.Strict                  -- import the map container
import Data.Functor.Compose             -- import the compose functor

mlst = [(i,[i..i+3]) | i<-[0..3]]       -- an list of tuples with first value as key
                                        -- second as a list of numbers.

mmap = fromList mlst                    -- construct map of lists, the type is (Map Integer [Integer])

cmap = Compose mmap                     -- lift nested container to composed type
                                        -- so we can use slicing operator (&) on it
```

### Apply Slicing on Composed Containers
We can slice on any nested container with a list of slicing requests, with the ith request perform on the ith dimension. Continues on the example above:

```haskell ghci

cmap&[":","0,1"]        -- all rows in the map, with first two entries of each list
cmap&["1:3","1:-1"]     -- rows (items in the map) 1 and 2, with each list from index 1 to len-1
cmap&["::2"]            -- all rows with step 2, and all elements in list of each row

```

the outputs are:

```
Compose (fromList [(0,[0,1]),(1,[1,2]),(2,[2,3]),(3,[3,4])])

Compose (fromList [(1,[2,3]),(2,[3,4])])

Compose (fromList [(0,[0,1,2,3]),(2,[2,3,4,5])])
```

For higher level nested containers, we need to first construct composed type with the corresponding level. For example, the map of lists of vectors, say `mlv`, needs to be composed twice, `cmlv = Compose $ Compose mlv` before applying `(&)` operator.

# Current Instances of Indexable

1. Data.List
1. Data.Map.Strict
1. Data.HashMap.Strict
1. Data.IntMap
1. Data.Sequence
1. Data.Vector

and all finite nested combination of above containers.

# Create New Instances

To add new containers `f` with key `b` as indexable, two minimal functions need to be provided:

1. `(&?) :: f a -> b -> Maybe a` : a function safely return the element of type `a` by a key of tyep `b`.
1. `fromLst :: (Typeable b) => [(b,a)] -> f a` : a function that construct container `f` of value `a` from a list of key value pairs. (list-like containers should forget the keys in this function). 

After appling these two function, new container can be nested with other containers and using slicing operator `(&)`.

# Other Operators Available in Indexable

1. `(&?)` safe lookup
1. `(&!)` unsafe lookup


# How to Install

This package is currently in the candidate package pool of Hackage. You can download from there, or simply clone this git.