Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/jjba23/free-alacarte
Free monads based on intuitions from the Data types à la Carte paper. Combine functors and make embedded DSLs in Haskell.
https://github.com/jjba23/free-alacarte
composition dependency-injection dsl free functional-programming functor haskell library monad
Last synced: 25 days ago
JSON representation
Free monads based on intuitions from the Data types à la Carte paper. Combine functors and make embedded DSLs in Haskell.
- Host: GitHub
- URL: https://github.com/jjba23/free-alacarte
- Owner: jjba23
- License: lgpl-3.0
- Created: 2024-09-02T13:21:54.000Z (2 months ago)
- Default Branch: trunk
- Last Pushed: 2024-09-29T22:01:43.000Z (about 1 month ago)
- Last Synced: 2024-09-30T09:03:07.610Z (about 1 month ago)
- Topics: composition, dependency-injection, dsl, free, functional-programming, functor, haskell, library, monad
- Language: Haskell
- Homepage: https://hackage.haskell.org/package/free-alacarte
- Size: 777 KB
- Stars: 10
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.org
- License: COPYING
Awesome Lists containing this project
README
* Free à la Carte
Free monads based on from intuitions from the Data types à la Carte paper. Combine functors and make embedded DSLs in Haskell.
#+begin_html
#+end_html* How do you use this ?
Read further for the classical teletype and filesystem example, and much more explanation.
** Free a la Carte tests
The tests for Free a la Carte are a great place to learn the basics and take inspiration.Check it out: [[./test/Free/AlaCarte/Test.hs][./test/Free/AlaCarte/Test.hs]]
** Teletype + Filesystem example
This section gives a brief demonstration of using free monads to model effects.
Four effectful functions are defined, categorized into two separate data types.
#+begin_src haskell
data Teletype a
= GetChar (Char -> a)
| PutChar Char a
deriving (Functor)data FileSystem a
= ReadFile FilePath (String -> a)
| WriteFile FilePath String a
deriving (Functor)
#+end_srcIf you are into it, you can also write the Functor instances by hand, for your free monads, e.g.:
#+begin_src haskell
instance Functor Teletype where
fmap :: (a -> b) -> Teletype a -> Teletype b
fmap f = \case
GetChar g -> GetChar (f . g)
PutChar c g -> PutChar c (f g)#+end_src
An ~exec~ function can execute values of these data types using the ~Free~ free monad. This uses intuitions of category theory to describe imperative sequence of computations as a fold over a functor. *NOTE*: the ~exec~ function is provided by this library and you don't need to implement it yourself.
#+begin_src haskell
exec :: Exec f => Free f a -> IO a
exec = foldFree return execAlgebra
#+end_srcYou should then write the ~Exec~ instances, in other words, the concrete implementations.
*NOTE*: the typeclass ~Exec~, and ~Exec (f :+: g)~ instance are also provided by this library, and you don't need to implement it yourself.#+begin_src haskell
class Functor f => Exec f where
execAlgebra :: f (IO a) -> IO ainstance (Exec f, Exec g) => Exec (f :+: g) where
execAlgebra = \case
Left' e -> execAlgebra e
Right' e -> execAlgebra e-- write your own implementations
instance Exec Teletype where
execAlgebra = \case
GetChar f -> Prelude.getChar >>= f
PutChar c io -> Prelude.putChar c >> ioinstance Exec FileSystem where
execAlgebra (ReadFile path f) = Prelude.readFile path >>= f
execAlgebra (WriteFile path s f) = Prelude.writeFile path s >> f#+end_src
Then we can define some smart constructors to create our embedded DSL and save us some boilerplate, while adding syntactic sugar.
#+begin_src haskell
getChar :: (Teletype :<: f) => Free f Char
getChar = injectFree (GetChar Pure)putChar :: (Teletype :<: f) => Char -> Free f ()
putChar c = injectFree (PutChar c (Pure ()))readFile :: (FileSystem :<: f) => FilePath -> Free f String
readFile path = injectFree (ReadFile path Pure)writeFile :: (FileSystem :<: f) => FilePath -> String -> Free f ()
writeFile path s = injectFree (WriteFile path s (Pure ()))
#+end_srcThe ~cat~ function serves as an example of composition. In the following, I use a more general type than that used in the paper. Here we use ~mapM_~ instead of ~mapM~ to discard the resulting list of unit.
#+begin_src haskell
cat :: (FileSystem :<: f, Teletype :<: f) => FilePath -> Free f ()
cat path = mapM_ putChar =<< readFile path
#+end_srcThe following example uses the ~cat~ function to print the content of the README.md file in this directory.
#+begin_src haskell
main :: IO ()
main = exec @(FileSystem :+: Teletype) $ cat "README.md"
#+end_src* More on the topic
I can only extremely recommend the following resources to gain more understanding about the ideas and intuitions behind this library, and behind Data types à la Carte.
- Original paper, by Wouter Swierstra: https://webspace.science.uu.nl/~swier004/publications/2008-jfp.pdf
- Powerpoint explanation, by Wouter Swierstra: https://webspace.science.uu.nl/~swier004/talks/2018-fp-ams.pdf
- Good alternative explanation and implementation, by Travis Cardwell: https://www.extrema.is/blog/2022/04/04/data-types-a-la-carte#+begin_html
#+end_html* Projects using Free a la Carte
- WikiMusic API: https://github.com/jjba23/wikimusic-api
- WikiMusic SSR: https://github.com/jjba23/wikimusic-ssr
- Yak: https://github.com/jjba23/yak
- Rephs: https://github.com/jjba23/rephs
- HSResumeBuilder: https://github.com/jjba23/hsresumebuilder
- JJBA: https://github.com/jjba23/jjba