Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/meooow25/priority-applicative
Applicatives with priorities
https://github.com/meooow25/priority-applicative
Last synced: 8 days ago
JSON representation
Applicatives with priorities
- Host: GitHub
- URL: https://github.com/meooow25/priority-applicative
- Owner: meooow25
- License: mit
- Created: 2023-10-18T14:14:45.000Z (about 1 year ago)
- Default Branch: master
- Last Pushed: 2023-10-22T00:12:41.000Z (about 1 year ago)
- Last Synced: 2023-10-22T02:36:28.883Z (about 1 year ago)
- Language: Haskell
- Homepage:
- Size: 7.81 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# priority-applicative
Applicatives with priorities
## Usage
`Control.Applicative.Prio` allows controlling the order of execution of [`Applicative`][1] effects
with priorities. Effects are executed in order of increasing priority. If two priorities are equal,
either effect may be executed first.A simple example:
```hs
import Control.Applicative.Prio (PrioAp)
import qualified Control.Applicative.Prio as PrioApprioPrint :: (Ord k, Show k, Show a) => k -> a -> PrioAp k IO a
prioPrint k x =
PrioAp.lift k (x <$ putStrLn ("priority " ++ show k ++ ": " ++ show x))foo :: IO (Int, (String, Bool), Double, Char)
foo = PrioAp.unlift $
(,,,) <$>
prioPrint 3 42 <*>
((,) <$> prioPrint 1 "hello" <*> prioPrint 5 False) <*>
prioPrint 4 3.14 <*>
prioPrint 2 'p'
```When `foo` is run, its value will be `(42,("hello",False),3.14,'p')` as expected.
But it will print out the following:```
priority 1: "hello"
priority 2: 'p'
priority 3: 42
priority 4: 3.14
priority 5: False
````Control.Alternative.Prio` extends the behavior to [`Alternative`][2]. Alternative branches are
chosen in order of the minimum priority effect in the branch.```hs
import qualified Control.Alternative.Prio as PrioAltbar :: IO ()
bar = PrioAlt.unlift $
(PrioAlt.lift 4 (print 4) *> PrioAlt.lift 2 (print 2)) <|>
(PrioAlt.lift 1 (print 1) *> PrioAlt.lift 3 empty)
```The minimum priorities in the two branches are 2 and 1. So the second is chosen first. When the
second branch fails, it switches to the first branch.
The output of running `bar` is:```
1
2
4
```## More examples
### Parsing in priority order
```hs
import Control.Alternative.Prio as PrioAltdata Name = Name LastName FirstName
data Person = Person (Either Name Nickname) Age MailpFirstName :: Parser FirstName
pLastName :: Parser LastName
pNickname :: Parser Nickname
pAge :: Parser Age
pMail :: Parser Mail-- The data is present in sorted order:
-- age, firstName, lastName, mail OR
-- age, mail, nicknamepPerson :: Parser Person
pPerson = PrioAlt.unlift $
Person <$>
((Left . Name <$>
PrioAlt.lift "lastName" pLastName <*>
PrioAlt.lift "firstName" pFirstName) <|>
Right <$> PrioAlt.lift "nickname" pNickname) <*>
PrioAlt.lift "age" pAge <*>
PrioAlt.lift "mail" pMail
```Without `PrioAlt` one would need to
* Write an fmap function to swap around the arguments, instead of a plain constructor
* Manually branch the parser after `age`### Sorting
```hs
import Control.Monad.Trans.Writer.Lazy -- from transformers
import qualified Control.Applicative.Prio as PrioApsort :: Ord a => [a] -> [a]
sort = execWriter . PrioAp.unlift . traverse_ (\x -> PrioAp.lift x (tell [x]))
```### Non-empty heap
```hs
import Data.Functor.Const
import Control.Applicative.Prio (PrioAp, Some(..))
import qualified Control.Applicative.Prio as PrioAptype Heap p a = Some (PrioAp p (Const a))
singleton :: p -> a -> Heap p a
singleton p x = Some (PrioAp.lift p (Const x))union :: Ord p => Heap p a -> Heap p a -> Heap p a
union (Some h1) (Some h2) = Some (h1 *> h2)minView :: Heap p a -> Maybe ((p, a), Heap p a)
minView (Some h) = fmap f (PrioAp.minView h)
where
f ((p, Some (Const x)), h) = ((p, x), h)
```## Performance
The current implementation is backed by a sorted list for `PrioAp` and sorted tree for `PrioAlt`.
This makes it simple and easy to understand, but it's not very efficient. I have some ideas on how
to improve performance.## Acknowledgements
This package uses the technique used in [`Control.Applicative.Permutations`][3] in the
`parser-combinators` package, which is in turn inspired by the functional pearl, [_Parsing
Permutation Phrases_][4], by Arthur Baars, Andres Löh and S. Doaitse Swierstra.[1]: https://hackage.haskell.org/package/base/docs/Control-Applicative.html#t:Applicative
[2]: https://hackage.haskell.org/package/base/docs/Control-Applicative.html#t:Alternative
[3]: https://hackage.haskell.org/package/parser-combinators-1.3.0/docs/Control-Applicative-Permutations.html
[4]: https://www.cs.ox.ac.uk/jeremy.gibbons/wg21/meeting56/loeh-paper.pdf