Skip to content

Commit dc19784

Browse files
authored
Convert Log into a dynamically dispatched effect (#10)
1 parent 1f58990 commit dc19784

File tree

4 files changed

+81
-90
lines changed

4 files changed

+81
-90
lines changed

.github/workflows/haskell-ci.yml

Lines changed: 38 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
#
99
# For more information, see https://github.com/haskell-CI/haskell-ci
1010
#
11-
# version: 0.17.20231010
11+
# version: 0.19.20240514
1212
#
13-
# REGENDATA ("0.17.20231010",["github","--config=cabal.haskell-ci","cabal.project"])
13+
# REGENDATA ("0.19.20240514",["github","--config=cabal.haskell-ci","cabal.project"])
1414
#
1515
name: Haskell-CI
1616
on:
@@ -27,24 +27,29 @@ jobs:
2727
timeout-minutes:
2828
60
2929
container:
30-
image: buildpack-deps:bionic
30+
image: buildpack-deps:jammy
3131
continue-on-error: ${{ matrix.allow-failure }}
3232
strategy:
3333
matrix:
3434
include:
35-
- compiler: ghc-9.8.1
35+
- compiler: ghc-9.10.1
3636
compilerKind: ghc
37-
compilerVersion: 9.8.1
37+
compilerVersion: 9.10.1
3838
setup-method: ghcup
3939
allow-failure: false
40-
- compiler: ghc-9.6.3
40+
- compiler: ghc-9.8.2
4141
compilerKind: ghc
42-
compilerVersion: 9.6.3
42+
compilerVersion: 9.8.2
4343
setup-method: ghcup
4444
allow-failure: false
45-
- compiler: ghc-9.4.7
45+
- compiler: ghc-9.6.5
4646
compilerKind: ghc
47-
compilerVersion: 9.4.7
47+
compilerVersion: 9.6.5
48+
setup-method: ghcup
49+
allow-failure: false
50+
- compiler: ghc-9.4.8
51+
compilerKind: ghc
52+
compilerVersion: 9.4.8
4853
setup-method: ghcup
4954
allow-failure: false
5055
- compiler: ghc-9.2.8
@@ -62,32 +67,17 @@ jobs:
6267
compilerVersion: 8.10.7
6368
setup-method: ghcup
6469
allow-failure: false
65-
- compiler: ghc-8.8.4
66-
compilerKind: ghc
67-
compilerVersion: 8.8.4
68-
setup-method: hvr-ppa
69-
allow-failure: false
7070
fail-fast: false
7171
steps:
7272
- name: apt
7373
run: |
7474
apt-get update
7575
apt-get install -y --no-install-recommends gnupg ca-certificates dirmngr curl git software-properties-common libtinfo5
76-
if [ "${{ matrix.setup-method }}" = ghcup ]; then
77-
mkdir -p "$HOME/.ghcup/bin"
78-
curl -sL https://downloads.haskell.org/ghcup/0.1.19.5/x86_64-linux-ghcup-0.1.19.5 > "$HOME/.ghcup/bin/ghcup"
79-
chmod a+x "$HOME/.ghcup/bin/ghcup"
80-
"$HOME/.ghcup/bin/ghcup" install ghc "$HCVER" || (cat "$HOME"/.ghcup/logs/*.* && false)
81-
"$HOME/.ghcup/bin/ghcup" install cabal 3.10.1.0 || (cat "$HOME"/.ghcup/logs/*.* && false)
82-
else
83-
apt-add-repository -y 'ppa:hvr/ghc'
84-
apt-get update
85-
apt-get install -y "$HCNAME"
86-
mkdir -p "$HOME/.ghcup/bin"
87-
curl -sL https://downloads.haskell.org/ghcup/0.1.19.5/x86_64-linux-ghcup-0.1.19.5 > "$HOME/.ghcup/bin/ghcup"
88-
chmod a+x "$HOME/.ghcup/bin/ghcup"
89-
"$HOME/.ghcup/bin/ghcup" install cabal 3.10.1.0 || (cat "$HOME"/.ghcup/logs/*.* && false)
90-
fi
76+
mkdir -p "$HOME/.ghcup/bin"
77+
curl -sL https://downloads.haskell.org/ghcup/0.1.20.0/x86_64-linux-ghcup-0.1.20.0 > "$HOME/.ghcup/bin/ghcup"
78+
chmod a+x "$HOME/.ghcup/bin/ghcup"
79+
"$HOME/.ghcup/bin/ghcup" install ghc "$HCVER" || (cat "$HOME"/.ghcup/logs/*.* && false)
80+
"$HOME/.ghcup/bin/ghcup" install cabal 3.10.2.0 || (cat "$HOME"/.ghcup/logs/*.* && false)
9181
env:
9282
HCKIND: ${{ matrix.compilerKind }}
9383
HCNAME: ${{ matrix.compiler }}
@@ -99,22 +89,13 @@ jobs:
9989
echo "CABAL_DIR=$HOME/.cabal" >> "$GITHUB_ENV"
10090
echo "CABAL_CONFIG=$HOME/.cabal/config" >> "$GITHUB_ENV"
10191
HCDIR=/opt/$HCKIND/$HCVER
102-
if [ "${{ matrix.setup-method }}" = ghcup ]; then
103-
HC=$("$HOME/.ghcup/bin/ghcup" whereis ghc "$HCVER")
104-
HCPKG=$(echo "$HC" | sed 's#ghc$#ghc-pkg#')
105-
HADDOCK=$(echo "$HC" | sed 's#ghc$#haddock#')
106-
echo "HC=$HC" >> "$GITHUB_ENV"
107-
echo "HCPKG=$HCPKG" >> "$GITHUB_ENV"
108-
echo "HADDOCK=$HADDOCK" >> "$GITHUB_ENV"
109-
echo "CABAL=$HOME/.ghcup/bin/cabal-3.10.1.0 -vnormal+nowrap" >> "$GITHUB_ENV"
110-
else
111-
HC=$HCDIR/bin/$HCKIND
112-
echo "HC=$HC" >> "$GITHUB_ENV"
113-
echo "HCPKG=$HCDIR/bin/$HCKIND-pkg" >> "$GITHUB_ENV"
114-
echo "HADDOCK=$HCDIR/bin/haddock" >> "$GITHUB_ENV"
115-
echo "CABAL=$HOME/.ghcup/bin/cabal-3.10.1.0 -vnormal+nowrap" >> "$GITHUB_ENV"
116-
fi
117-
92+
HC=$("$HOME/.ghcup/bin/ghcup" whereis ghc "$HCVER")
93+
HCPKG=$(echo "$HC" | sed 's#ghc$#ghc-pkg#')
94+
HADDOCK=$(echo "$HC" | sed 's#ghc$#haddock#')
95+
echo "HC=$HC" >> "$GITHUB_ENV"
96+
echo "HCPKG=$HCPKG" >> "$GITHUB_ENV"
97+
echo "HADDOCK=$HADDOCK" >> "$GITHUB_ENV"
98+
echo "CABAL=$HOME/.ghcup/bin/cabal-3.10.2.0 -vnormal+nowrap" >> "$GITHUB_ENV"
11899
HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\d+)\.(\d+)\.(\d+)(\.(\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')
119100
echo "HCNUMVER=$HCNUMVER" >> "$GITHUB_ENV"
120101
echo "ARG_TESTS=--enable-tests" >> "$GITHUB_ENV"
@@ -162,9 +143,9 @@ jobs:
162143
run: |
163144
$CABAL v2-update -v
164145
- name: cache (tools)
165-
uses: actions/cache/restore@v3
146+
uses: actions/cache/restore@v4
166147
with:
167-
key: ${{ runner.os }}-${{ matrix.compiler }}-tools-0f8d33a5
148+
key: ${{ runner.os }}-${{ matrix.compiler }}-tools-655fd156
168149
path: ~/.haskell-ci-tools
169150
- name: install cabal-plan
170151
run: |
@@ -177,16 +158,16 @@ jobs:
177158
cabal-plan --version
178159
- name: install doctest
179160
run: |
180-
$CABAL --store-dir=$HOME/.haskell-ci-tools/store v2-install $ARG_COMPILER --ignore-project -j2 doctest --constraint='doctest ^>=0.22'
181-
doctest --version
161+
if [ $((HCNUMVER < 91000)) -ne 0 ] ; then $CABAL --store-dir=$HOME/.haskell-ci-tools/store v2-install $ARG_COMPILER --ignore-project -j2 doctest --constraint='doctest ^>=0.22.0' ; fi
162+
if [ $((HCNUMVER < 91000)) -ne 0 ] ; then doctest --version ; fi
182163
- name: save cache (tools)
183-
uses: actions/cache/save@v3
164+
uses: actions/cache/save@v4
184165
if: always()
185166
with:
186-
key: ${{ runner.os }}-${{ matrix.compiler }}-tools-0f8d33a5
167+
key: ${{ runner.os }}-${{ matrix.compiler }}-tools-655fd156
187168
path: ~/.haskell-ci-tools
188169
- name: checkout
189-
uses: actions/checkout@v3
170+
uses: actions/checkout@v4
190171
with:
191172
path: source
192173
- name: initial cabal.project for sdist
@@ -214,15 +195,15 @@ jobs:
214195
echo " ghc-options: -Werror=missing-methods" >> cabal.project
215196
cat >> cabal.project <<EOF
216197
EOF
217-
$HCPKG list --simple-output --names-only | perl -ne 'for (split /\s+/) { print "constraints: $_ installed\n" unless /^(log-effectful)$/; }' >> cabal.project.local
198+
$HCPKG list --simple-output --names-only | perl -ne 'for (split /\s+/) { print "constraints: any.$_ installed\n" unless /^(log-effectful)$/; }' >> cabal.project.local
218199
cat cabal.project
219200
cat cabal.project.local
220201
- name: dump install plan
221202
run: |
222203
$CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dry-run all
223204
cabal-plan
224205
- name: restore cache
225-
uses: actions/cache/restore@v3
206+
uses: actions/cache/restore@v4
226207
with:
227208
key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
228209
path: ~/.cabal/store
@@ -242,8 +223,8 @@ jobs:
242223
$CABAL v2-test $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --test-show-details=direct
243224
- name: doctest
244225
run: |
245-
cd ${PKGDIR_log_effectful} || false
246-
doctest -XHaskell2010 -XBangPatterns -XConstraintKinds -XDataKinds -XDeriveFunctor -XDeriveGeneric -XDerivingStrategies -XFlexibleContexts -XFlexibleInstances -XGADTs -XGeneralizedNewtypeDeriving -XLambdaCase -XMultiParamTypeClasses -XNoStarIsType -XRankNTypes -XRoleAnnotations -XScopedTypeVariables -XStandaloneDeriving -XTupleSections -XTypeApplications -XTypeFamilies -XTypeOperators src
226+
if [ $((HCNUMVER < 91000)) -ne 0 ] ; then cd ${PKGDIR_log_effectful} || false ; fi
227+
if [ $((HCNUMVER < 91000)) -ne 0 ] ; then doctest -XHaskell2010 -XBangPatterns -XConstraintKinds -XDataKinds -XDeriveFunctor -XDeriveGeneric -XDerivingStrategies -XFlexibleContexts -XFlexibleInstances -XGADTs -XGeneralizedNewtypeDeriving -XLambdaCase -XMultiParamTypeClasses -XNoStarIsType -XRankNTypes -XRoleAnnotations -XScopedTypeVariables -XStandaloneDeriving -XTupleSections -XTypeApplications -XTypeFamilies -XTypeOperators src ; fi
247228
- name: cabal check
248229
run: |
249230
cd ${PKGDIR_log_effectful} || false
@@ -256,7 +237,7 @@ jobs:
256237
rm -f cabal.project.local
257238
$CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
258239
- name: save cache
259-
uses: actions/cache/save@v3
240+
uses: actions/cache/save@v4
260241
if: always()
261242
with:
262243
key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1+
# log-effectful-1.0.1.0 (2024-??-??)
2+
* Convert `Log` into a dynamically dispatched effect.
3+
14
# log-effectful-1.0.0.0 (2022-10-10)
25
* Initial release.

log-effectful.cabal

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
cabal-version: 2.4
1+
cabal-version: 3.0
22
build-type: Simple
33
name: log-effectful
4-
version: 1.0.0.0
4+
version: 1.0.1.0
55
license: BSD-3-Clause
66
license-file: LICENSE
77
category: System
@@ -16,8 +16,7 @@ extra-source-files:
1616
CHANGELOG.md
1717
README.md
1818

19-
tested-with: GHC ==8.8.4 || ==8.10.7 || ==9.0.2 || ==9.2.8 || ==9.4.7 || ==9.6.3
20-
|| ==9.8.1
19+
tested-with: GHC == { 8.10.7, 9.0.2, 9.2.8, 9.4.8, 9.6.5, 9.8.2, 9.10.1 }
2120

2221
bug-reports: https://github.com/haskell-effectful/log-effectful/issues
2322
source-repository head
@@ -55,6 +54,7 @@ library
5554
import: language
5655

5756
build-depends: base <5
57+
, aeson >=2.0.0.0
5858
, effectful-core >=1.0.0.0 && <3.0.0.0
5959
, log-base >=0.12.0.0
6060
, text

src/Effectful/Log.hs

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
-- | Logging via 'MonadLog'.
44
module Effectful.Log
55
( -- * Effect
6-
Log
6+
Log (..)
77

88
-- ** Handlers
99
, runLog
@@ -12,17 +12,23 @@ module Effectful.Log
1212
, module Log
1313
) where
1414

15+
import Data.Aeson.Types
1516
import Data.Text (Text)
1617
import Data.Time.Clock
17-
import Effectful.Dispatch.Static
18+
import Effectful.Dispatch.Dynamic
19+
import Effectful.Reader.Static
1820
import Effectful
1921
import Log
2022

2123
-- | Provide the ability to log messages via 'MonadLog'.
22-
data Log :: Effect
24+
data Log :: Effect where
25+
LogMessageOp :: LogLevel -> Text -> Value -> Log m ()
26+
LocalData :: [Pair] -> m a -> Log m a
27+
LocalDomain :: Text -> m a -> Log m a
28+
LocalMaxLogLevel :: LogLevel -> m a -> Log m a
29+
GetLoggerEnv :: Log m LoggerEnv
2330

24-
type instance DispatchOf Log = Static WithSideEffects
25-
newtype instance StaticRep Log = Log LoggerEnv
31+
type instance DispatchOf Log = Dynamic
2632

2733
-- | Run the 'Log' effect.
2834
--
@@ -38,30 +44,31 @@ runLog
3844
-> Eff (Log : es) a
3945
-- ^ The computation to run.
4046
-> Eff es a
41-
runLog component logger maxLogLevel = evalStaticRep $ Log LoggerEnv
42-
{ leLogger = logger
43-
, leComponent = component
44-
, leDomain = []
45-
, leData = []
46-
, leMaxLogLevel = maxLogLevel
47-
}
47+
runLog component logger maxLogLevel = reinterpret reader $ \env -> \case
48+
LogMessageOp level message data_ -> do
49+
time <- liftIO getCurrentTime
50+
logEnv <- ask
51+
liftIO $ logMessageIO logEnv time level message data_
52+
LocalData data_ action -> localSeqUnlift env $ \unlift -> do
53+
(`local` unlift action) $ \logEnv -> logEnv { leData = data_ ++ leData logEnv }
54+
LocalDomain domain action -> localSeqUnlift env $ \unlift -> do
55+
(`local` unlift action) $ \logEnv -> logEnv { leDomain = leDomain logEnv ++ [domain] }
56+
LocalMaxLogLevel level action -> localSeqUnlift env $ \unlift -> do
57+
(`local` unlift action) $ \logEnv -> logEnv { leMaxLogLevel = level }
58+
GetLoggerEnv -> ask
59+
where
60+
reader = runReader LoggerEnv
61+
{ leLogger = logger
62+
, leComponent = component
63+
, leDomain = []
64+
, leData = []
65+
, leMaxLogLevel = maxLogLevel
66+
}
4867

4968
-- | Orphan, canonical instance.
5069
instance Log :> es => MonadLog (Eff es) where
51-
logMessage level message data_ = do
52-
time <- unsafeEff_ getCurrentTime
53-
Log logEnv <- getStaticRep
54-
unsafeEff_ $ logMessageIO logEnv time level message data_
55-
56-
localData data_ = localStaticRep $ \(Log logEnv) ->
57-
Log logEnv { leData = data_ ++ leData logEnv }
58-
59-
localDomain domain = localStaticRep $ \(Log logEnv) ->
60-
Log logEnv { leDomain = leDomain logEnv ++ [domain] }
61-
62-
localMaxLogLevel level = localStaticRep $ \(Log logEnv) ->
63-
Log logEnv { leMaxLogLevel = level }
64-
65-
getLoggerEnv = do
66-
Log env <- getStaticRep
67-
pure env
70+
logMessage level message data_ = send $ LogMessageOp level message data_
71+
localData data_ action = send $ LocalData data_ action
72+
localDomain domain action = send $ LocalDomain domain action
73+
localMaxLogLevel level action = send $ LocalMaxLogLevel level action
74+
getLoggerEnv = send GetLoggerEnv

0 commit comments

Comments
 (0)