Compare commits

...

10 Commits

Author SHA1 Message Date
Sanchayan Maity 7ee4cb51b2 Switch to using cabal 2023-01-03 21:18:17 +05:30
Sanchayan Maity 277dc29645 src: Lib: Use infix fmap 2021-05-08 14:31:00 +05:30
Sanchayan Maity 27ef8809b8 Update stack LTS 2021-05-08 14:30:49 +05:30
Sanchayan Maity d69d5eea05 Bump to 16.5 LTS and drop unused servant packages 2020-10-08 18:59:05 +05:30
Sanchayan Maity e039f32bc5 Update README
Signed-off-by: Sanchayan Maity <maitysanchayan@gmail.com>
2020-08-12 18:19:26 +05:30
Sanchayan Maity a70e8d66ba Update README
Signed-off-by: Sanchayan Maity <maitysanchayan@gmail.com>
2020-08-08 19:09:53 +05:30
Sanchayan Maity 3053851eb1 README: Add resources related to fix point
Signed-off-by: Sanchayan Maity <maitysanchayan@gmail.com>
2020-08-01 12:16:50 +05:30
Sanchayan Maity 09bfc5582b Update README
Signed-off-by: Sanchayan Maity <maitysanchayan@gmail.com>
2020-07-28 21:03:46 +05:30
Sanchayan Maity e4c13abefa src: Refactor DB and Logger out of lib
Signed-off-by: Sanchayan Maity <maitysanchayan@gmail.com>
2020-07-28 18:27:33 +05:30
Sanchayan Maity 08d89b1663 app: Main: Add a running example of the DSL
Signed-off-by: Sanchayan Maity <maitysanchayan@gmail.com>
2020-07-28 11:59:00 +05:30
13 changed files with 202 additions and 145 deletions

2
.gitignore vendored
View File

@ -1,3 +1,3 @@
free-monad-example.cabal
.stack-work/
dist-newstyle
*~

View File

@ -1 +1,44 @@
# free-monad-example
# Free Monad sample project
Using [Hydra](https://github.com/graninas/Hydra) as a reference for study.
# Library
https://hackage.haskell.org/package/free
# Resources
- [What are free monads](https://stackoverflow.com/questions/13352205/what-are-free-monads)
- [You could have invented free monads](http://www.haskellforall.com/2012/06/you-could-have-invented-free-monads.html)
- [Purify code using free monads](http://www.haskellforall.com/2012/07/purify-code-using-free-monads.html)
- [What does free buy us](https://www.parsonsmatt.org/2017/09/22/what_does_free_buy_us.html)
- [Random gist](https://gist.github.com/CMCDragonkai/165d9a598b8fb333ea65)
- [Free Monads](https://www.tweag.io/blog/2018-02-05-free-monads/)
- [Many roads to free monads](https://www.schoolofhaskell.com/user/dolio/many-roads-to-free-monads)
- [Hierarchical Free Monads & Software Design in Functional Programming by Alexander Granin](https://www.youtube.com/watch?v=3GKQ4ni2pS0)
# Free Monads Performance
- [Asymptotic Improvement of Computations over Free Monads](https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.143.2323&rep=rep1&type=pdf)
- [Free Monads for less - Part 1](http://comonad.com/reader/2011/free-monads-for-less/)
- [Free Monads for less - Part 2](http://comonad.com/reader/2011/free-monads-for-less-2/)
- [Free Monads for less - Part 3](http://comonad.com/reader/2011/free-monads-for-less-3/)
- [Kan Extensions](http://comonad.com/reader/2008/kan-extensions/)
- [The Free and The Furious: And by 'Furious' I mean Codensity](https://www.youtube.com/watch?v=EiIZlX_k89Y)
- [Effects for less - Great talk, must watch](https://www.youtube.com/watch?v=0jI-AlWEwYI)
- [Problem Set - The Codensity transformation](http://blog.ezyang.com/2012/01/problem-set-the-codensity-transformation/)
# Fix Point
- [Fix and Recursion](https://en.wikibooks.org/wiki/Haskell/Fix_and_recursion)
- [The Y Combinator](https://mvanier.livejournal.com/2700.html)
- [The Y Combinator - Slight Return](https://mvanier.livejournal.com/2897.html)
- [A series of articles on Recursion schemes](https://blog.sumtypeofway.com/archive.html)
- [Understanding Algebras](https://www.schoolofhaskell.com/user/bartosz/understanding-algebras)
# Jupyter Notebooks (Includes the one used for Free Monad)
- [https://gitlab.com/SanchayanMaity/haskell-notebooks](https://gitlab.com/SanchayanMaity/haskell-notebooks)
# youtube Recording of the talk
https://www.youtube.com/watch?v=fhu1UQel5eo

View File

@ -1,6 +1,21 @@
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Database.PostgreSQL.Simple
import Data.Text (pack)
import Lib
dslOperations :: Connection -> DSL ()
dslOperations conn = do
logMsg "Logging from inside the DSL"
xs <- runQuery conn "select 2 + 2"
logMsg $ pack $ show (xs :: [Only Int])
main :: IO ()
main = putStrLn "Free monad example"
main = do
putStrLn "Free monad example"
conn <- connect defaultConnectInfo {
connectDatabase = "postgres"
}
runDSL $ dslOperations conn

67
free-monad-example.cabal Normal file
View File

@ -0,0 +1,67 @@
cabal-version: 1.12
name: free-monad-example
version: 0.1.0.0
description:
Please see the README on GitHub at <https://github.com/sanchayanmaity/free-monad-example#readme>
homepage: https://github.com/sanchayanmaity/free-monad-example#readme
bug-reports: https://github.com/sanchayanmaity/free-monad-example/issues
author: Sanchayan Maity
maintainer: maitysanchayan@gmail.com
copyright: 2020 Sanchayan Maity
license: BSD3
license-file: LICENSE
build-type: Simple
extra-source-files: README.md
source-repository head
type: git
location: https://github.com/sanchayanmaity/free-monad-example
library
exposed-modules:
DB.Interpreter
DB.Language
Lib
Logger.Interpreter
Logger.Language
other-modules: Paths_free_monad_example
hs-source-dirs: src
ghc-options: -Wall -Wunused-binds
build-depends:
base >=4.7 && <5
, free
, postgresql-simple
, text
default-language: Haskell2010
executable free-monad-example-exe
main-is: Main.hs
other-modules: Paths_free_monad_example
hs-source-dirs: app
ghc-options: -Wall -Wunused-binds -threaded -rtsopts -with-rtsopts=-N
build-depends:
base >=4.7 && <5
, free
, free-monad-example
, postgresql-simple
, text
default-language: Haskell2010
test-suite free-monad-example-test
type: exitcode-stdio-1.0
main-is: Spec.hs
other-modules: Paths_free_monad_example
hs-source-dirs: test
ghc-options: -Wall -Wunused-binds -threaded -rtsopts -with-rtsopts=-N
build-depends:
base >=4.7 && <5
, free
, free-monad-example
, postgresql-simple
, text
default-language: Haskell2010

View File

@ -1,13 +0,0 @@
cradle:
stack:
- path: "./src"
component: "free-monad-example:lib"
- path: "./app/Main.hs"
component: "free-monad-example:exe:free-monad-example-exe"
- path: "./app/Paths_free_monad_example.hs"
component: "free-monad-example:exe:free-monad-example-exe"
- path: "./test"
component: "free-monad-example:test:free-monad-example-test"

View File

@ -22,8 +22,6 @@ dependencies:
- base >= 4.7 && < 5
- free
- postgresql-simple
- servant
- servant-server
- text
ghc-options:

13
src/DB/Interpreter.hs Normal file
View File

@ -0,0 +1,13 @@
module DB.Interpreter where
import Control.Exception (throwIO)
import Control.Monad.Free.Church (foldF)
import Database.PostgreSQL.Simple (Connection, Query)
import DB.Language
interpretSqlDBMethod :: Connection -> Query -> SqlDBMethodF a -> IO a
interpretSqlDBMethod conn squery (SqlDBMethod runner next) = next <$> runner conn squery
interpretSqlDBMethod _ _ (SqlThrowException exception _) = throwIO exception
runDB :: Connection -> Query -> SqlDb a -> IO a
runDB conn squery = foldF $ interpretSqlDBMethod conn squery

23
src/DB/Language.hs Normal file
View File

@ -0,0 +1,23 @@
{-# LANGUAGE GADTs #-}
module DB.Language where
import Control.Exception (Exception)
import Control.Monad.Free.Church (F, liftF)
import Database.PostgreSQL.Simple (Connection, FromRow, Query)
data SqlDBMethodF next where
SqlDBMethod :: FromRow r => (Connection -> Query -> IO [r]) -> ([r] -> next) -> SqlDBMethodF next
SqlThrowException :: Exception e => e -> (a -> next) -> SqlDBMethodF next
instance Functor SqlDBMethodF where
fmap f (SqlDBMethod runner next) = SqlDBMethod runner (f . next)
fmap f (SqlThrowException exceptionMsg next) = SqlThrowException exceptionMsg (f . next)
type SqlDb = F SqlDBMethodF
sqlDbMethod :: FromRow r => (Connection -> Query -> IO [r]) -> SqlDb [r]
sqlDbMethod action = liftF $ SqlDBMethod action id
sqlThrowException :: Exception e => e -> SqlDb a
sqlThrowException exception = liftF $ SqlThrowException exception id

View File

@ -1,55 +1,15 @@
{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Lib where
import Control.Exception
import Control.Monad.Free.Church
import Database.PostgreSQL.Simple
import Data.Text
data SqlDBMethodF next where
SqlDBMethod :: FromRow r => (Connection -> Query -> IO [r]) -> ([r] -> next) -> SqlDBMethodF next
SqlThrowException :: Exception e => e -> (a -> next) -> SqlDBMethodF next
instance Functor SqlDBMethodF where
fmap f (SqlDBMethod runner next) = SqlDBMethod runner (f . next)
fmap f (SqlThrowException exceptionMsg next) = SqlThrowException exceptionMsg (f . next)
type SqlDb = F SqlDBMethodF
sqlDbMethod :: FromRow r => (Connection -> Query -> IO [r]) -> SqlDb [r]
sqlDbMethod action = liftF $ SqlDBMethod action id
sqlThrowException :: Exception e => e -> SqlDb a
sqlThrowException exception = liftF $ SqlThrowException exception id
interpretSqlDBMethod :: Connection -> Query -> SqlDBMethodF a -> IO a
interpretSqlDBMethod conn squery (SqlDBMethod runner next) = next <$> runner conn squery
interpretSqlDBMethod _ _ (SqlThrowException exception _) = throwIO exception
runDB :: Connection -> Query -> SqlDb a -> IO a
runDB conn squery = foldF $ interpretSqlDBMethod conn squery
data LoggerMethodF next where
LogMessage :: Text -> (() -> next) -> LoggerMethodF next
instance Functor LoggerMethodF where
fmap f (LogMessage msg next) = LogMessage msg (f . next)
type Logger = F LoggerMethodF
logMessage :: Text -> Logger ()
logMessage msg = liftF $ LogMessage msg id
interpretLoggerMethod :: LoggerMethodF a -> IO a
interpretLoggerMethod (LogMessage msg next) = next <$> print msg
runLogger :: Logger () -> IO ()
runLogger = foldF interpretLoggerMethod
import Control.Monad.Free.Church (F, foldF, liftF, MonadFree)
import Database.PostgreSQL.Simple (Connection, FromRow, Query, query_)
import Data.Text (Text)
import DB.Interpreter (runDB)
import DB.Language (SqlDb, sqlDbMethod)
import Logger.Interpreter (runLogger)
import Logger.Language (Logger, logMessage)
data DSLMethod next where
LogMsg :: Logger () -> (() -> next) -> DSLMethod next
@ -63,9 +23,9 @@ type DSL = F DSLMethod
interpretDSLMethod :: DSLMethod a -> IO a
interpretDSLMethod (LogMsg logger next) =
fmap next $ runLogger logger
next <$> runLogger logger
interpretDSLMethod (RunQuery conn squery sql next) =
fmap next $ runDB conn squery sql
next <$> runDB conn squery sql
runDSL :: DSL () -> IO ()
runDSL = foldF interpretDSLMethod

11
src/Logger/Interpreter.hs Normal file
View File

@ -0,0 +1,11 @@
module Logger.Interpreter where
import Control.Monad.Free.Church (foldF)
import Logger.Language
interpretLoggerMethod :: LoggerMethodF a -> IO a
interpretLoggerMethod (LogMessage msg next) = next <$> print msg
runLogger :: Logger () -> IO ()
runLogger = foldF interpretLoggerMethod

18
src/Logger/Language.hs Normal file
View File

@ -0,0 +1,18 @@
{-# LANGUAGE GADTs #-}
module Logger.Language where
import Control.Monad.Free.Church (F, liftF)
import Data.Text (Text)
data LoggerMethodF next where
LogMessage :: Text -> (() -> next) -> LoggerMethodF next
instance Functor LoggerMethodF where
fmap f (LogMessage msg next) = LogMessage msg (f . next)
type Logger = F LoggerMethodF
logMessage :: Text -> Logger ()
logMessage msg = liftF $ LogMessage msg id

View File

@ -1,66 +0,0 @@
# This file was automatically generated by 'stack init'
#
# Some commonly used options have been documented as comments in this file.
# For advanced use and comprehensive documentation of the format, please see:
# https://docs.haskellstack.org/en/stable/yaml_configuration/
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
# A snapshot resolver dictates the compiler version and the set of packages
# to be used for project dependencies. For example:
#
# resolver: lts-3.5
# resolver: nightly-2015-09-21
# resolver: ghc-7.10.2
#
# The location of a snapshot can be provided as a file or url. Stack assumes
# a snapshot provided as a file might change, whereas a url resource does not.
#
# resolver: ./custom-snapshot.yaml
# resolver: https://example.com/snapshots/2018-01-01.yaml
resolver: lts-15.15
# User packages to be built.
# Various formats can be used as shown in the example below.
#
# packages:
# - some-directory
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
# subdirs:
# - auto-update
# - wai
packages:
- .
# Dependency packages to be pulled from upstream that are not in the resolver.
# These entries can reference officially published versions as well as
# forks / in-progress versions pinned to a git hash. For example:
#
# extra-deps:
# - acme-missiles-0.3
# - git: https://github.com/commercialhaskell/stack.git
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
#
# extra-deps: []
# Override default flag values for local packages and extra-deps
# flags: {}
# Extra package databases containing global packages
# extra-package-dbs: []
# Control whether we use the GHC we find on the path
# system-ghc: true
#
# Require a specific version of stack, using version ranges
# require-stack-version: -any # Default
# require-stack-version: ">=2.3"
#
# Override the architecture used by stack, especially useful on Windows
# arch: i386
# arch: x86_64
#
# Extra directories used by stack for building
# extra-include-dirs: [/path/to/dir]
# extra-lib-dirs: [/path/to/dir]
#
# Allow a newer minor version of GHC than the snapshot specifies
# compiler-check: newer-minor

View File

@ -1,12 +0,0 @@
# This file was autogenerated by Stack.
# You should not edit this file by hand.
# For more information, please see the documentation at:
# https://docs.haskellstack.org/en/stable/lock_files
packages: []
snapshots:
- completed:
size: 496112
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/15/15.yaml
sha256: 86169722ad0056ffc9eacc157ef80ee21d7024f92c0d2961c89ccf432db230a3
original: lts-15.15