From fe064dcf1d8d201486feda732d890fa13e66af5c Mon Sep 17 00:00:00 2001
From: Julien Moutinho <julm@sourcephile.fr>
Date: Sun, 23 Jul 2023 00:14:55 +0200
Subject: [PATCH 1/1] init

---
 .chglog/CHANGELOG.tpl.md |  37 +++
 .chglog/config.yml       |  37 +++
 .envrc                   |   1 +
 .hlint.yaml              |  28 ++
 .reuse/dep5              |  13 +
 Makefile                 | 111 ++++++++
 flake.lock               | 121 +++++++++
 flake.nix                |  60 +++++
 fourmolu.yaml            |   8 +
 src/ForallSem.hs         | 547 +++++++++++++++++++++++++++++++++++++++
 symantic.cabal           | 249 ++++++++++++++++++
 11 files changed, 1212 insertions(+)
 create mode 100755 .chglog/CHANGELOG.tpl.md
 create mode 100755 .chglog/config.yml
 create mode 100644 .envrc
 create mode 100644 .hlint.yaml
 create mode 100644 .reuse/dep5
 create mode 100644 Makefile
 create mode 100644 flake.lock
 create mode 100644 flake.nix
 create mode 100644 fourmolu.yaml
 create mode 100644 src/ForallSem.hs
 create mode 100644 symantic.cabal

diff --git a/.chglog/CHANGELOG.tpl.md b/.chglog/CHANGELOG.tpl.md
new file mode 100755
index 0000000..07711aa
--- /dev/null
+++ b/.chglog/CHANGELOG.tpl.md
@@ -0,0 +1,37 @@
+{{ range .Versions }}
+## {{ .Tag.Name }} ({{ datetime "2006-01-02" .Tag.Date }})
+
+{{ range .CommitGroups -}}
+### {{ .Title }}
+
+{{ range .Commits -}}
+* {{ upperFirst .Type }}{{ if .Subject }} {{ .Subject }}{{ end }}.
+{{ end }}
+{{ end -}}
+
+{{- if .RevertCommits -}}
+### Reverts
+
+{{ range .RevertCommits -}}
+* {{ .Revert.Header }}
+{{ end }}
+{{ end -}}
+
+{{- if .MergeCommits -}}
+### Merges
+
+{{ range .MergeCommits -}}
+* {{ .Header }}
+{{ end }}
+{{ end -}}
+
+{{- if .NoteGroups -}}
+{{ range .NoteGroups -}}
+### {{ .Title }}
+
+{{ range .Notes }}
+{{ .Body }}
+{{ end }}
+{{ end -}}
+{{ end -}}
+{{ end -}}
diff --git a/.chglog/config.yml b/.chglog/config.yml
new file mode 100755
index 0000000..2e25049
--- /dev/null
+++ b/.chglog/config.yml
@@ -0,0 +1,37 @@
+style: none
+template: CHANGELOG.tpl.md
+info:
+  title: ChangeLog
+  repository_url: "https://git.sourcephile.fr/haskell/literate-web"
+options:
+  sort: "date"
+
+  commits:
+    sort_by: Type
+
+  commit_groups:
+    group_by: Scope
+    sort_by: Custom
+    title_order:
+      - iface
+      - doc
+      - impl
+      - tests
+      - build
+    title_maps:
+      build: Build
+      doc: Documentation
+      iface: Interface
+      impl: Implementation
+      tests: Tests
+
+  header:
+    pattern: "^([\\w\\$\\.\\-\\*\\s]*)\\:\\s(\\w*)\\s*(.*)$"
+    pattern_maps:
+      - Scope
+      - Type
+      - Subject
+
+  notes:
+    keywords:
+      - BREAKING CHANGE
diff --git a/.envrc b/.envrc
new file mode 100644
index 0000000..3550a30
--- /dev/null
+++ b/.envrc
@@ -0,0 +1 @@
+use flake
diff --git a/.hlint.yaml b/.hlint.yaml
new file mode 100644
index 0000000..f95c6ca
--- /dev/null
+++ b/.hlint.yaml
@@ -0,0 +1,28 @@
+- extensions:
+  - name: Haskell2010
+  - name: NoCPP
+  - name: TypeApplications
+
+- ignore: {name: Move brackets to avoid $}
+- ignore: {name: Reduce duplication}
+- ignore: {name: Redundant $}
+- ignore: {name: Redundant bracket}
+- ignore: {name: Redundant do}
+- ignore: {name: Redundant lambda}
+- ignore: {name: Use camelCase}
+- ignore: {name: Use const}
+- ignore: {name: Use fmap}
+- ignore: {name: Use if}
+- ignore: {name: Use import/export shortcut}
+- ignore: {name: Use intercalate}
+- ignore: {name: Use list literal pattern}
+- ignore: {name: Use list literal}
+- ignore: {name: Use newtype instead of data}
+- ignore: {name: Use null}
+- ignore: {name: Use span}
+- ignore: {name: Use unless}
+- ignore: {name: Use ++}
+
+# BEGIN: generated hints
+- fixity: "infixr 4 </>"
+# END: generated hints
diff --git a/.reuse/dep5 b/.reuse/dep5
new file mode 100644
index 0000000..4db55a9
--- /dev/null
+++ b/.reuse/dep5
@@ -0,0 +1,13 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: literate-web
+Upstream-Contact: Julien Moutinho <julm+literate-web@sourcephile.fr>
+Source: https://git.sourcephile.fr/haskell/literate-web
+
+Files: *.lock cabal.project* *.cabal *.md .chglog/* .envrc
+ .gitignore .gitmodules .git-blame-ignore-revs *.yaml
+Copyright: Julien Moutinho <julm+literate-web@sourcephile.fr>
+License: CC0-1.0
+
+Files: src/* tests/* Makefile *.nix
+Copyright: Julien Moutinho <julm+literate-web@sourcephile.fr>
+License: AGPL-3.0-or-later
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..41fbb85
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,111 @@
+cabal := $(wildcard *.cabal)
+package := $(notdir ./$(cabal:.cabal=))
+version := $(shell sed -ne 's/^version: *\(.*\)/\1/p' $(cabal))
+project := $(patsubst %.cabal,%,$(cabal))
+cabal_builddir ?= dist-newstyle
+
+override REPL_OPTIONS += -ignore-dot-ghci
+override TEST_OPTIONS += --color=always
+override GHCID_OPTIONS += --no-height-limit --reverse-errors --color=always --warnings --restart $(cabal)
+
+all: build
+build:
+	cabal build $(CABAL_BUILD_FLAGS)
+clean c:
+	cabal clean
+repl:
+	cabal repl $(CABAL_REPL_FLAGS) $(project)
+ghcid:
+	ghcid $(GHCID_OPTIONS) --command 'cabal repl -fno-code $(CABAL_REPL_FLAGS) $(project) $(addprefix --repl-options ,$(REPL_OPTIONS))'
+
+.PHONY: tests
+t tests:
+	cabal test $(CABAL_TEST_FLAGS) \
+	 --test-show-details always --test-options "$(TEST_OPTIONS)"
+tests/prof-time: $(project)-tests.eventlog.json
+tests/prof-heap: $(project)-tests.eventlog.html
+.PHONY: $(project)-test.eventlog
+$(project)-test.eventlog $(project)-test.prof:
+	cabal test $(CABAL_TEST_FLAGS) \
+	 --test-show-details always --test-options "$(TEST_OPTIONS) +RTS $(RTS_OPTIONS)" \
+	 --enable-profiling $(addprefix --ghc-options ,$(GHC_PROF_OPTIONS)) || true
+t/repl tests/repl:
+	cabal repl $(CABAL_REPL_FLAGS) $(CABAL_TEST_FLAGS) --enable-tests $(project)-tests
+t/ghcid tests/ghcid:
+	ghcid $(GHCID_OPTIONS) --command 'cabal repl $(CABAL_REPL_FLAGS) $(project) $(addprefix --repl-options ,$(REPL_OPTIONS))' \
+	  --run=':! ghcid $(GHCID_OPTIONS) --command "cabal repl $(CABAL_REPL_FLAGS) $(CABAL_TEST_FLAGS) $(project):tests" --test ":main $(TEST_OPTIONS)"'
+
+.PHONY: benchmarks/time
+bt benchmarks/time:
+	cabal bench $(CABAL_BENCH_FLAGS) time \
+	 --benchmark-options "$(BENCHMARKS_TIME_OPTIONS)"
+.PHONY: benchmarks/weigh
+bw benchmarks/weigh:
+	cabal bench $(CABAL_BENCH_FLAGS) weigh \
+	 --benchmark-options "$(BENCHMARKS_WEIGH_OPTIONS)"
+benchmarks/weigh/ghcid:
+	ghcid $(GHCID_OPTIONS) --target=relactive --run=':! ghcid $(GHCID_OPTIONS) --target=weigh --run'
+
+executables/async/ghcid:
+	ghcid $(GHCID_OPTIONS) --target=relactive --run=':! ghcid $(GHCID_OPTIONS) --target=async --run'
+executables/fsnotify/ghcid:
+	ghcid $(GHCID_OPTIONS) --target=relactive --run=':! ghcid $(GHCID_OPTIONS) --target=fsnotify --run'
+
+
+%/accept: TEST_OPTIONS += --accept
+%/accept: %
+	
+%/cover: CABAL_TEST_FLAGS += --enable-coverage
+%/cover: %
+	
+%.eventlog.html: RTS_OPTIONS += -hy -l-au
+%.eventlog.html: %.eventlog
+	eventlog2html $<
+%.eventlog.json: RTS_OPTIONS += -p -l-au
+%.eventlog.json: %.eventlog
+	hs-speedscope $<
+
+doc:
+	cabal haddock --haddock-css ocean --haddock-hyperlink-source
+
+.PHONY: ChangeLog.md
+ChangeLog.md:
+	! git tag --merged | grep -Fqx $(package)-$(version)
+	git diff --exit-code
+	git tag -f $(package)-$(version)
+	git-chglog --output $@.new --tag-filter-pattern '$(package)-.*' $(package)-$(version)
+	touch $@
+	cat $@ >>$@.new
+	mv -f $@.new $@
+	git tag -d $(package)-$(version)
+	git add '$@'
+	git commit -m 'doc: update `$@`'
+tag: build ChangeLog.md
+	git tag -s -m $(package)-$(version) $(package)-$(version)
+
+tar:
+	git diff --exit-code
+	reuse lint
+	cabal sdist
+	cabal haddock --haddock-for-hackage --enable-doc
+upload: LANG=C
+upload: tar
+	git tag --merged | grep -Fqx $(package)-$(version)
+	git push --follow-tags $(GIT_PUSH_FLAGS)
+	cabal upload $(CABAL_UPLOAD_FLAGS) "$(cabal_builddir)"/sdist/$(package)-$(version).tar.gz
+	cabal upload $(CABAL_UPLOAD_FLAGS) --documentation "$(cabal_builddir)"/$(package)-$(version)-docs.tar.gz
+%/publish: CABAL_UPLOAD_FLAGS+=--publish
+%/publish: %
+	
+publish: upload/publish
+
+.PHONY: .hlint.yaml
+.hlint.yaml: $(shell find src -name '*.hs' -not -name 'HLint.hs')
+	sed -i -e '/^# BEGIN: generated hints/,/^# END: generated hints/d' $@
+	echo >>$@ '# BEGIN: generated hints'
+	hlint --find . | grep -- '- fixity:' | sort -u >>$@
+	echo >>$@ '# END: generated hints'
+lint: .hlint.yaml
+	if hlint --quiet --report=hlint.html -XNoCPP $(HLINT_FLAGS) .; \
+	then rm -f hlint.html; \
+	else sensible-browser ./hlint.html & fi
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..aa62899
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,121 @@
+{
+  "nodes": {
+    "flake-utils": {
+      "locked": {
+        "lastModified": 1667077288,
+        "narHash": "sha256-bdC8sFNDpT0HK74u9fUkpbf1MEzVYJ+ka7NXCdgBoaA=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "6ee9ebb6b1ee695d2cacc4faa053a7b9baa76817",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "flake-utils_2": {
+      "locked": {
+        "lastModified": 1667077288,
+        "narHash": "sha256-bdC8sFNDpT0HK74u9fUkpbf1MEzVYJ+ka7NXCdgBoaA=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "6ee9ebb6b1ee695d2cacc4faa053a7b9baa76817",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "nixpkgs": {
+      "locked": {
+        "lastModified": 1686237827,
+        "narHash": "sha256-fAZB+Zkcmc+qlauiFnIH9+2qgwM0NO/ru5pWEw3tDow=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "81ed90058a851eb73be835c770e062c6938c8a9e",
+        "type": "github"
+      },
+      "original": {
+        "id": "nixpkgs",
+        "type": "indirect"
+      }
+    },
+    "pre-commit-hooks": {
+      "inputs": {
+        "flake-utils": "flake-utils",
+        "nixpkgs": [
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1667992213,
+        "narHash": "sha256-8Ens8ozllvlaFMCZBxg6S7oUyynYx2v7yleC5M0jJsE=",
+        "owner": "cachix",
+        "repo": "pre-commit-hooks.nix",
+        "rev": "ebcbfe09d2bd6d15f68de3a0ebb1e4dcb5cd324b",
+        "type": "github"
+      },
+      "original": {
+        "owner": "cachix",
+        "repo": "pre-commit-hooks.nix",
+        "type": "github"
+      }
+    },
+    "pre-commit-hooks_2": {
+      "inputs": {
+        "flake-utils": "flake-utils_2",
+        "nixpkgs": [
+          "symantic-base",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1667992213,
+        "narHash": "sha256-8Ens8ozllvlaFMCZBxg6S7oUyynYx2v7yleC5M0jJsE=",
+        "owner": "cachix",
+        "repo": "pre-commit-hooks.nix",
+        "rev": "ebcbfe09d2bd6d15f68de3a0ebb1e4dcb5cd324b",
+        "type": "github"
+      },
+      "original": {
+        "owner": "cachix",
+        "repo": "pre-commit-hooks.nix",
+        "type": "github"
+      }
+    },
+    "root": {
+      "inputs": {
+        "nixpkgs": "nixpkgs",
+        "pre-commit-hooks": "pre-commit-hooks",
+        "symantic-base": "symantic-base"
+      }
+    },
+    "symantic-base": {
+      "inputs": {
+        "nixpkgs": [
+          "nixpkgs"
+        ],
+        "pre-commit-hooks": "pre-commit-hooks_2"
+      },
+      "locked": {
+        "lastModified": 1689629067,
+        "narHash": "sha256-JJmKWWemB+6J73IwQqqN61o+z4B+XL5Z4d0q2DcKNVk=",
+        "ref": "refs/heads/main",
+        "rev": "f928eacc54007508ed00235b2ba7e065e17f5f66",
+        "revCount": 90,
+        "type": "git",
+        "url": "git://git.sourcephile.fr/haskell/symantic-base"
+      },
+      "original": {
+        "type": "git",
+        "url": "git://git.sourcephile.fr/haskell/symantic-base"
+      }
+    }
+  },
+  "root": "root",
+  "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..6fbe0ea
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,60 @@
+{
+  inputs = {
+    nixpkgs.url = "flake:nixpkgs";
+    pre-commit-hooks.url = "github:cachix/pre-commit-hooks.nix";
+    pre-commit-hooks.inputs.nixpkgs.follows = "nixpkgs";
+    symantic-base.url = "git://git.sourcephile.fr/haskell/symantic-base";
+    symantic-base.inputs.nixpkgs.follows = "nixpkgs";
+  };
+  outputs = inputs:
+    let
+      pkg = "symantic";
+      lib = inputs.nixpkgs.lib;
+      forAllSystems = f: lib.genAttrs lib.systems.flakeExposed (system: f rec {
+        inherit system;
+        pkgs = inputs.nixpkgs.legacyPackages.${system};
+        haskellPackages = pkgs.haskellPackages.extend (with pkgs.haskell.lib; hfinal: hsuper: {
+          ${pkg} = doBenchmark (buildFromSdist (hfinal.callCabal2nix pkg ./. { }));
+          symantic-base = buildFromSdist (hfinal.callCabal2nix "symantic-base" inputs.symantic-base { });
+        });
+      });
+    in
+    rec {
+      # nix -L build
+      defaultPackage = forAllSystems ({ haskellPackages, ... }: haskellPackages.${pkg});
+      # nix -L develop  or  direnv allow
+      devShell = forAllSystems ({ pkgs, haskellPackages, system, ... }:
+        haskellPackages.shellFor {
+          packages = ps: [ ps.${pkg} ];
+          nativeBuildInputs = [
+            haskellPackages.cabal-install
+            haskellPackages.ghcid
+            haskellPackages.haskell-language-server
+            haskellPackages.hlint
+            #haskellPackages.stan
+            pkgs.git-chglog
+            pkgs.reuse
+          ];
+          withHoogle = false;
+          doBenchmark = true;
+          inherit (checks.${system}.pre-commit-check) shellHook;
+        });
+      # nix flake check
+      checks = forAllSystems (args: with args; {
+        pre-commit-check = inputs.pre-commit-hooks.lib.${system}.run {
+          src = ./.;
+          #settings.ormolu.cabalDefaultExtensions = true;
+          settings.ormolu.defaultExtensions = [
+            "ImportQualifiedPost"
+            "TypeApplications"
+          ];
+          hooks = {
+            hlint.enable = true;
+            nixpkgs-fmt.enable = true;
+            fourmolu.enable = true;
+            cabal-fmt.enable = true;
+          };
+        };
+      });
+    };
+}
diff --git a/fourmolu.yaml b/fourmolu.yaml
new file mode 100644
index 0000000..1fc42af
--- /dev/null
+++ b/fourmolu.yaml
@@ -0,0 +1,8 @@
+comma-style: leading
+diff-friendly-import-export: true
+haddock-style: single-line
+indent-wheres: true
+indentation: 2
+newlines-between-decls: 1
+record-brace-space: false
+respectful: true
diff --git a/src/ForallSem.hs b/src/ForallSem.hs
new file mode 100644
index 0000000..342f22b
--- /dev/null
+++ b/src/ForallSem.hs
@@ -0,0 +1,547 @@
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE NoMonomorphismRestriction #-}
+{-# LANGUAGE QuantifiedConstraints #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE StandaloneDeriving #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE UndecidableInstances #-}
+{-# LANGUAGE UndecidableSuperClasses #-}
+{-# LANGUAGE ImportQualifiedPost #-}
+{-# LANGUAGE PatternSynonyms #-}
+{-# LANGUAGE StandaloneKindSignatures #-}
+{-# LANGUAGE PolyKinds #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE ViewPatterns #-}
+
+module ForallSem where
+
+import Data.Functor (Functor(..), (<$>))
+import Data.Functor.Constant (Constant(..))
+import Control.Applicative (Applicative(..))
+import Control.Monad (Monad(..))
+import Data.Kind (Type, Constraint)
+import Data.Function (($), (.), id)
+import Data.String (String)
+import Data.Semigroup (Semigroup(..))
+import Data.Maybe (Maybe(..), isJust)
+import Text.Read (Read(..), reads)
+import Data.Bool (otherwise)
+import Text.Show (Show(..))
+import Data.Eq (Eq(..))
+import Data.Either (Either(..))
+import Data.Int (Int)
+import Prelude (error)
+import Control.Monad.Trans.Except qualified as MT
+import Control.Monad.Trans.Reader qualified as MT
+import Control.Monad.Trans.State qualified as MT
+import Type.Reflection
+import Unsafe.Coerce (unsafeCoerce)
+import Data.Proxy (Proxy(..))
+import Prelude qualified
+import GHC.Types
+
+type Semantic = Type -> Type
+type Syntax = Semantic -> Constraint
+class Syn0 (sem::Semantic) where
+  syn0 :: sem ()
+class Syn1 (sem::Semantic) where
+  syn1 :: sem ()
+class Syn2 (sem::Semantic) where
+  syn2 :: sem () -> sem () -> sem ()
+
+p0 :: (forall sem. syn sem => Syn0 sem) => ForallSem syn ()
+p0 = ForallSem syn0
+
+p1 :: (forall sem. syn sem => Syn1 sem) => ForallSem syn ()
+p1 = ForallSem syn1
+
+p3 :: forall syn.
+      ( forall sem. syn sem => Syn0 sem
+      , forall sem. syn sem => Syn1 sem
+      , forall sem. syn sem => Syn2 sem
+      ) => ForallSem syn ()
+p3 = ForallSem $ syn2 (unForallSem @syn p0) (unForallSem @syn p1)
+
+
+
+
+
+
+
+data ForallSem (syn::Syntax) (a::Type)
+   = ForallSem { unForallSem :: forall sem. syn sem => sem a }
+
+instance ( forall sem. syn sem => Functor sem
+         ) => Functor (ForallSem syn) where
+  fmap f (ForallSem sem) = ForallSem (fmap f sem)
+  a <$ (ForallSem sem) = ForallSem (a <$ sem)
+instance ( forall sem. syn sem => Applicative sem
+         , Functor (ForallSem syn)
+         ) => Applicative (ForallSem syn) where
+  pure a = ForallSem (pure a)
+  liftA2 f (ForallSem a) (ForallSem b) = ForallSem (liftA2 f a b)
+  (<*>) (ForallSem f) (ForallSem a) = ForallSem ((<*>) f a)
+  (<*) (ForallSem f) (ForallSem a) = ForallSem ((<*) f a)
+  (*>) (ForallSem f) (ForallSem a) = ForallSem ((*>) f a)
+instance ( forall sem. syn sem => Monad sem
+         , Applicative (ForallSem syn)
+         ) => Monad (ForallSem syn) where
+  (>>=) (ForallSem sa) f = ForallSem (sa >>= \a -> case f a of ForallSem sb -> sb)
+  return = pure
+  (>>) = (*>)
+
+-- * Interpreter 'Parser'
+data TermVT syn = forall vs a. TermVT
+  { typeTermVT :: Ty vs a
+  , unTermVT  :: ForallSem syn a
+  }
+
+-- instance Eq (TyVT) where
+--   TyVT x == TyVT y =
+--     let (x', y') = appendVars x y in
+--     isJust $ eqTypeKi x' y'
+
+-- * Type family @(++)@
+type family (++) xs ys where
+  '[] ++ ys  = ys
+  -- xs  ++ '[] = xs
+  (x ': xs) ++ ys = x ': xs ++ ys
+infixr 5 ++
+
+-- * Type 'Len'
+data Len (xs::[k]) where
+  LenZ :: Len '[]
+  LenS :: Len xs -> Len (x ': xs)
+
+instance Show (Len vs) where
+  showsPrec _p = showsPrec 10 . intLen
+
+addLen :: Len a -> Len b -> Len (a ++ b)
+addLen LenZ     b = b
+addLen (LenS a) b = LenS $ addLen a b
+
+shiftLen :: forall t b a.  Len a -> Len (a ++      b) -> Len (a ++ t ': b)
+shiftLen LenZ b = LenS b
+shiftLen (LenS a) (LenS b) = LenS $ shiftLen @t @b a b
+
+intLen :: Len xs -> Int
+intLen = go 0
+  where
+  go :: Int -> Len xs -> Int
+  go i LenZ     = i
+  go i (LenS l) = go (1 Prelude.+ i) l
+
+-- ** Class 'LenInj'
+class LenInj (vs::[k]) where
+  lenInj :: Len vs
+instance LenInj '[] where
+  lenInj = LenZ
+instance LenInj as => LenInj (a ': as) where
+  lenInj = LenS lenInj
+
+type Name = String
+-- Use a CUSK, because it's a polymorphically recursive type,
+-- See https://ryanglscott.github.io/2020/01/05/five-benefits-to-using-standalonekindsignatures/
+data Ty :: forall aK. [Type] -> aK -> Type where
+  TyConst :: Len vs -> TypeRep a -> Ty vs a
+  TyVar :: Name -> Var vs a -> Ty vs a
+  TyApp :: Ty vs f -> Ty vs a -> Ty vs (f a)
+infixl 9 `TyApp`
+deriving instance Show (Ty vs a)
+instance AllocVars Ty where
+  allocVarsL len (TyConst l c)  = TyConst (addLen len l) c
+  allocVarsL len (TyApp f a)    = TyApp (allocVarsL len f) (allocVarsL len a)
+  allocVarsL len (TyVar n v)    = TyVar n (allocVarsL len v)
+  --allocVarsL len (TyFam l f as) = TyFam (addLen len l) f $ allocVarsL len `mapTys` as
+  
+  allocVarsR len (TyConst l c)  = TyConst (addLen l len) c
+  allocVarsR len (TyApp f a)    = TyApp (allocVarsR len f) (allocVarsR len a)
+  allocVarsR len (TyVar n v)    = TyVar n (allocVarsR len v)
+  --allocVarsR len (TyFam l f as) = TyFam (addLen l len) f $ allocVarsR len `mapTys` as
+
+data TyVT = forall vs a. TyVT (Ty vs a)
+deriving instance Show TyVT
+instance Eq TyVT where
+  TyVT x == TyVT y = isJust (eqTyKi x' y')
+    where (x', y') = appendVars x y
+
+-- | /Heterogeneous type equality/:
+-- return two proofs when two 'Type's are equals:
+-- one for the type and one for the kind.
+eqTyKi :: Ty vs (x::xK) -> Ty vs (y::yK) -> Maybe (x:~~:y)
+eqTyKi (TyConst _lx x) (TyConst _ly y) = eqTypeRep x y
+eqTyKi (TyVar   _nx x) (TyVar   _ny y) = eqVarKi   x y
+eqTyKi (TyApp fx ax) (TyApp fy ay)
+ | Just HRefl <- eqTyKi fx fy
+ , Just HRefl <- eqTyKi ax ay
+ = Just HRefl
+-- eqTyKi (TyFam _ _lx fx ax) (TyFam _ _ly fy ay)
+--  | Just HRefl <- eqTypeRep fx fy
+--  , Just HRefl <- eqTypes ax ay
+--  = Just HRefl
+-- -- NOTE: 'TyFam' is expanded as much as possible.
+-- eqTyKi x@TyFam{} y = eqTyKi (expandFam x) y
+-- eqTyKi x y@TyFam{} = eqTyKi x (expandFam y)
+eqTyKi _x _y = Nothing
+
+-- data Some :: (Type -> Type) -> Type where
+--   Some :: TypeRep a -> t a -> Some t
+-- 
+-- mapSome :: (forall a. s a -> t a) -> Some s -> Some t
+-- mapSome f (Some a t) = Some a (f t)
+-- 
+-- type SomeTy = Some (Constant ())
+
+-- pattern SomeTy :: TypeRep a -> SomeTy
+-- pattern SomeTy a = Some a (Constant ())
+-- {-# COMPLETE SomeTy #-}
+
+-- someType :: TypeRep a -> SomeTy
+-- someType a = Some a (Constant ())
+
+--infer :: Vars vs -> TermVT syn -> Maybe (TermVT syn)
+--infer ctx te = case te of
+--Lit i -> return $ Some TTyInt (embed (CInt i))
+
+
+data Parser syn = Parser
+  { unParser ::
+      --MT.State (TokenTerm a)
+        (Either ErrMsg (TermVT syn))
+  }
+
+{-
+instance ( forall sem. syn sem => Functor sem
+         ) => Functor (Parser syn) where
+  fmap f (Parser esa) = Parser $
+    case esa of
+      Left e -> Left e
+      Right (ForallSem sem) -> Right (ForallSem (f <$> sem))
+  a <$ Parser esa = Parser $
+    case esa of
+      Left e -> Left e
+      Right (ForallSem sem) -> Right (ForallSem (a <$ sem))
+instance ( forall sem. syn sem => Applicative sem
+         , Applicative (ForallSem syn)
+         , forall err. syn (Either err)
+         , syn (Parser syn) -- FIXME: what constraint is still missing to still require that?
+         ) => Applicative (Parser syn) where
+  pure a = Parser (Right (ForallSem (pure a)))
+  liftA2 f (Parser a) (Parser b) = Parser (liftA2 (liftA2 f) a b)
+  (<*>) (Parser f) (Parser a) = Parser (liftA2 (<*>) f a)
+  (*>) (Parser f) (Parser a) = Parser (liftA2 (*>) f a)
+  (<*) (Parser f) (Parser a) = Parser (liftA2 (<*) f a)
+instance ( forall sem. syn sem => Monad sem
+         , forall err. syn (Either err)
+         , syn (Parser syn) -- FIXME: what constraint is still missing to still require that?
+         ) => Monad (Parser syn) where
+  (>>=) (Parser efsa) f = Parser (efsa >>= \(ForallSem sa) -> sa >>= unParser . f)
+-}
+
+-- * Type 'BinTree'
+-- | /Binary Tree/.
+data BinTree a
+ =   BinTree0 a
+ |   BinTree2 (BinTree a) (BinTree a)
+ deriving (Eq, Show)
+
+type TermAST = BinTree TokenTerm
+data TokenTerm
+   = TokenTermAtom String
+   | TokenTermAbst String TyVT TermAST
+   deriving (Show)
+
+tree0 = add (lit 8) (neg (add (lit 1) (lit 2)))
+tree1 = mul (add (lit 8) (neg (add (lit 1) (lit 2)))) (lit 2)
+tree2 = lam (\(x::sem Int) -> mul (lit 0) (add (lit 1) (lit 2)))
+tree0Print = print tree0
+tree1Print = print tree1
+tree2Print = print tree2
+
+type ErrMsg = String
+
+safeRead :: Read a => String -> Either ErrMsg a
+safeRead s = case reads s of
+               [(x,"")] -> Right x
+               _        -> Left $ "Read error: " <> s
+
+instance (forall sem. syn sem => Addable sem) => Addable (ForallSem syn) where
+  lit n = ForallSem (lit n)
+  neg (ForallSem a) = ForallSem (neg a)
+  add (ForallSem a) (ForallSem b) = ForallSem (add a b)
+instance (forall sem. syn sem => Mulable sem) => Mulable (ForallSem syn) where
+  mul (ForallSem a) (ForallSem b) = ForallSem (mul a b)
+instance ( forall sem. syn sem => Abstractable sem
+         --, forall sem. syn sem => Typeable sem -- user instance not accepted
+         --, forall s1 s2. (syn s1, syn s2) => s1 ~ s2 -- crazy...
+         ) => Abstractable (ForallSem syn) where
+  ForallSem a2b .@ ForallSem a = ForallSem (a2b .@ a)
+  lam f = ForallSem (lam (\a -> let ForallSem b = f (forallSem a) in b))
+    where
+    -- Safe here because a :: sem a and b :: sem b share the same 'sem'.
+    forallSem :: sem a -> ForallSem syn a
+    forallSem a = ForallSem (unsafeCoerce a)
+
+
+
+parseAddable ::
+ ( forall sem. syn sem => Addable sem
+ , syn (ForallSem syn)
+ ) =>
+ (TermAST -> Parser syn) -> TermAST -> Parser syn
+parseAddable _final (BinTree0 (TokenTermAtom s))
+  | Right (i::Int) <- safeRead s
+  = Parser $ Right $ TermVT (TyConst LenZ typeRep) $ ForallSem @_ @Int $ lit i
+parseAddable final (BinTree2 (BinTree0 (TokenTermAtom "Neg")) aT) = Parser $
+  case final aT of
+    Parser aE -> do
+      TermVT aTy (ForallSem a :: ForallSem syn a) <- aE
+      case eqTyKi aTy (TyConst (lenVars aTy) (typeRep @Int)) of
+        Nothing -> Left "TypeError"
+        Just HRefl -> do
+          Right $ TermVT aTy $ neg a
+parseAddable final (BinTree2 (BinTree2 (BinTree0 (TokenTermAtom "Add")) aT) bT) =
+  Parser $ do
+    TermVT aTy (ForallSem a :: ForallSem syn a) <- unParser (final aT)
+    case eqTyKi aTy (TyConst (lenVars aTy) (typeRep @Int)) of
+      Nothing -> Left "TypeError"
+      Just HRefl -> do
+        TermVT bTy (ForallSem b :: ForallSem syn b) <- unParser (final bT)
+        case eqTyKi bTy (TyConst (lenVars bTy) (typeRep @Int)) of
+          Nothing -> Left "TypeError"
+          Just HRefl -> do
+            Right $ TermVT aTy $ ForallSem @_ @Int $ add a b
+parseAddable _final e = Parser $ Left $ "Invalid tree: " <> show e
+
+--tree0Parser :: (forall sem. syn sem => Addable sem, syn (ForallSem syn)) => Either ErrMsg (ForallSem syn Int)
+--tree0Parser = unParser $ parse tree0Print
+
+class    (Addable sem, Mulable sem, Abstractable sem) => FinalSyntaxes sem
+instance (Addable sem, Mulable sem, Abstractable sem) => FinalSyntaxes sem
+
+parseMulable ::
+  ( forall sem. syn sem => Mulable sem
+  , forall sem. syn sem => Addable sem
+  , syn (ForallSem syn)
+  ) =>
+  (TermAST -> Parser syn) -> TermAST -> Parser syn
+parseMulable final (BinTree2 (BinTree2 (BinTree0 (TokenTermAtom "Mul")) aT) bT) =
+  Parser $ do
+    case (final aT, final bT) of
+      (Parser aE, Parser bE) -> do
+        TermVT aTy (ForallSem a :: ForallSem syn a) <- aE
+        case eqTyKi aTy (TyConst (lenVars aTy) (typeRep @Int)) of
+          Nothing -> Left "TypeError"
+          Just HRefl -> do
+            TermVT bTy (ForallSem b :: ForallSem syn b) <- bE
+            case eqTyKi bTy (TyConst (lenVars bTy) (typeRep @Int)) of
+              Nothing -> Left "TypeError"
+              Just HRefl -> do
+                    Right $ TermVT aTy (ForallSem @_ @a $ mul a b)
+parseMulable final t = parseAddable final t
+
+parseAbstractable ::
+  ( forall sem. syn sem => Mulable sem
+  , forall sem. syn sem => Addable sem
+  , forall sem. syn sem => Abstractable sem
+  , syn (ForallSem syn)
+  ) =>
+  a ~ Int =>
+  (TermAST -> Parser syn) -> TermAST -> Parser syn
+-- TODO: parse variables
+parseAbstractable final (BinTree0 (TokenTermAbst n (TyVT (aTy::Ty vs a)) bT)) =
+  Parser $
+    case final bT of
+      Parser bE -> do
+            TermVT abTy (abF :: ForallSem syn ab) <- bE
+            case eqTyKi (TyConst (lenVars aTy) (typeRep @Type)) aTy of
+              Nothing -> Left "TypeError"
+              Just HRefl -> do
+                case abTy of
+                  FUN iTy bTy ->
+                    case eqTyKi (TyConst (lenVars bTy) (typeRep @Type)) bTy of
+                      Nothing -> Left "TypeError"
+                      Just HRefl -> do
+                        -- FIXME: appendVars
+                        case eqTyKi aTy iTy of
+                          Nothing -> Left "TypeError"
+                          Just HRefl -> do
+                            case abF of
+                              ForallSem ab ->
+                                Right $ TermVT abTy $ ForallSem @_ @ab $
+                                  -- FIXME: Tyable
+                                  lam (ab .@)
+                  _ -> Left "TypeError"
+parseAbstractable final t = parseMulable final t
+
+pattern FUN :: forall k (fun :: k) vs. ()
+            => forall (r1 :: RuntimeRep) (r2 :: RuntimeRep)
+                      (arg :: TYPE r1) (res :: TYPE r2).
+               (k ~ Type, fun ~~ (arg -> res))
+            => Ty vs arg
+            -> Ty vs res
+            -> Ty vs fun
+pattern FUN arg res <- (TyConst _len (eqTypeRep (typeRep @(->)) -> Just HRefl) `TyApp` arg) `TyApp` res
+  where FUN arg res = (TyConst (lenVars arg) (typeRep @(->)) `TyApp` arg) `TyApp` res
+
+-- * Class 'LenVars'
+-- | Return the 'Len' of the 'Var' context.
+class LenVars a where
+  lenVars :: a -> Len (VarsOf a)
+
+type instance VarsOf (Ty vs a) = vs
+instance LenVars (Ty vs a) where
+  lenVars (TyConst len _c) = len
+  lenVars (TyApp f _a)     = lenVars f
+  lenVars (TyVar _n v)     = lenVars v
+  -- lenVars (TyFam l _f _as) = l
+
+-- * Type family 'VarsOf'
+-- | Return the 'Var' context of something.
+type family   VarsOf a :: [Type]
+type instance VarsOf (Var vs v) = vs
+--type instance VarsOf (UsedVars src vs vs') = vs'
+
+
+
+-- * Class 'AllocVars'
+-- | Allocate 'Var's in a 'Var' context,
+-- either to the left or to the right.
+class AllocVars (a::[Type] -> k -> Type) where
+  allocVarsL :: Len len -> a vs x -> a (len ++ vs) x
+  allocVarsR :: Len len -> a vs x -> a (vs ++ len) x
+
+appendVars ::
+ AllocVars a =>
+ AllocVars b =>
+ LenVars (a vs_x x) =>
+ LenVars (b vs_y y) =>
+ VarsOf (a vs_x x) ~ vs_x =>
+ VarsOf (b vs_y y) ~ vs_y =>
+ a vs_x (x::kx) ->
+ b vs_y (y::ky) ->
+ ( a (vs_x ++ vs_y) x
+ , b (vs_x ++ vs_y) y )
+appendVars x y =
+  ( allocVarsR (lenVars y) x
+  , allocVarsL (lenVars x) y
+  )
+
+
+
+-- Var
+data Var (vs::[Type]) (v::vK) :: Type where
+  VarZ :: TypeRep vk -> Len (Proxy (v::vK) ': vs) -> Var (Proxy (v::vK) ': vs) v
+  VarS :: Var vs v -> Var (not_v ': vs) v
+instance AllocVars Var where
+  allocVarsL LenZ v       = v
+  allocVarsL (LenS len) v = VarS (allocVarsL len v)
+
+  allocVarsR len (VarZ k l) = VarZ k (addLen l len)
+  allocVarsR len (VarS v)   = VarS (allocVarsR len v)
+
+data VarV vs = forall v. VarV (Var vs v)
+instance LenVars (Var vs v) where
+  lenVars (VarZ _k l) = l
+  lenVars (VarS v)    = LenS (lenVars v)
+
+deriving instance Show (Var vs v)
+
+eqVarKi :: Var vs x -> Var vs y -> Maybe ((x::kx) :~~: (y::ky))
+eqVarKi VarZ{} VarZ{}     = Just HRefl
+eqVarKi (VarS x) (VarS y) = eqVarKi x y
+eqVarKi _ _               = Nothing
+
+-- Vars
+data Vars (vs::[Type]) :: Type where
+  -- VarsZ represents an empty context.
+  VarsZ :: Vars '[]
+  -- A cons stores a variable name and its type,
+  -- and then the rest of the context.
+  VarsS :: String -> TypeRep vK -> Vars vs -> Vars (Proxy (v::vK) ': vs)
+type instance VarsOf (Vars vs) = vs
+instance LenVars (Vars vs) where
+  lenVars VarsZ = LenZ
+  lenVars (VarsS _n _kv vs) = LenS $ lenVars vs
+
+
+-- Vars
+data Env :: [Type] -> Type where
+  ENil :: Env '[]
+  ECons :: v -> Env vs -> Env (v ': vs)
+
+-- (!) :: Env vs -> Var vs v -> v
+-- (ECons x _) ! VarZ = x
+-- (ECons _ e) ! (VarS x) = e ! x
+-- ENil ! _ = error "GHC can't tell this is impossible"
+
+lookupVars :: String -> Vars vs -> Maybe (VarV vs)
+lookupVars _ VarsZ = Nothing
+lookupVars n (VarsS vN vK vs)
+  | n == vN = Just (VarV (VarZ vK (LenS (lenVars vs))))
+  | otherwise = (\(VarV v) -> VarV (VarS v)) <$> lookupVars n vs
+
+
+fix :: (a -> a) -> a
+fix f = f (fix f)
+
+parse ::
+  ( forall sem. syn sem => Addable sem
+  , forall sem. syn sem => Mulable sem
+  , forall sem. syn sem => Abstractable sem
+  , syn (ForallSem syn)
+  ) =>
+  TermAST -> Parser syn
+parse = fix parseAbstractable
+
+tree0ParsePrint :: TermAST
+tree0ParsePrint = case parse @FinalSyntaxes tree0Print of
+  Parser (Left e) -> error e
+  Parser (Right (TermVT _ty (ForallSem sem))) -> print sem
+
+tree1ParsePrint :: TermAST
+tree1ParsePrint = case parse @FinalSyntaxes tree1Print of
+  Parser (Left e) -> error e
+  Parser (Right (TermVT _ty (ForallSem sem))) -> print sem
+
+tree2ParsePrint :: TermAST
+tree2ParsePrint = case parse @FinalSyntaxes tree2Print of
+  Parser (Left e) -> error e
+  Parser (Right (TermVT _ty (ForallSem sem))) -> print sem
+
+class Addable sem where
+  lit :: Int -> sem Int
+  neg :: sem Int -> sem Int
+  add :: sem Int -> sem Int -> sem Int
+class Mulable sem where
+  mul :: sem Int -> sem Int -> sem Int
+class Abstractable sem where
+  -- | Lambda term abstraction, in HOAS (Higher-Order Abstract Syntax) style.
+  lam :: Typeable a => (sem a -> sem b) -> sem (a -> b)
+  -- | Application, aka. unabstract.
+  (.@) :: sem (a -> b) -> sem a -> sem b
+
+
+-- * Interpreter 'Printer'
+newtype Printer a = Printer { unPrinter :: TermAST }
+print :: Printer a -> TermAST
+print = unPrinter
+print2 :: String -> Printer a1 -> Printer a2 -> Printer a3
+print2 n (Printer aT) (Printer bT) = Printer $ BinTree2 (BinTree2 (BinTree0 (TokenTermAtom n)) aT) bT
+instance Addable Printer where
+  lit n = Printer $ BinTree0 (TokenTermAtom (show n))
+  neg (Printer aT) = Printer $ BinTree2 (BinTree0 (TokenTermAtom "Neg")) aT
+  add = print2 "Add"
+instance Mulable Printer where
+  mul = print2 "Mul"
+instance Abstractable Printer where
+  Printer f .@ Printer a = Printer $ BinTree2 f a
+  lam (f::Printer a -> Printer b) = Printer $
+                                  -- FIXME: Tyable? instead of Typeable?
+      BinTree0 (TokenTermAbst "x" (TyVT (TyConst (lenInj @_ @'[]) (typeRep @a)))
+                                  (unPrinter (f (Printer (BinTree0 (TokenTermAtom "x"))))))
diff --git a/symantic.cabal b/symantic.cabal
new file mode 100644
index 0000000..4c0004d
--- /dev/null
+++ b/symantic.cabal
@@ -0,0 +1,249 @@
+cabal-version:      3.0
+name:               literate-web
+maintainer:         mailto:literate-web@sourcephile.fr
+bug-reports:        https://mails.sourcephile.fr/inbox/literate-web
+homepage:           https://git.sourcephile.fr/literate-web.git
+author:             Julien Moutinho <julm+literate-web@sourcephile.fr>
+copyright:          Julien Moutinho <julm+literate-web@sourcephile.fr>
+license:            AGPL-3.0-or-later
+license-file:       LICENSES/AGPL-3.0-or-later.txt
+
+--             PVP: +-+------- breaking API changes
+--                  | | +----- non-breaking API additions
+--                  | | | +--- code changes with no API change
+version:            0.0.0.20221117
+stability:          experimental
+category:           Web
+synopsis:           Haskell-website compiler
+description:
+  Exploring the design space of compile-time website generator
+  by using a domain-specific language (DSL)
+  embedded into the Haskell language.
+  .
+  Alternatives:
+  .
+  * <https://hackage.haskell.org/package/ema ema>
+
+build-type:         Simple
+tested-with:        GHC ==9.2.4
+extra-doc-files:    ChangeLog.md
+extra-source-files:
+  .envrc
+  cabal.project
+  flake.lock
+  flake.nix
+
+extra-tmp-files:
+
+source-repository head
+  type:     git
+  location: git://git.sourcephile.fr/haskell/literate-web.git
+
+common boilerplate
+  default-language:   Haskell2010
+  default-extensions:
+    NoImplicitPrelude
+    BlockArguments
+    DataKinds
+    DefaultSignatures
+    DeriveDataTypeable
+    DeriveGeneric
+    DerivingVia
+    FlexibleContexts
+    FlexibleInstances
+    GADTs
+    ImportQualifiedPost
+    LambdaCase
+    MultiParamTypeClasses
+    NamedFieldPuns
+    NumericUnderscores
+    OverloadedStrings
+    RecordWildCards
+    ScopedTypeVariables
+    TupleSections
+    TypeApplications
+    TypeFamilies
+    TypeOperators
+
+  ghc-options:
+    -Wall -Wincomplete-record-updates -Wincomplete-uni-patterns
+    -Wmonomorphism-restriction -Wpartial-fields
+    -fprint-potential-instances
+
+-- -dshow-passes
+-- -ddump-to-file
+-- -ddump-simpl
+-- -dsuppress-coercions
+-- -dsuppress-module-prefixes
+-- -dsuppress-type-applications
+-- -O0
+-- -fmax-simplifier-iterations=0
+
+common library-deps
+  build-depends:
+    , async                  >=2.2
+    , base                   >=4.6  && <5
+    , bytestring             >=0.10
+    , containers             >=0.5
+    , directory              >=1.3
+    , filepath               >=1.4
+    , filepattern            >=0.1
+    , hashable
+    , http-client            >=0.6
+    , http-media             >=0.7
+    , monad-classes
+    , peano
+    , reflection
+    , symantic-base          >=0.5
+    , template-haskell
+    , text
+    , transformers           >=0.5
+    , unicode-transforms     >=0.2
+    , unordered-containers
+    , uri-encode             >=1.5
+    , wai
+    , wai-middleware-static
+    , wai-websockets
+    , warp
+    , websockets             >=0.12
+    , pipes
+    , pipes-safe
+    , pipes-concurrency
+    , pipes-parse
+    , pipes-group
+    , mvc
+    , mvc-updates
+    , ghc-prim
+
+library
+  import:          boilerplate, library-deps
+  hs-source-dirs:  src
+  exposed-modules:
+    ForallSem
+    Literate.Web
+    Literate.Web.Semantics.Compiler
+    Literate.Web.Semantics.Addresser
+    Literate.Web.Syntaxes
+    Literate.Web.Types.MIME
+    Literate.Web.Types.URL
+
+library relactive
+  import:          boilerplate, library-deps
+  hs-source-dirs:  src
+  build-depends:
+    , async
+    , contravariant  >=1.5
+    , stm
+    , monad-classes
+
+  exposed-modules:
+    Control.Reactive
+    Control.Reactive.IORef
+    Control.Reactive.MVar
+    Control.Reactive.Relation
+    Control.Reactive.STRef
+    Control.Reactive.TVar
+    Control.Reactive.Value
+
+-- Literate.Web.Semantics.Server
+-- Literate.Web.Semantics.Client
+--Literate.Web.Decoder
+--Literate.Web.Encoder
+--Literate.Web.Generator
+--Literate.Web.MIME
+
+--Literate.Web.Decoder
+test-suite literate-web-tests
+  -- library-deps is only to have ghcid reloaded on changes in src
+  import:          boilerplate, library-deps
+  type:            exitcode-stdio-1.0
+  hs-source-dirs:  tests
+  main-is:         Main.hs
+  other-modules:
+    Examples.Ex01
+    Examples.Ex02
+    Examples.Ex03
+    Examples.Ex04
+    Examples.Ex05
+    Goldens
+    Paths_literate_web
+    Utils
+  autogen-modules: Paths_literate_web
+  build-depends:
+    , base           >=4.6  && <5
+    , containers     >=0.5
+    , literate-web
+    , monad-classes
+    , relude
+    , symantic-base  >=0.5
+    , tasty          >=0.11
+    , tasty-golden   >=2.3
+    , tasty-hunit    >=0.9
+    , text           >=1.2
+    , transformers   >=0.5
+    , relactive
+
+-- , relude         >=1    && <2
+benchmark time
+  import:         boilerplate, library-deps
+  type:           exitcode-stdio-1.0
+  hs-source-dirs: benchmarks/time
+  main-is:        Main.hs
+  build-depends:
+    , base         >=4.6 && <5
+    , relactive
+    , tasty
+    , tasty-bench
+
+  -- , relude >= 1
+  -- Set a larger allocation area (nursery)
+  -- to remove some noisiness of the garbage collection.
+  ghc-options:    -with-rtsopts=-A32m
+
+benchmark weigh
+  import:         boilerplate, library-deps
+  type:           exitcode-stdio-1.0
+  hs-source-dirs: benchmarks/weigh src
+  build-depends:
+    , async
+    , contravariant  >=1.5
+    , stm
+
+  main-is:        Main.hs
+  build-depends:
+    , base    >=4.6 && <5
+    , relude  >=1
+    , weigh
+
+--, relactive
+executable async
+  import:         boilerplate, library-deps
+  hs-source-dirs: executables/async
+  main-is:        Main.hs
+  build-depends:
+    , async
+    , base           >=4.6 && <5
+    , relactive
+    , relude
+    , symantic-base
+    , tasty
+    , tasty-bench
+
+-- , relude >= 1
+
+executable fsnotify
+  import:         boilerplate, library-deps
+  hs-source-dirs: executables/fsnotify
+  main-is:        Main.hs
+  build-depends:
+    , async
+    , base           >=4.6 && <5
+    , directory
+    , filepath
+    , fsnotify
+    , relactive
+    , relude         >=1
+    , symantic-base
+    , tasty
+    , tasty-bench
+    , time
-- 
2.47.0