Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/ekmett/structs
Exploring how to make a strict imperative universe in the GHC runtime system.
https://github.com/ekmett/structs
Last synced: 6 days ago
JSON representation
Exploring how to make a strict imperative universe in the GHC runtime system.
- Host: GitHub
- URL: https://github.com/ekmett/structs
- Owner: ekmett
- License: other
- Created: 2015-08-21T20:26:40.000Z (about 9 years ago)
- Default Branch: master
- Last Pushed: 2024-04-20T19:50:57.000Z (7 months ago)
- Last Synced: 2024-10-16T06:42:47.234Z (20 days ago)
- Language: Haskell
- Size: 354 KB
- Stars: 48
- Watchers: 10
- Forks: 7
- Open Issues: 2
-
Metadata Files:
- Readme: README.markdown
- Changelog: CHANGELOG.markdown
- License: LICENSE
Awesome Lists containing this project
README
structs
==========[![Hackage](https://img.shields.io/hackage/v/structs.svg)](https://hackage.haskell.org/package/structs) [![Build Status](https://github.com/ekmett/structs/workflows/Haskell-CI/badge.svg)](https://github.com/ekmett/structs/actions?query=workflow%3AHaskell-CI)
This package explores strict mutable data structures in Haskell.
In particular, pointer-based data structures are effectively 'half price' due to the encoding used.
However, the result is that if you use the `slot` and `field` system wrong, you can and will `SEGFAULT`.
This means the `Internal` modules are very much internal.
Some documentation is available at
[http://ekmett.github.io/structs/Data-Struct.html](http://ekmett.github.io/structs/Data-Struct.html)Examples
--------## Non-recursive data types
We use the template haskell helper `makeStruct` to automatically convert
a Haskell `data` definition to a `Struct`.As an example, we create a type that mimics a tuple of integers.
```hs
makeStruct [d|
data TupleInts a s = TupleInts
{ tupleLeft, tupleRight :: a
}
|]
```
This declaration uses `makeStruct`, which will generate a bunch of
helper functions for us to use.Notice the extra type parameter `s` in `TupleInts a s`. This is used to
carry around state information by `structs`, and so is mandatory.```hs
-- Create a new tuple of ints.
mkTupleInts :: PrimMonad m => Int -> Int -> m (TupleInts a (PrimState m))
mkTupleInts a b = st newTupleInts a b
````newTupleInts` is a function that was auto-generated by `makeStructs`, whose
parameters are all the fields, which returns a `TupleInts` within a
`PrimMonad` context. Notice the use of `PrimState m` for the state
type parameter of `TupleInts`, which is used to carry the state around.```hs
-- set the left element of the tuple
setTupleLeft :: PrimMonad m => TupleInts a (PrimState m) -> a -> m ()
setTupleLeft tup val = setField tupleLeft tup val-- get the left element of the tuple
getTupleLeft :: PrimMonad m => TupleInts a (PrimState m) -> m a
getTupleLeft tup = getField tupleLeft tup
```The Template Haskell generates `tupleLeft, tupleRight :: Field (TupleInts a) a`, which
can be used to get and set fields with `getField, setField`. The type signature
indicates that `tupleLeft, tupleRight` extract an `a` from a `TupleInts a`.## Recursive data types
We identify recursive members of a struct with `Slot`s. These are like
```hs
makeStruct [d|
data LinkedList a s = LinkedList
{ val :: a,
next :: !(LinkedList a s) }
|]
```for this definition, `makeStruct` auto-generates
`next :: Slot (LinkedList a s) (LinkedList a s)`.
Similar to the case of `Field`, the type tells us that `next` extracts
a `LinkedList a s` from a `LinkedList a s````
-- Make an empty linked list
mkEmptyLinkedList :: LinkedList a s
mkEmptyLinkedList = Nil
````Nil` is a special value which can be assigned to any `Struct`.
```hs
-- Make a linked list node with a value
mkLinkedListNode :: PrimMonad m => a -> m (LinkedList a (PrimState m))
mkLinkedListNode a = newLinkedList a Nil
```
Once again, `newLinkedList` is auto-generated by `makeStruct` which we
use to initialize the linked list.```
-- Append a node to a linked list.
appendLinkedList :: PrimMonad m =>
LinkedList x (PrimState m)
-> x
-> m (LinkedList x (PrimState m))
appendLinkedList xs x = do
isend <- isNil <$> (get next xs)
if isend
then do
nodex <- mkLinkedListNode x
set next xs nodex
return xs
else do
xs' <- get next xs
appendLinkedList xs' x
makeStruct [d|
data LinkedList a s = LinkedList
{ val :: a,
next :: !(LinkedList a s) }
|]-- Make an empty linked list
mkEmptyLinkedList :: LinkedList a s
mkEmptyLinkedList = Nil-- Make a linked list node with a value
mkLinkedListNode :: PrimMonad m => a -> m (LinkedList a (PrimState m))
mkLinkedListNode a = newLinkedList a Nil-- Append a node to a linked list.
appendLinkedList :: PrimMonad m =>
LinkedList x (PrimState m)
-> x
-> m (LinkedList x (PrimState m))
appendLinkedList xs x = do
isend <- isNil <$> (get next xs)
if isend
then do
nodex <- mkLinkedListNode x
set next xs nodex
return xs
else do
xs' <- get next xs
appendLinkedList xs' x
```The rest is straightforward uses of `get`, `set`, `getField`, and `setField` to
manipulate the linked list as usual.FAQ
---1. Why can fields not be strict? (compiler error)
2. How do I free memory once `alloc`d?Contact Information
-------------------Contributions and bug reports are welcome!
Please feel free to contact me through github or on the #haskell IRC channel on irc.freenode.net.
-Edward Kmett