https://github.com/ibizaman/hs-template-nix-reflexfrp
haskell project using nix and reflex frp
https://github.com/ibizaman/hs-template-nix-reflexfrp
Last synced: about 1 month ago
JSON representation
haskell project using nix and reflex frp
- Host: GitHub
- URL: https://github.com/ibizaman/hs-template-nix-reflexfrp
- Owner: ibizaman
- Created: 2021-01-04T05:09:28.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2021-08-18T19:25:48.000Z (almost 4 years ago)
- Last Synced: 2025-02-16T19:34:26.017Z (4 months ago)
- Language: Haskell
- Size: 41 KB
- Stars: 0
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Template Haskell Project
This is a template haskell project to quickly get started using nix
and Reflex FRP.The goal of this project is two-fold:
1. Provide a template to quickly start a new project using Reflex
FRP without needing to spend some time setting up the
infrastructure.
2. Provide an example app that is quite dumb but still mixes a few
interesting packages.## Create a project from this template:
Give no arguments for usage:
```
./src/hs-template-nix-reflexfrp/template.sh
No PARENTPATH given, aborting
USAGE: ./template.sh PARENTPATH PROJECTNAME GITHUBUSER FULLNAME CACHIXHANDLE
```Give the arguments to create a project:
```
./src/hs-template-nix-reflexfrp/template.sh src testproject ibizaman "Pierre Penninckx" ibizaman
Creating project in src/testproject
GITHUBUSER: ibizaman
FULLNAME: Pierre Penninckx
CACHIXHANDLE: ibizaman
Project is createdTasks to complete:
* Please update the android.frontend and ios.frontend fields in src/testproject/release.nix
* Cachix is configured to use ibizaman, please make sure you have read and write access to that project.
* Please create the CACHIX_TESTPROJECT_AUTHTOKEN variable in github containing the auth token for cachix, otherwise the github workflow will fail.
* Comment out some sections of modd.conf
```It also prints out a few tasks that were not automated or that cannot
be automated (setting up cachix for example).This `template.sh` script is opinionated and assumes you're using
github. It's good enough for now but I can tell you right away I don't
like that.## Files Layout
The app is composed of three packages:
- `backend/` package is a daemon managing a list of items stored in
a SQLite database.
- `app/` contains the main file that will generate the binary.
- `src/` contains the library used by `app/`.
- `test/` contains the tests testing the library in `src/`.
- `package.yaml` is an [`hpack`](https://github.com/sol/hpack)
file that generates the cabal file for the package.
- `frontend/` package is the reflex app providing a UI to add and
remove items by talking to the daemon.
- `app/` contains the main file that will generate the binary.
- `src/` contains the library used by `app/`.
- `test/` contains the tests testing the library in `src/`.
- `package.yaml` is an [`hpack`](https://github.com/sol/hpack)
file that generates the cabal file for the package.
- `common/` package is shared between the backend and the frontend.
- `src/` contains the library.
- `test/` contains the tests testing the library in `src/`.
- `package.yaml` is an [`hpack`](https://github.com/sol/hpack)
file that generates the cabal file for the package.The project root contains the files needed for the infrastructure:
- `cabal.project` is used by cabal to manage the project when compiling with ghc.
- `cabal-ghcjs.project` is used by cabal to manage the project when compiling with ghcjs.
- `Makefile` contains all the targets to build the project.
- `modd.conf` is the config file for [`modd`](https://github.com/cortesi/modd).
- Editor files:
- `hie.yaml` is the config file for [`haskell-language-server`](https://github.com/haskell/haskell-language-server/).
- Nix files:
- `default.nix` used when running `nix-build`.
- `shell.nix` used when running `nix-shell`.
- `shell.ghcjs.nix` like `shell.nix` but when compiling with ghcjs.
- `release.nix` is imported by `default.nix` and `shell.nix`.`nix-shell` includes
[`haskell-language-server`](https://github.com/haskell/haskell-language-server/),
an LSP server```bash
$ make cabals # run hpack on the package.yaml files to produce the .cabal files[...]
$ nix-shell --pure
[nix-shell] $ haskell-language-server-wrapper
Found "/home/ibizaman/projects/hs-template-nix/hie.yaml" for "/home/ibizaman/projects/hs-template-nix/a"
Module "/home/ibizaman/projects/hs-template-nix/a" is loaded by Cradle: Cradle {cradleRootDir = "/home/ibizaman/projects/hs-template-nix", cradleOptsProg = CradleAction: Stack}
Run entered for haskell-language-server-wrapper(haskell-language-server-wrapper) Version 0.4.0.0 x86_64 ghc-8.6.5
Current directory: /home/ibizaman/projects/hs-template-nix
Operating system: linux
Arguments: []
Cradle directory: /home/ibizaman/projects/hs-template-nix
Cradle type: StackTool versions found on the $PATH
cabal: Not found
stack: 2.3.1
ghc: 8.6.5Step 1/4: Finding files to test in /home/ibizaman/projects/hs-template-nix
Found 4 filesStep 2/4: Looking for hie.yaml files that control setup
Found 1 cradleStep 3/4: Initializing the IDE
Step 4/4: Type checking the file
[...]
[INFO] finish: User TypeCheck (took 0.04s)Completed (4 files worked, 0 files failed)
```A few code formatters:
- [`ormolu`](https://github.com/tweag/ormolu) (default formatter for haskell-language-server)
```bash
[nix-shell] $ ormolu --mode check $(find . -name '*.hs' -not -path "./.stack-work/*"); echo $?
```
- [`brittany`](https://github.com/lspitzner/brittany) (fails as code is formatter with ormolu)
```bash
[nix-shell] $ brittany --check-mode */*.hs; echo $?
1
```
- [`floskell`](https://hackage.haskell.org/package/floskell)
- [`fourmolu`](https://github.com/parsonsmatt/fourmolu)
- [`stylish-haskell`](https://github.com/jaspervdj/stylish-haskell)and [`hlint`](https://github.com/ndmitchell/hlint), a
code improvement suggester:
```bash
[nix-shell] $ hlint **/*.hs
No hints
```## Build with nix
```bash
$ nix-build --pure
$ ./result/bin/hs-template-nix-exe
Hello World
```This runs the tests too. Running this will always recompile
everything, you probably want to use the next section instead.## Build incrementally
```bash
$ nix-shell --pure --run 'stack build'
$ nix-shell --pure --run 'stack run
Hello World
```Or, using the makefile:
```bash
$ make build
$ make run
Hello World
```## Local hoogle server
```bash
$ nix-shell --pure --run 'stack build --haddock --haddock-deps'
$ nix-shell --pure --run 'stack hoogle -- generate --quiet --local'
$ nix-shell --pure --run 'stack hoogle -- server --local --port=65000 --no-security-headers'
```Or, using the makefile:
```bash
$ make hoogle-build hoogle-generate hoogle-serve
Server starting on port 65000 and host/IP Host "127.0.0.1"
```Then go to http://localhost:65000/?scope=package%3Ahs-template-nix to
see the documentation of this project. You will see the documentation
for the `Utils` package:
## Watch files
```bash
$ modd
```This starts a hoogle server with `make hoogle-serve` as well as runs
in parallel `make hoogle-build hoogle-generate` and `make test`
anytime a file has changed. See [modd.conf](modd.conf).## Cachix
Cachix integration is provided by the `ibizaman` binary cache:
```bash
cachix use ibizaman
```Or, with the Makefile:
```bash
make cachix-enable
```To push to it, run:
```bash
$ nix-build | cachix push ibizaman
$ nix-build shell.nix | cachix push ibizaman
```Or, with the Makefile:
```bash
make cachix-push
```## Github Action
A Github Action builds, tests and uploads the binary of the project as
an artifact. Thanks to cachix, this is pretty quick, under 2 minutes.To download it, follow these steps:
1. Go to the [latest github action run](https://github.com/ibizaman/hs-template-nix/actions).
2. Download the artifact.
3. Unzip it with `unzip hs-template-nix.zip`
4. set the executable permission bit with `chmod a+x hs-template-nix`
5. run it with `./hs-template-nix-exe` and you will see printed `"Hello World"`.## Editor integration
This is mostly about integrating your editor with the
`haskell-language-server` executable found inside the `nix-shell`,
which handles linters and formatters.About `haskell-language-server`, the [hie.nix](hie.nix) file helps it
find the files in the `app/`, `src/` and `test/` folders and know with
which stack target to associate it. Later if you extend your codebase,
you can run `nix-shell --pure --run 'stack ide targets'` to get a full
list like:```
$ nix-shell --pure --run 'stack ide targets'
hs-template-nix:lib
hs-template-nix:exe:hs-template-nix-exe
hs-template-nix:test:hs-template-nix-test
```### Emacs
I saw recommended to run your editor directly in nix-shell so it can
access all needed binaries but I don't really like that. Also, I run
an emacs daemon so there's no way this can scale.Anyway, here is a working config that gracefully loads binary from
inside nix-shell whenever the project is using nix.```elisp
(setq-default tab-width 4)
(defun my/disable-tabs ()
"Disable tabs and set them to 4 spaces."
(setq-default tab-width 4)
(setq tab-width 4)
(setq indent-tabs-mode nil))
; Tabs are used to format buffer with `lsp-format-buffer'.
(add-hook 'haskell-mode-hook 'my/disable-tabs)(defun my/lsp-format-buffer-silent ()
"Silence errors from `lsp-format-buffer'."
(ignore-errors (lsp-format-buffer)))(use-package lsp-mode
:straight t
:commands lsp
:hook ((sh-mode . lsp-deferred)
(javascript-mode . lsp-deferred)
(html-mode . lsp-deferred)
(before-save . my/lsp-format-buffer-silent))
:config
(setq lsp-signature-auto-activate t)
(lsp-lens-mode t))(use-package lsp-ui
:straight t
:hook (lsp-mode-hook . lsp-ui-mode)
:commands lsp-ui-mode
:config
(setq lsp-ui-flycheck-enable t
lsp-ui-flycheck-live-reporting nil))(use-package company-lsp
:straight t
:commands company-lsp
:config
(push 'company-lsp company-backends))(use-package nix-sandbox
:straight t)(use-package haskell-mode
:straight t
:after nix-sandbox
:init
(defun my/haskell-set-stylish ()
(if-let* ((sandbox (nix-current-sandbox))
(fullcmd (nix-shell-command sandbox "brittany"))
(path (car fullcmd))
(args (cdr fullcmd)))
(setq-local haskell-mode-stylish-haskell-path path
haskell-mode-stylish-haskell-args args)))
(defun my/haskell-set-hoogle ()
(if-let* ((sandbox (nix-current-sandbox)))
(setq-local haskell-hoogle-command (nix-shell-string sandbox "hoogle"))))
:hook ((haskell-mode . capitalized-words-mode)
(haskell-mode . haskell-decl-scan-mode)
(haskell-mode . haskell-indent-mode)
(haskell-mode . haskell-indentation-mode)
(haskell-mode . my/haskell-set-stylish)
(haskell-mode . my/haskell-set-hoogle)
(haskell-mode . lsp-deferred)
(haskell-mode . haskell-auto-insert-module-template))
:config
(defun my/haskell-hoogle--server-command (port)
(if-let* ((hooglecmd `("hoogle" "serve" "--local" "-p" ,(number-to-string port)))
(sandbox (nix-current-sandbox)))
(apply 'nix-shell-command sandbox hooglecmd)
hooglecmd))
(setq haskell-hoogle-server-command 'my/haskell-hoogle--server-command
haskell-stylish-on-save t))(use-package lsp-haskell
:straight t
:after nix-sandbox
:init
(setq lsp-prefer-flymake nil)
(require 'lsp-haskell)
:config
;; from https://github.com/travisbhartwell/nix-emacs#haskell-mode
(defun my/nix--lsp-haskell-wrapper (args)
(if-let ((sandbox (nix-current-sandbox)))
(apply 'nix-shell-command sandbox args)
args))
(setq lsp-haskell-server-path "haskell-language-server-wrapper"
lsp-haskell-server-wrapper-function 'my/nix--lsp-haskell-wrapper))(use-package nix-mode
:straight t
:mode "\\.nix\\'"
:init
(require 'nix-build))
```