Nix introduction
This commit is contained in:
parent
0d1e4ff2a3
commit
e24de08c34
17 changed files with 836 additions and 120 deletions
|
@ -13,7 +13,7 @@ A very short and intense introduction to Haskell.
|
||||||
|
|
||||||
This is an update of my old (2012) article.
|
This is an update of my old (2012) article.
|
||||||
A lot of things have changed since then.
|
A lot of things have changed since then.
|
||||||
And I took the time to read it again.
|
I took the time to read it again.
|
||||||
#+end_notes
|
#+end_notes
|
||||||
|
|
||||||
#+begin_quote
|
#+begin_quote
|
||||||
|
@ -3476,29 +3476,12 @@ You should understand the essence of the Haskell language.
|
||||||
But now, to really be able to create something useful, you will need to
|
But now, to really be able to create something useful, you will need to
|
||||||
also understand not only the language but the ecosystem around it.
|
also understand not only the language but the ecosystem around it.
|
||||||
|
|
||||||
There are many different people involved with Haskell and some with quite
|
This chapter will focus on how to build applications with Haskell.
|
||||||
different point of view.
|
How to use libraries inside your project.
|
||||||
There are:
|
And perhaps provide a few pointers to start to organize your projects.
|
||||||
|
|
||||||
- /core contributers/, that write papers, contribute to GHC and participate
|
Note application development is easier to introduce than library development.
|
||||||
in language update proposals,
|
Mostly because dependency management will be a lot easier.
|
||||||
- /Haskell focused tools creators/,
|
|
||||||
people creating tools useful for the Haskell community like IDE engines
|
|
||||||
and plugins, tools to help building Haskell projects, etc...
|
|
||||||
- /library creators/,
|
|
||||||
- /application creators/, users that use the resources from the Haskell
|
|
||||||
ecosystem to create useful applications or solve problems using it.
|
|
||||||
|
|
||||||
Of course I am missing a lot of categories and also most Haskellers
|
|
||||||
participate in many different ones.
|
|
||||||
But it is important to bear in mind that Haskell is a free software for
|
|
||||||
anyone to use and participate in its enhancements.
|
|
||||||
In particular, about the missing categories are just Haskell hobyist that
|
|
||||||
write blog post about it, and participate in the community.
|
|
||||||
|
|
||||||
This entire section will be about, writing real-world applications.
|
|
||||||
How to start a new project, how to search for the libraries and use the
|
|
||||||
tools around Haskell for example.
|
|
||||||
|
|
||||||
#+begin_notes
|
#+begin_notes
|
||||||
If you find those part too hard, do not feel discouraged though, most
|
If you find those part too hard, do not feel discouraged though, most
|
||||||
|
@ -3511,57 +3494,593 @@ it really "clicked" for them.
|
||||||
:CUSTOM_ID: start-a-new-project
|
:CUSTOM_ID: start-a-new-project
|
||||||
:END:
|
:END:
|
||||||
|
|
||||||
There are multiple way to start a new project.
|
There are many different way to start.
|
||||||
The community exists for some time now, I can think of 3 "starter pack":
|
The most common one is certainly to use =cabal-install=.
|
||||||
|
Another popular option is to use =stack=.
|
||||||
|
=stack= add a layer on top of =cabal-install= and use fixed set of libraries
|
||||||
|
known to compile together.
|
||||||
|
The last option is to use =cabal-install= but take care of the dependencies
|
||||||
|
of your project via =nix=.
|
||||||
|
|
||||||
1. Use =cabal-install= (the source)
|
The last option is often considered as the most complex and difficult for
|
||||||
2. Use =stack=
|
beginner.
|
||||||
3. Use =nix=
|
And guess what?
|
||||||
|
Against all odds, I will introduce haskell project development via =nix=.
|
||||||
|
|
||||||
All methods will in the end need =cabal-install=.
|
Still, you shall not be intimidated. Look:
|
||||||
While, both the =cabal-install= and =stack= method are "easy" to start
|
|
||||||
with, the =nix= method is for more advanced user that want to invest time
|
|
||||||
in learning =nix=.
|
|
||||||
|
|
||||||
Also, my personal feeling is that for beginners the =stack= method should
|
- To create a new project the steps will be:
|
||||||
be preferred.
|
1. run =nix-shell= (to have =cabal= executable in your PATH)
|
||||||
By default it will "freeze" your dependencies and will improve your ability
|
2. run =cabal install -i= and answer a few questions
|
||||||
to have reproducible builds.
|
3. copy a few =.nix= files in your project directory
|
||||||
|
4. run another =nix-shell= in your new directory this time to enter in the
|
||||||
|
local dev env of your new project.
|
||||||
|
|
||||||
I wouldn't want to start any war on the tooling.
|
- To add a new library:
|
||||||
Technical choices like those are often the occasion for bikeshedding[fn:8]
|
1. Just add it in the =.cabal= file, and enter again in your =nix-shell=.
|
||||||
discussions and end up like many programming debates (space vs tabs, vim vs
|
|
||||||
emacs, linux vs macOs, c vs c++, etc...)
|
|
||||||
|
|
||||||
So even though I will assume that you will use the =stack= method to create
|
I will just walk you through all the steps in detail.
|
||||||
your project, I still want to show how you could only use =cabal-install=.
|
And mostly I will tell you not to take care about most warning messages.
|
||||||
|
For our end-goal, those are mostly noise.
|
||||||
|
I am aware of the level of complexity that it looks like at first.
|
||||||
|
But really most of the apparent complexity is due to poor naming convention
|
||||||
|
and not to any fundenmental core difficulty.
|
||||||
|
|
||||||
[fn:8] From [[https://en.wiktionary.org/wiki/bikeshedding][wikitionary]] the term was coined as a metaphor to illuminate
|
*** Bootstrap a project template files
|
||||||
Parkinson’s Law of Triviality.
|
|
||||||
Parkinson observed that a committee whose job is to approve plans for a
|
|
||||||
nuclear power plant may spend the majority of its time on relatively
|
|
||||||
unimportant but easy-to-grasp issues, such as what materials to use
|
|
||||||
for the staff bikeshed, while neglecting the design of the power
|
|
||||||
plant itself, which is far more important but also far more
|
|
||||||
difficult to criticize constructively.
|
|
||||||
It was popularized in the Berkeley Software Distribution community by
|
|
||||||
Poul-Henning Kamp and has spread from there to the software
|
|
||||||
industry at large.
|
|
||||||
|
|
||||||
*** =cabal-install=
|
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: -cabal-install-
|
:CUSTOM_ID: bootstrap-a-project-template-files
|
||||||
:END:
|
:END:
|
||||||
|
|
||||||
|
1. put the [[file:shell.nix][shell.nix]] file in some directory
|
||||||
|
2. start =nix-shell --pure=
|
||||||
|
3. in the nix shell create a new directory and then
|
||||||
|
4. =cabal init -i=
|
||||||
|
5. You should use the default value for most questions except:
|
||||||
|
1. Should I generate a simple project with sensible defaults? [default: y] n
|
||||||
|
2. the package should build "Library AND Executable" (choice 3)
|
||||||
|
3. Cabal specification 2.4 (choice 4)
|
||||||
|
4. Application directory choose =app= (choice 3)
|
||||||
|
5. Library directory choose =lib= (choice 3)
|
||||||
|
6. Add informative comments, choose yes.
|
||||||
|
|
||||||
|
Here is a full interaction:
|
||||||
|
|
||||||
|
#+begin_src
|
||||||
|
~/dev/hsenv> nix-shell
|
||||||
|
|
||||||
|
[hs:hsenv]> mkdir my-app
|
||||||
|
|
||||||
|
[hs:hsenv]> cd my-app/
|
||||||
|
|
||||||
|
[hs:my-app]> cabal init -i
|
||||||
|
Warning: The package list for 'hackage.haskell.org' does not exist. Run 'cabal
|
||||||
|
update' to download it.
|
||||||
|
Should I generate a simple project with sensible defaults? [default: y] n
|
||||||
|
What does the package build:
|
||||||
|
1) Executable
|
||||||
|
2) Library
|
||||||
|
3) Library and Executable
|
||||||
|
Your choice? 3
|
||||||
|
What is the main module of the executable:
|
||||||
|
* 1) Main.hs (does not yet exist, but will be created)
|
||||||
|
2) Main.lhs (does not yet exist, but will be created)
|
||||||
|
3) Other (specify)
|
||||||
|
Your choice? [default: Main.hs (does not yet exist, but will be created)]
|
||||||
|
Please choose version of the Cabal specification to use:
|
||||||
|
* 1) 1.10 (legacy)
|
||||||
|
2) 2.0 (+ support for Backpack, internal sub-libs, '^>=' operator)
|
||||||
|
3) 2.2 (+ support for 'common', 'elif', redundant commas, SPDX)
|
||||||
|
4) 2.4 (+ support for '**' globbing)
|
||||||
|
Your choice? [default: 1.10 (legacy)] 4
|
||||||
|
Package name? [default: my-app]
|
||||||
|
Package version? [default: 0.1.0.0]
|
||||||
|
Please choose a license:
|
||||||
|
1) GPL-2.0-only
|
||||||
|
2) GPL-3.0-only
|
||||||
|
3) LGPL-2.1-only
|
||||||
|
4) LGPL-3.0-only
|
||||||
|
5) AGPL-3.0-only
|
||||||
|
6) BSD-2-Clause
|
||||||
|
* 7) BSD-3-Clause
|
||||||
|
8) MIT
|
||||||
|
9) ISC
|
||||||
|
10) MPL-2.0
|
||||||
|
11) Apache-2.0
|
||||||
|
12) LicenseRef-PublicDomain
|
||||||
|
13) NONE
|
||||||
|
14) Other (specify)
|
||||||
|
Your choice? [default: BSD-3-Clause]
|
||||||
|
Author name? [default: Yann Esposito (Yogsototh)]
|
||||||
|
Maintainer email? [default: yann.esposito@gmail.com]
|
||||||
|
Project homepage URL?
|
||||||
|
Project synopsis?
|
||||||
|
Project category:
|
||||||
|
* 1) (none)
|
||||||
|
2) Codec
|
||||||
|
3) Concurrency
|
||||||
|
4) Control
|
||||||
|
5) Data
|
||||||
|
6) Database
|
||||||
|
7) Development
|
||||||
|
8) Distribution
|
||||||
|
9) Game
|
||||||
|
10) Graphics
|
||||||
|
11) Language
|
||||||
|
12) Math
|
||||||
|
13) Network
|
||||||
|
14) Sound
|
||||||
|
15) System
|
||||||
|
16) Testing
|
||||||
|
17) Text
|
||||||
|
18) Web
|
||||||
|
19) Other (specify)
|
||||||
|
Your choice? [default: (none)]
|
||||||
|
Application (Main.hs) directory:
|
||||||
|
* 1) (none)
|
||||||
|
2) src-exe
|
||||||
|
3) app
|
||||||
|
4) Other (specify)
|
||||||
|
Your choice? [default: (none)] 3
|
||||||
|
Library source directory:
|
||||||
|
* 1) (none)
|
||||||
|
2) src
|
||||||
|
3) lib
|
||||||
|
4) src-lib
|
||||||
|
5) Other (specify)
|
||||||
|
Your choice? [default: (none)] 2
|
||||||
|
Should I generate a test suite for the library? [default: y]
|
||||||
|
Test directory:
|
||||||
|
* 1) test
|
||||||
|
2) Other (specify)
|
||||||
|
Your choice? [default: test]
|
||||||
|
What base language is the package written in:
|
||||||
|
* 1) Haskell2010
|
||||||
|
2) Haskell98
|
||||||
|
3) Other (specify)
|
||||||
|
Your choice? [default: Haskell2010]
|
||||||
|
Add informative comments to each field in the cabal file (y/n)? [default: n] y
|
||||||
|
|
||||||
|
Guessing dependencies...
|
||||||
|
|
||||||
|
Generating LICENSE...
|
||||||
|
Generating Setup.hs...
|
||||||
|
Generating CHANGELOG.md...
|
||||||
|
Generating src/MyLib.hs...
|
||||||
|
Generating app/Main.hs...
|
||||||
|
Generating test/MyLibTest.hs...
|
||||||
|
Generating my-app.cabal...
|
||||||
|
|
||||||
|
Warning: no synopsis given. You should edit the .cabal file and add one.
|
||||||
|
You may want to edit the .cabal file and add a Description field.
|
||||||
|
|
||||||
|
[hs:my-app]>
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
#+begin_notes
|
||||||
|
Please ignore the following warning:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
Warning: The package list for 'hackage.haskell.org' does not exist. Run 'cabal
|
||||||
|
update' to download it.
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Nix should take care of handling Haskell libraries not =cabal-install=.
|
||||||
|
No need to run =cabal update=.
|
||||||
|
#+end_notes
|
||||||
|
|
||||||
|
After this step you should end up with the following set of files:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
[hs:my-app]> tree
|
||||||
|
.
|
||||||
|
├── CHANGELOG.md
|
||||||
|
├── LICENSE
|
||||||
|
├── Setup.hs
|
||||||
|
├── app
|
||||||
|
│ └── Main.hs
|
||||||
|
├── src
|
||||||
|
│ └── MyLib.hs
|
||||||
|
├── my-app.cabal
|
||||||
|
└── test
|
||||||
|
└── MyLibTest.hs
|
||||||
|
|
||||||
|
3 directories, 7 files
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
*** Create a few nix files
|
||||||
|
:PROPERTIES:
|
||||||
|
:CUSTOM_ID: create-a-few-nix-files
|
||||||
|
:END:
|
||||||
|
|
||||||
|
#+begin_notes
|
||||||
|
The goal of this tutorial is not to make you learn =nix= because it is a
|
||||||
|
bit complex, but to explain you a bit, =nix= use a a /configuration language/
|
||||||
|
and not just a /configuration format/.
|
||||||
|
So to configure your =nix= environment you endup writing a /nix expression/
|
||||||
|
in this /nix language/.
|
||||||
|
And thus you can call the content of one nix-file in another one for
|
||||||
|
example, or use variables.
|
||||||
|
#+end_notes
|
||||||
|
|
||||||
|
The first file to create is the one that will pin the versions of all your
|
||||||
|
packages and libraries:
|
||||||
|
|
||||||
|
{{{lnk(my-app/nixpkgs.nix)}}}
|
||||||
|
#+begin_src nix :tangle my-app/nixpkgs.nix :mkdirp t
|
||||||
|
import (fetchTarball https://github.com/NixOS/nixpkgs/archive/19.09.tar.gz) {}
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
The second file is the =default.nix= file:
|
||||||
|
|
||||||
|
{{{lnk(my-app/default.nix)}}}
|
||||||
|
#+begin_src nix :tangle my-app/default.nix :mkdirp t
|
||||||
|
{ nixpkgs ? import ./nixpkgs.nix
|
||||||
|
, compiler ? "default"
|
||||||
|
, doBenchmark ? false }:
|
||||||
|
let
|
||||||
|
inherit (nixpkgs) pkgs;
|
||||||
|
name = "my-app";
|
||||||
|
haskellPackages = pkgs.haskellPackages;
|
||||||
|
variant = if doBenchmark
|
||||||
|
then pkgs.haskell.lib.doBenchmark
|
||||||
|
else pkgs.lib.id;
|
||||||
|
drv = haskellPackages.callCabal2nix name ./. {};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
my_project = drv;
|
||||||
|
shell = haskellPackages.shellFor {
|
||||||
|
# generate hoogle doc
|
||||||
|
withHoogle = true;
|
||||||
|
packages = p: [drv];
|
||||||
|
# packages dependencies (by default haskellPackages)
|
||||||
|
buildInputs = with haskellPackages;
|
||||||
|
[ hlint
|
||||||
|
ghcid
|
||||||
|
cabal-install
|
||||||
|
cabal2nix
|
||||||
|
hindent
|
||||||
|
# # if you want to add some system lib like ncurses
|
||||||
|
# # you could by writing it like:
|
||||||
|
# pkgs.ncurses
|
||||||
|
];
|
||||||
|
# nice prompt for the nix-shell
|
||||||
|
shellHook = ''
|
||||||
|
export PS1="\n[${name}:\033[1;32m\]\W\[\033[0m\]]> "
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
It uses the =nixpkgs.nix= file.
|
||||||
|
But also you can configure it to enable/disable benchmarks while building
|
||||||
|
your application.
|
||||||
|
I do not expect you to understand what is really going on here, but a short
|
||||||
|
explanation is this file take cares of:
|
||||||
|
|
||||||
|
1. use the pinned version of nixpkgs and should provide a working set of
|
||||||
|
haskell libraries.
|
||||||
|
2. read you =.cabal= file and find the set of libraries you depends on so
|
||||||
|
=nix= will be able to download them.
|
||||||
|
3. download a few useful packages for Haskell development, in particular
|
||||||
|
=hlint=, =ghcid=, =cabal-install=, =cabal2nix= and =hindent=.
|
||||||
|
I will talk about those tools later.
|
||||||
|
4. take care of handling the =nix-shell= prompt so you should see the name
|
||||||
|
of your project.
|
||||||
|
|
||||||
|
The only things you should manipulate for a new fresh project should be the
|
||||||
|
=name= and perhaps the =buildInputs= list to add a few more libraries that
|
||||||
|
could be either Haskell libraries or any library =nix= know about (for
|
||||||
|
example =ncurses=, in that case you should write it =pkgs.ncurses=).
|
||||||
|
|
||||||
|
The two last file simply use the =default.nix= file:
|
||||||
|
|
||||||
|
The =shell.nix= file:
|
||||||
|
|
||||||
|
{{{lnk(my-app/shell.nix)}}}
|
||||||
|
#+begin_src nix :tangle my-app/shell.nix :mkdirp t
|
||||||
|
(import ./. {}).shell
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
And =release.nix=:
|
||||||
|
|
||||||
|
{{{lnk(my-app/release.nix)}}}
|
||||||
|
#+begin_src nix :tangle my-app/release.nix :mkdirp t
|
||||||
|
let
|
||||||
|
def = import ./. {};
|
||||||
|
in
|
||||||
|
{ my_project = def.my_project; }
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
So download those files as well as this =.gitignore= file:
|
||||||
|
|
||||||
|
|
||||||
|
{{{lnk(my-app/.gitignore)}}}
|
||||||
|
#+begin_src gitignore :tangle my-app/.gitignore :mkdirp t
|
||||||
|
dist-newstyle/
|
||||||
|
result
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
*** Checking your environment
|
||||||
|
:PROPERTIES:
|
||||||
|
:CUSTOM_ID: checking-your-environment
|
||||||
|
:END:
|
||||||
|
|
||||||
|
Now you should see those files in your project:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
[hs:my-app]> tree
|
||||||
|
.
|
||||||
|
├── CHANGELOG.md
|
||||||
|
├── LICENSE
|
||||||
|
├── Setup.hs
|
||||||
|
├── app
|
||||||
|
│ └── Main.hs
|
||||||
|
├── default.nix
|
||||||
|
├── src
|
||||||
|
│ └── MyLib.hs
|
||||||
|
├── my-app.cabal
|
||||||
|
├── nixpkgs.nix
|
||||||
|
├── release.nix
|
||||||
|
├── shell.nix
|
||||||
|
└── test
|
||||||
|
└── MyLibTest.hs
|
||||||
|
|
||||||
|
3 directories, 11 files
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
You shall now enter =nix-shell= again, but in your =my-app= directory this time.
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
[hs:my-app]> nix-shell
|
||||||
|
warning: Nix search path entry '/nix/var/nix/profiles/per-user/root/channels' does not exist, ignoring
|
||||||
|
building '/nix/store/j3hi4wm9996wfga61arc2917klfgspwr-cabal2nix-my-app.drv'...
|
||||||
|
installing
|
||||||
|
warning: Nix search path entry '/nix/var/nix/profiles/per-user/root/channels/nixpkgs' does not exist, ignoring
|
||||||
|
warning: file 'nixpkgs' was not found in the Nix search path (add it using $NIX_PATH or -I), at (string):1:9; will use bash from your environment
|
||||||
|
|
||||||
|
[my-app:my-app]> which ghcid
|
||||||
|
/nix/store/ckps9wgbmpckxdvs42p6sqz64dfqiv35-ghcid-0.7.5-bin/bin/ghcid
|
||||||
|
|
||||||
|
[my-app:my-app]> cabal run my-app
|
||||||
|
Build profile: -w ghc-8.6.5 -O1
|
||||||
|
In order, the following will be built (use -v for more details):
|
||||||
|
- my-app-0.1.0.0 (src) (first run)
|
||||||
|
- my-app-0.1.0.0 (exe:my-app) (first run)
|
||||||
|
Configuring library for my-app-0.1.0.0..
|
||||||
|
Preprocessing library for my-app-0.1.0.0..
|
||||||
|
Building library for my-app-0.1.0.0..
|
||||||
|
[1 of 1] Compiling MyLib ( src/MyLib.hs, /Users/y/hsenv/my-app/dist-newstyle/build/x86_64-osx/ghc-8.6.5/my-app-0.1.0.0/build/MyLib.o )
|
||||||
|
Configuring executable 'my-app' for my-app-0.1.0.0..
|
||||||
|
Preprocessing executable 'my-app' for my-app-0.1.0.0..
|
||||||
|
Building executable 'my-app' for my-app-0.1.0.0..
|
||||||
|
[1 of 1] Compiling Main ( app/Main.hs, /Users/y/hsenv/my-app/dist-newstyle/build/x86_64-osx/ghc-8.6.5/my-app-0.1.0.0/x/my-app/build/my-app/my-app-tmp/Main.o )
|
||||||
|
Linking /Users/y/hs-env/my-app/dist-newstyle/build/x86_64-osx/ghc-8.6.5/my-app-0.1.0.0/x/my-app/build/my-app/my-app ...
|
||||||
|
Hello, Haskell!
|
||||||
|
someFunc
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Great! It works!
|
||||||
|
Try to run it again:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
[my-app:my-app]> cabal run my-app
|
||||||
|
Up to date
|
||||||
|
Hello, Haskell!
|
||||||
|
someFunc
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
This time, the compilation is not done again.
|
||||||
|
=cabal= is smart enough not to repeat the compilation again.
|
||||||
|
|
||||||
|
#+begin_notes
|
||||||
|
You could also use =nix-build= to compile your app.
|
||||||
|
I think this is nice to do for releases.
|
||||||
|
But for development, you should use =cabal=.
|
||||||
|
#+end_notes
|
||||||
|
|
||||||
|
*** Add a library
|
||||||
|
:PROPERTIES:
|
||||||
|
:CUSTOM_ID: add-a-library
|
||||||
|
:END:
|
||||||
|
|
||||||
|
#+begin_notes
|
||||||
|
{{{tldr}}} do not be afraid by the lenght of this section in fact, this is
|
||||||
|
straightforward.
|
||||||
|
I just take a lot of time to go through all intermediate steps.
|
||||||
|
|
||||||
|
1. add the library in the =build-depends= inside your =.cabal= file.
|
||||||
|
2. restart =nix-shell= to download the new dependencies.
|
||||||
|
#+end_notes
|
||||||
|
|
||||||
|
If you open the =my-app.cabal= file in an editor you should see a =library=
|
||||||
|
section and and =executable my-app= section.
|
||||||
|
In particular for each section you can see a =build-depends= sub-section as
|
||||||
|
this one:
|
||||||
|
|
||||||
|
#+begin_src cabal
|
||||||
|
...
|
||||||
|
library
|
||||||
|
...
|
||||||
|
build-depends: base ^>=4.12.0.0
|
||||||
|
...
|
||||||
|
executable my-app
|
||||||
|
...
|
||||||
|
build-depends: base ^>=4.12.0.0, my-app
|
||||||
|
...
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
#+begin_notes
|
||||||
|
The =^>=4.12.0.0= means that it should use the latest non breaking version
|
||||||
|
of the haskell package =base=. The author of the =base= package are
|
||||||
|
responsible not to break the API for minor releases.
|
||||||
|
Haskell libs uses a 4 number versionning quite similar to the semantic
|
||||||
|
versionning scheme with just another minor number for non visible changes.
|
||||||
|
I will not argue much, but mainly, semantic versionning and Haskell
|
||||||
|
versionning are just a "right to break things to your users".
|
||||||
|
|
||||||
|
I don't want to talk a lot more about this, but, it would be nice if more
|
||||||
|
people would watch this talk[fn:9] related to versionning.
|
||||||
|
|
||||||
|
[fn:9]: [[https://www.youtube.com/watch?v=oyLBGkS5ICk][Spec-ulation Keynote - Rich Hickey]]
|
||||||
|
|
||||||
|
If you want to know more about Haskell versionning convention:
|
||||||
|
https://pvp.haskell.org
|
||||||
|
#+end_notes
|
||||||
|
|
||||||
|
Add the =protolude= lib in the library build-depends like this:
|
||||||
|
|
||||||
|
#+begin_src cabal
|
||||||
|
...
|
||||||
|
library
|
||||||
|
...
|
||||||
|
build-depends: base ^>=4.12.0.0,
|
||||||
|
protolude
|
||||||
|
...
|
||||||
|
executable my-app
|
||||||
|
...
|
||||||
|
build-depends: base ^>=4.12.0.0, my-app
|
||||||
|
...
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
#+begin_notes
|
||||||
|
I did not included a version constraint here.
|
||||||
|
This is ok if you do not deploy your library publicly.
|
||||||
|
This would be absolutely awful if you deploy your library publicly.
|
||||||
|
So while developing a private app nobody can see except you, nothing is
|
||||||
|
wrong with this.
|
||||||
|
But I would encourage you to write those version bounds.
|
||||||
|
It is sane to do that, but be warned that your lib might rot if you want it
|
||||||
|
to be part of a working set of libs.
|
||||||
|
So you might be pinged time to time to update some bounds or to adap your
|
||||||
|
code to the breaking change of a lib you are using.
|
||||||
|
Do not think too much about this.
|
||||||
|
This is generally quite trivial work to do to maintain your lib into a
|
||||||
|
working lib set.
|
||||||
|
#+end_notes
|
||||||
|
|
||||||
|
Now that you have added =protolude= modify slightly the code of your app to
|
||||||
|
use it.
|
||||||
|
Change the code inside =src/MyLib.hs=:
|
||||||
|
|
||||||
|
{{{lnk(my-app/src/MyLib.hs)}}}
|
||||||
|
#+begin_src haskell :tangle my-app/src/MyLib.hs :mkdirp t
|
||||||
|
{-# LANGUAGE NoImplicitPrelude #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
module MyLib (someFunc) where
|
||||||
|
|
||||||
|
import Protolude
|
||||||
|
|
||||||
|
someFunc :: IO ()
|
||||||
|
someFunc = putText "someFunc"
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
Please do not try to search right now about what this change is doing.
|
||||||
|
It should work mostly as before.
|
||||||
|
The goal here is just to check that you can use another library easily.
|
||||||
|
|
||||||
|
So now you should get out of the =nix-shell= because =nix= dependencies
|
||||||
|
changed.
|
||||||
|
Generally just type =^D= (=Ctrl-d=) then launch =nix-shell --pure=.
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
[my-app:my-app]> cabal build
|
||||||
|
Warning: The package list for 'hackage.haskell.org' does not exist. Run 'cabal
|
||||||
|
update' to download it.
|
||||||
|
Resolving dependencies...
|
||||||
|
cabal: Could not resolve dependencies:
|
||||||
|
[__0] trying: my-app-0.1.0.0 (user goal)
|
||||||
|
[__1] unknown package: protolude (dependency of my-app)
|
||||||
|
[__1] fail (backjumping, conflict set: my-app, protolude)
|
||||||
|
After searching the rest of the dependency tree exhaustively, these were the
|
||||||
|
goals I've had most trouble fulfilling: my-app, protolude
|
||||||
|
|
||||||
|
|
||||||
|
[my-app:my-app]> exit
|
||||||
|
|
||||||
|
[hs:my-app]> nix-shell
|
||||||
|
warning: Nix search path entry '/nix/var/nix/profiles/per-user/root/channels' does not exist, ignoring
|
||||||
|
building '/nix/store/sr4838rnmzn30j3qc5ray4i2n6n0p8pq-cabal2nix-my-app.drv'...
|
||||||
|
installing
|
||||||
|
|
||||||
|
[my-app:my-app]> cabal build
|
||||||
|
Build profile: -w ghc-8.6.5 -O1
|
||||||
|
In order, the following will be built (use -v for more details):
|
||||||
|
- my-app-0.1.0.0 (lib) (file src/MyLib.hs changed)
|
||||||
|
- my-app-0.1.0.0 (exe:my-app) (configuration changed)
|
||||||
|
Preprocessing library for my-app-0.1.0.0..
|
||||||
|
Building library for my-app-0.1.0.0..
|
||||||
|
[1 of 1] Compiling MyLib ( src/MyLib.hs, .../my-app/dist-newstyle/build/x86_64-osx/ghc-8.6.5/my-app-0.1.0.0/build/MyLib.o )
|
||||||
|
Configuring executable 'my-app' for my-app-0.1.0.0..
|
||||||
|
Preprocessing executable 'my-app' for my-app-0.1.0.0..
|
||||||
|
Building executable 'my-app' for my-app-0.1.0.0..
|
||||||
|
[1 of 1] Compiling Main ( app/Main.hs, .../my-app/dist-newstyle/build/x86_64-osx/ghc-8.6.5/my-app-0.1.0.0/x/my-app/build/my-app/my-app-tmp/Main.o ) [MyLib changed]
|
||||||
|
Linking .../my-app/dist-newstyle/build/x86_64-osx/ghc-8.6.5/my-app-0.1.0.0/x/my-app/build/my-app/my-app ...
|
||||||
|
|
||||||
|
[my-app:my-app]> cabal run my-app
|
||||||
|
Up to date
|
||||||
|
Hello, Haskell!
|
||||||
|
someFunc
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Yes!
|
||||||
|
|
||||||
|
*** Better defaults
|
||||||
|
:PROPERTIES:
|
||||||
|
:CUSTOM_ID: better-defaults
|
||||||
|
:END:
|
||||||
|
|
||||||
|
Some of the default values in the cabal file are not the best for a
|
||||||
|
professional and serious application development unfortunately.
|
||||||
|
First, let create a new block called =common professional-properties=
|
||||||
|
that will help us not repeat ourselve much and show more warning during compilation.
|
||||||
|
|
||||||
|
#+begin_src cabal
|
||||||
|
common professional-properties
|
||||||
|
default-language: Haskell2010
|
||||||
|
build-depends:
|
||||||
|
base ^>=4.12.0.0
|
||||||
|
ghc-options:
|
||||||
|
-Wall
|
||||||
|
-Wcompat
|
||||||
|
-Wincomplete-uni-patterns
|
||||||
|
-Wredundant-constraints
|
||||||
|
-Wnoncanonical-monad-instances
|
||||||
|
-- -Werror
|
||||||
|
-- -O2
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
This should then be used with import in all other sections (=library=,
|
||||||
|
=executable= and =test=).
|
||||||
|
Also add the =ghc-options= to enable the use of all core by default.
|
||||||
|
This might not always be a good idea.
|
||||||
|
But I think this is generally a better default for most modern application.
|
||||||
|
|
||||||
|
#+begin_src cabal
|
||||||
|
library
|
||||||
|
import: professional-properties
|
||||||
|
build-depends: protolude
|
||||||
|
...
|
||||||
|
|
||||||
|
executable my-app
|
||||||
|
import: professional-properties
|
||||||
|
ghc-options:
|
||||||
|
-- enable parallelism
|
||||||
|
-threaded
|
||||||
|
"-with-rtsopts=-N"
|
||||||
|
...
|
||||||
|
|
||||||
|
test-suite my-app-test
|
||||||
|
import: professional-properties
|
||||||
|
...
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
You can download the final cabal file: [[file:my-app/my-app.cabal][my-app.cabal]]
|
||||||
|
|
||||||
** Command line application
|
** Command line application
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: command-line-application
|
:CUSTOM_ID: command-line-application
|
||||||
:END:
|
:END:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
** Web Application
|
** Web Application
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: web-application
|
:CUSTOM_ID: web-application
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
import Data.Tree (Tree,Forest(..))
|
import Data.Tree (Tree,Forest(..))
|
||||||
import qualified Data.Tree as Tree
|
import qualified Data.Tree as Tree
|
||||||
|
|
||||||
data BinTree a = Empty
|
data BinTree a = Empty
|
||||||
| Node a (BinTree a) (BinTree a)
|
| Node a (BinTree a) (BinTree a)
|
||||||
deriving (Eq,Ord,Show)
|
deriving (Eq,Ord,Show)
|
||||||
|
|
||||||
-- | Function to transform our internal BinTree type to the
|
-- | Function to transform our internal BinTree type to the
|
||||||
-- type of Tree declared in Data.Tree (from containers package)
|
-- type of Tree declared in Data.Tree (from containers package)
|
||||||
-- so that the function Tree.drawForest can use
|
-- so that the function Tree.drawForest can use
|
||||||
binTreeToForestString :: (Show a) => BinTree a -> Forest String
|
binTreeToForestString :: (Show a) => BinTree a -> Forest String
|
||||||
binTreeToForestString Empty = []
|
binTreeToForestString Empty = []
|
||||||
binTreeToForestString (Node x left right) =
|
binTreeToForestString (Node x left right) =
|
||||||
[Tree.Node (show x) ((binTreeToForestString left) ++ (binTreeToForestString right))]
|
[Tree.Node (show x) ((binTreeToForestString left) ++ (binTreeToForestString right))]
|
||||||
|
|
||||||
-- | Function that given a BinTree print a representation of it in the console
|
-- | Function that given a BinTree print a representation of it in the console
|
||||||
prettyPrintTree :: (Show a) => BinTree a -> IO ()
|
prettyPrintTree :: (Show a) => BinTree a -> IO ()
|
||||||
prettyPrintTree = putStrLn . Tree.drawForest . binTreeToForestString
|
prettyPrintTree = putStrLn . Tree.drawForest . binTreeToForestString
|
||||||
|
|
||||||
nullTree = Node 0 nullTree nullTree
|
nullTree = Node 0 nullTree nullTree
|
||||||
|
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
import Data.Tree (Tree,Forest(..))
|
import Data.Tree (Tree,Forest(..))
|
||||||
import qualified Data.Tree as Tree
|
import qualified Data.Tree as Tree
|
||||||
|
|
||||||
data BinTree a = Empty
|
data BinTree a = Empty
|
||||||
| Node a (BinTree a) (BinTree a)
|
| Node a (BinTree a) (BinTree a)
|
||||||
deriving (Eq,Ord,Show)
|
deriving (Eq,Ord,Show)
|
||||||
|
|
||||||
-- | Function to transform our internal BinTree type to the
|
-- | Function to transform our internal BinTree type to the
|
||||||
-- type of Tree declared in Data.Tree (from containers package)
|
-- type of Tree declared in Data.Tree (from containers package)
|
||||||
-- so that the function Tree.drawForest can use
|
-- so that the function Tree.drawForest can use
|
||||||
binTreeToForestString :: (Show a) => BinTree a -> Forest String
|
binTreeToForestString :: (Show a) => BinTree a -> Forest String
|
||||||
binTreeToForestString Empty = []
|
binTreeToForestString Empty = []
|
||||||
binTreeToForestString (Node x left right) =
|
binTreeToForestString (Node x left right) =
|
||||||
[Tree.Node (show x) ((binTreeToForestString left) ++ (binTreeToForestString right))]
|
[Tree.Node (show x) ((binTreeToForestString left) ++ (binTreeToForestString right))]
|
||||||
|
|
||||||
-- | Function that given a BinTree print a representation of it in the console
|
-- | Function that given a BinTree print a representation of it in the console
|
||||||
prettyPrintTree :: (Show a) => BinTree a -> IO ()
|
prettyPrintTree :: (Show a) => BinTree a -> IO ()
|
||||||
prettyPrintTree = putStrLn . Tree.drawForest . binTreeToForestString
|
prettyPrintTree = putStrLn . Tree.drawForest . binTreeToForestString
|
||||||
|
|
||||||
-- | take all element of a BinTree up to some depth
|
-- | take all element of a BinTree up to some depth
|
||||||
treeTakeDepth _ Empty = Empty
|
treeTakeDepth _ Empty = Empty
|
||||||
treeTakeDepth 0 _ = Empty
|
treeTakeDepth 0 _ = Empty
|
||||||
treeTakeDepth n (Node x left right) = let
|
treeTakeDepth n (Node x left right) = let
|
||||||
nl = treeTakeDepth (n-1) left
|
nl = treeTakeDepth (n-1) left
|
||||||
nr = treeTakeDepth (n-1) right
|
nr = treeTakeDepth (n-1) right
|
||||||
in
|
in
|
||||||
|
|
2
src/posts/0010-Haskell-Now/my-app/.gitignore
vendored
Normal file
2
src/posts/0010-Haskell-Now/my-app/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
dist-newstyle/
|
||||||
|
result
|
5
src/posts/0010-Haskell-Now/my-app/CHANGELOG.md
Normal file
5
src/posts/0010-Haskell-Now/my-app/CHANGELOG.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# Revision history for my-app
|
||||||
|
|
||||||
|
## 0.1.0.0 -- YYYY-mm-dd
|
||||||
|
|
||||||
|
* First version. Released on an unsuspecting world.
|
30
src/posts/0010-Haskell-Now/my-app/LICENSE
Normal file
30
src/posts/0010-Haskell-Now/my-app/LICENSE
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
Copyright (c) 2019, Yann Esposito (Yogsototh)
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following
|
||||||
|
disclaimer in the documentation and/or other materials provided
|
||||||
|
with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of Yann Esposito (Yogsototh) nor the names of other
|
||||||
|
contributors may be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
2
src/posts/0010-Haskell-Now/my-app/Setup.hs
Normal file
2
src/posts/0010-Haskell-Now/my-app/Setup.hs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
import Distribution.Simple
|
||||||
|
main = defaultMain
|
8
src/posts/0010-Haskell-Now/my-app/app/Main.hs
Normal file
8
src/posts/0010-Haskell-Now/my-app/app/Main.hs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
module Main where
|
||||||
|
|
||||||
|
import qualified MyLib (someFunc)
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = do
|
||||||
|
putStrLn "Hello, Haskell!"
|
||||||
|
MyLib.someFunc
|
0
src/posts/0010-Haskell-Now/my-app/cabal.project.local
Normal file
0
src/posts/0010-Haskell-Now/my-app/cabal.project.local
Normal file
35
src/posts/0010-Haskell-Now/my-app/default.nix
Normal file
35
src/posts/0010-Haskell-Now/my-app/default.nix
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
{ nixpkgs ? import ./nixpkgs.nix
|
||||||
|
, compiler ? "default"
|
||||||
|
, doBenchmark ? false }:
|
||||||
|
let
|
||||||
|
inherit (nixpkgs) pkgs;
|
||||||
|
name = "my-app";
|
||||||
|
haskellPackages = pkgs.haskellPackages;
|
||||||
|
variant = if doBenchmark
|
||||||
|
then pkgs.haskell.lib.doBenchmark
|
||||||
|
else pkgs.lib.id;
|
||||||
|
drv = haskellPackages.callCabal2nix name ./. {};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
my_project = drv;
|
||||||
|
shell = haskellPackages.shellFor {
|
||||||
|
# generate hoogle doc
|
||||||
|
withHoogle = true;
|
||||||
|
packages = p: [drv];
|
||||||
|
# packages dependencies (by default haskellPackages)
|
||||||
|
buildInputs = with haskellPackages;
|
||||||
|
[ hlint
|
||||||
|
ghcid
|
||||||
|
cabal-install
|
||||||
|
cabal2nix
|
||||||
|
hindent
|
||||||
|
# # if you want to add some system lib like ncurses
|
||||||
|
# # you could by writing it like:
|
||||||
|
# pkgs.ncurses
|
||||||
|
];
|
||||||
|
# nice prompt for the nix-shell
|
||||||
|
shellHook = ''
|
||||||
|
export PS1="\n[${name}:\033[1;32m\]\W\[\033[0m\]]> "
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
97
src/posts/0010-Haskell-Now/my-app/my-app.cabal
Normal file
97
src/posts/0010-Haskell-Now/my-app/my-app.cabal
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
cabal-version: 2.4
|
||||||
|
|
||||||
|
-- Initial package description 'my-app.cabal' generated by 'cabal init'.
|
||||||
|
-- For further documentation, see http://haskell.org/cabal/users-guide/
|
||||||
|
|
||||||
|
-- The name of the package.
|
||||||
|
name: my-app
|
||||||
|
|
||||||
|
-- The package version. See the Haskell package versioning policy (PVP)
|
||||||
|
-- for standards guiding when and how versions should be incremented.
|
||||||
|
-- https://pvp.haskell.org
|
||||||
|
-- PVP summary: +-+------- breaking API changes
|
||||||
|
-- | | +----- non-breaking API additions
|
||||||
|
-- | | | +--- code changes with no API change
|
||||||
|
version: 0.1.0.0
|
||||||
|
|
||||||
|
-- A short (one-line) description of the package.
|
||||||
|
synopsis: app used for demonstration
|
||||||
|
|
||||||
|
-- A longer description of the package.
|
||||||
|
-- description:
|
||||||
|
|
||||||
|
-- A URL where users can report bugs.
|
||||||
|
-- bug-reports:
|
||||||
|
|
||||||
|
-- The license under which the package is released.
|
||||||
|
license: BSD-3-Clause
|
||||||
|
|
||||||
|
-- The file containing the license text.
|
||||||
|
license-file: LICENSE
|
||||||
|
|
||||||
|
-- The package author(s).
|
||||||
|
author: Yann Esposito (Yogsototh)
|
||||||
|
|
||||||
|
-- An email address to which users can send suggestions, bug reports, and
|
||||||
|
-- patches.
|
||||||
|
maintainer: yann.esposito@gmail.com
|
||||||
|
|
||||||
|
-- A copyright notice.
|
||||||
|
-- copyright:
|
||||||
|
|
||||||
|
-- category:
|
||||||
|
|
||||||
|
-- Extra files to be distributed with the package, such as examples or a
|
||||||
|
-- README.
|
||||||
|
extra-source-files: CHANGELOG.md
|
||||||
|
|
||||||
|
common shared-properties
|
||||||
|
default-language: Haskell2010
|
||||||
|
build-depends:
|
||||||
|
base ^>=4.12.0.0
|
||||||
|
ghc-options:
|
||||||
|
-Wall
|
||||||
|
-Wcompat
|
||||||
|
-Wincomplete-uni-patterns
|
||||||
|
-Wredundant-constraints
|
||||||
|
-Wnoncanonical-monad-instances
|
||||||
|
-- -Werror
|
||||||
|
-- -O2
|
||||||
|
|
||||||
|
library
|
||||||
|
import: shared-properties
|
||||||
|
-- Modules exported by the library.
|
||||||
|
exposed-modules: MyLib
|
||||||
|
build-depends: protolude
|
||||||
|
-- Modules included in this library but not exported.
|
||||||
|
-- other-modules:
|
||||||
|
-- LANGUAGE extensions used by modules in this package.
|
||||||
|
-- other-extensions:
|
||||||
|
-- Directories containing source files.
|
||||||
|
hs-source-dirs: src
|
||||||
|
|
||||||
|
executable my-app
|
||||||
|
import: shared-properties
|
||||||
|
-- .hs or .lhs file containing the Main module.
|
||||||
|
main-is: Main.hs
|
||||||
|
build-depends: my-app
|
||||||
|
ghc-options:
|
||||||
|
-- enable parallelism
|
||||||
|
-threaded
|
||||||
|
"-with-rtsopts=-N"
|
||||||
|
-- Modules included in this executable, other than Main.
|
||||||
|
-- other-modules:
|
||||||
|
-- LANGUAGE extensions used by modules in this package.
|
||||||
|
-- other-extensions:
|
||||||
|
-- Directories containing source files.
|
||||||
|
hs-source-dirs: app
|
||||||
|
|
||||||
|
test-suite my-app-test
|
||||||
|
import: shared-properties
|
||||||
|
-- The interface type and version of the test suite.
|
||||||
|
type: exitcode-stdio-1.0
|
||||||
|
-- The directory where the test specifications are found.
|
||||||
|
hs-source-dirs: test
|
||||||
|
-- The entrypoint to the test suite.
|
||||||
|
main-is: MyLibTest.hs
|
||||||
|
|
1
src/posts/0010-Haskell-Now/my-app/nixpkgs.nix
Normal file
1
src/posts/0010-Haskell-Now/my-app/nixpkgs.nix
Normal file
|
@ -0,0 +1 @@
|
||||||
|
import (fetchTarball https://github.com/NixOS/nixpkgs/archive/19.09.tar.gz) {}
|
4
src/posts/0010-Haskell-Now/my-app/release.nix
Normal file
4
src/posts/0010-Haskell-Now/my-app/release.nix
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
let
|
||||||
|
def = import ./. {};
|
||||||
|
in
|
||||||
|
{ my_project = def.my_project; }
|
1
src/posts/0010-Haskell-Now/my-app/shell.nix
Normal file
1
src/posts/0010-Haskell-Now/my-app/shell.nix
Normal file
|
@ -0,0 +1 @@
|
||||||
|
(import ./. {}).shell
|
8
src/posts/0010-Haskell-Now/my-app/src/MyLib.hs
Normal file
8
src/posts/0010-Haskell-Now/my-app/src/MyLib.hs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{-# LANGUAGE NoImplicitPrelude #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
module MyLib (someFunc) where
|
||||||
|
|
||||||
|
import Protolude
|
||||||
|
|
||||||
|
someFunc :: IO ()
|
||||||
|
someFunc = putText "someFunc"
|
4
src/posts/0010-Haskell-Now/my-app/test/MyLibTest.hs
Normal file
4
src/posts/0010-Haskell-Now/my-app/test/MyLibTest.hs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
module Main (main) where
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = putStrLn "Test suite not yet implemented."
|
|
@ -1,5 +1,5 @@
|
||||||
{ nixpkgs ? import (fetchTarball https://github.com/NixOS/nixpkgs/archive/19.09.tar.gz) {} }:
|
{ nixpkgs ? import (fetchTarball https://github.com/NixOS/nixpkgs/archive/19.09.tar.gz) {} }:
|
||||||
let
|
let
|
||||||
inherit (nixpkgs) pkgs;
|
inherit (nixpkgs) pkgs;
|
||||||
inherit (pkgs) haskellPackages;
|
inherit (pkgs) haskellPackages;
|
||||||
|
|
||||||
|
@ -16,11 +16,11 @@ let
|
||||||
pkgs.gdb
|
pkgs.gdb
|
||||||
haskellPackages.cabal-install
|
haskellPackages.cabal-install
|
||||||
];
|
];
|
||||||
in
|
in
|
||||||
pkgs.stdenv.mkDerivation {
|
pkgs.stdenv.mkDerivation {
|
||||||
name = "env";
|
name = "env";
|
||||||
buildInputs = nixPackages;
|
buildInputs = nixPackages;
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
export PS1="\n[hs:\033[1;32m\]\W\[\033[0m\]]> "
|
export PS1="\n[hs:\033[1;32m\]\W\[\033[0m\]]> "
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue