Repository: tweag/nickel
Pull request #336 (2021-04-01 13:08:09)

Update examples
Depend on #317 #296. Current examples are a bunch of small tests from an old version of the language that are more puzzling than helpful. This PR removes them and add more sensible ones, using current syntax and features. It adds them to the tests as well.

~~This PR is currently draft, waiting for #296 and others to land to update the contracts in a more realistic manner.~~


Patch:
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 00000000..b55cbc17
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,78 @@
+# Examples
+
+This directory contain an evolving selection of examples of Nickel programs.
+
+## Execution
+
+Please follow the main repository's README instructions to have a working nickel
+executable. You can then either use the command line or launch an interactive
+session. Play with the values to see contracts failing when fed with invalid
+data!
+
+### From the command line
+
+- *Base values*: some examples just return numbers, strings or booleans. You can run them directly:
+    ```
+    $ nickel -f fibonacci.ncl
+    Done: Num(55.0)
+    ```
+- *Configurations*: some return records representing configuration. Because of laziness, Nickel
+    won't currently print any readable result as is. Please use the `export`
+    subcommand to see the result serialized as JSON:
+    ```
+    $ nickel -f merge-main.ncl export
+    {
+      "firewall": {
+      ...
+    }
+    ```
+
+    Alternatively, you can query a configuration or a sub-element to get
+    list of attributes or the documentation:
+
+    ```
+    $ nickel -f record-contract.ncl query kind
+      • contract: <ReplicationController, ReplicaSet, Pod>
+      • value: `ReplicationController
+      • documentation: The kind of the element being configured.
+    ```
+
+### From the REPL
+
+First start the REPL:
+
+```
+$ nickel repl
+nickel>
+```
+
+- Just import a file directly to evaluate it:
+    ```
+    nickel> import "fibonacci.ncl"
+    55
+
+    nickel>
+    ```
+- Use `builtins.serialize` to have the same behavior as the `export` subcommand
+  and print the result as JSON:
+
+  ```
+  nickel> builtins.serialize `Json (import "merge-main.ncl")
+  "{
+      \"firewall\": {
+      ...
+  }"
+
+  nickel>
+  ```
+- Use `:query` to retrieve information and documentation about a field:
+
+  ```
+  nickel>let config = import "record-contract.ncl"
+  nickel>:query config.kind
+  • contract: <ReplicationController, ReplicaSet, Pod>
+  • value: `ReplicationController
+  • documentation: The kind of the element being configured.
+
+  nickel>
+  ```
diff --git a/examples/config-gcc/README.md b/examples/config-gcc/README.md
new file mode 100644
index 00000000..5dd09816
--- /dev/null
+++ b/examples/config-gcc/README.md
@@ -0,0 +1,31 @@
+# GCC Configuration
+
+This is an example of writing contracts and generating a configuration for an
+invocation of the `gcc` compiler.
+
+## Run
+
+```
+$ nickel -f config-gcc.ncl export
+```
+
+## Contracts
+
+This example defines a couple contracts:
+
+- `GccFlag`: define valid flags that can be passed to `gcc`. It demonstrates a
+    use-case where data can be specified in different format: here, a flag is
+    either a simple string like `"-Wextra"` or a structured value `{flag = "W",
+    arg = "extra"}`. The contract normalizes the value to a string, so that it
+    appears as such in the output. This contract is illustrative and by no mean
+    exhaustive.
+- `Path`: define a valid `/`-separated path as a string. In particular, the
+    `//` sequence is forbidden.
+- `SharedObjectFile`: define a file whose extension is `.so`.
+- `OptLevel`: optimization level, either `0`, `1` or `2`.
+- `Contract`: the schema of the end configuration.
+
+## Playground
+
+You can try to break any of the previous contracts to see what happens: provide
+a non supported flag, a `pathLibC` that doesn't end in `".so"`, and so on.
diff --git a/examples/config-gcc/config-gcc.ncl b/examples/config-gcc/config-gcc.ncl
new file mode 100644
index 00000000..19301944
--- /dev/null
+++ b/examples/config-gcc/config-gcc.ncl
@@ -0,0 +1,79 @@
+// Validate and normalize gcc flags. They can be either a string `-Wextra` or
+// a structured value `{flag = "W", arg = "extra"}`. Arguments are not checked.
+let GccFlag =
+  // We only allow the following flags
+  let available = ["W", "c", "S", "e", "o"] in
+  fun label value =>
+  if builtins.isStr value then
+    if strings.length value > 0 &&
+      lists.any (fun x => x == strings.substring value 0 1) available then
+      value
+    else
+      contracts.blame (contracts.tag "unknown flag #{value}" label)
+  else if builtins.isRecord value then
+    if records.hasField "flag" value && records.hasField "arg" value then
+      if lists.any (fun x => x == value.flag) available then
+        //Normalize the tag to a string
+        value.flag ++ value.arg
+      else
+        contracts.blame (contracts.tag "unkown flag #{value.flag}")
+    else
+      contracts.blame (contracts.tag "bad record structure: missing field
+        `flag` or `arg`" label)
+  else
+    contracts.blame (contracts.tag "expected record or string") in
+
+let Path =
+  let pattern = m#"^(.+)/([^/]+)$"#m in
+  fun label value =>
+    if builtins.isStr value then
+      if strings.isMatch value pattern then
+        value
+      else
+        contracts.blame (contracts.tag "invalid path" label)
+    else
+      contracts.blame (contracts.tag "not a string" label) in
+
+let SharedObjectFile = fun label value =>
+  if builtins.isStr value then
+    if strings.isMatch value m#"\.so$"#m then
+      value
+    else
+      contracts.blame (contracts.tag "not an .so file" label)
+  else
+    contracts.blame (contracts.tag "not a string" label) in
+
+let OptLevel = fun label value =>
+  if value == 0 || value == 1 || value == 2 then
+    value
+  else
+    contracts.blame label in
+
+let Contract = {
+  pathLibC | doc "Path to libc."
+           | #Path
+           | #SharedObjectFile
+           | default = "/lib/x86_64-linux-gnu/libc.so",
+
+  flags | doc "
+            Additional flags to pass to GCC. Either provide a string without the
+            leading `-`, or a structured value `{flag : Str, arg: Str}`.
+          "
+        | List #GccFlag
+        | default = [],
+
+  optimizationLevel | doc "
+                       Optimization level. Possible values:
+
+                        - *0*: unoptimized
+                        - *1*: normal
+                        - *2*: use optimizations
+                      "
+                    | #OptLevel
+                    | default = 1,
+} in
+
+{
+  flags = ["Wextra", {flag = "o", arg = "stuff.o"}],
+  optimizationLevel = 2,
+} | #Contract
diff --git a/examples/fibonacci/README.md b/examples/fibonacci/README.md
new file mode 100644
index 00000000..b63e6e05
--- /dev/null
+++ b/examples/fibonacci/README.md
@@ -0,0 +1,13 @@
+# Fibonacci
+
+This is a basic example of a recursive function: fibonnacci. This is the naive,
+exponential version of fibonacci: don't call it on a big value!
+
+Currently, only record bindings are recursive. To use a recursive function, one
+has to use a record.
+
+## Run
+
+```
+$ nickel -f fibonacci.ncl
+```
diff --git a/examples/fibonacci/fibonacci.ncl b/examples/fibonacci/fibonacci.ncl
new file mode 100644
index 00000000..2cd9c89b
--- /dev/null
+++ b/examples/fibonacci/fibonacci.ncl
@@ -0,0 +1,15 @@
+// Currently, only record bindings are recursive. To use a recursive function,
+// one has to use a record.
+
+// This is the naive, exponential version of fibonacci: don't call it on a big
+// value!
+let fibonacci = {
+ f = fun n =>
+  if n == 0 then
+    0 
+  else if n == 1 then
+    1
+  else 
+    f (n-1) + f (n-2)
+}.f in
+fibonacci 10
diff --git a/examples/lists/README.md b/examples/lists/README.md
new file mode 100644
index 00000000..395c0bf2
--- /dev/null
+++ b/examples/lists/README.md
@@ -0,0 +1,25 @@
+# Lists
+
+This example shows how to write generic, statically typed functions in Nickel.
+The code is illustrative: in practice, you should use the list stdlib functions
+`lists.map` and `lists.fold` instead of redefining your own.
+
+## Run
+
+```
+$ nickel -f lists.ncl export
+```
+
+## Playground
+
+To see the effect of static typing, you can alter any of the `map` or `fold`
+function to see the program fails before it is even executed.
+
+Another aspect is how Nickel guard typed function from untyped code by using
+contracts: at the end of the example, you can provide wrong arguments in the
+calls to `myListLib.map` or `myListLib.fold` to see contracts error appear:
+
+```
+-    let l = myListLib.map (fun x => x+1) [1, 2, 3, 4, 5, 6] in
++    let l = myListLib.map [1, 2, 3, 4, 5, 6] (fun x => x+1) in
+```
diff --git a/examples/lists/lists.ncl b/examples/lists/lists.ncl
new file mode 100644
index 00000000..0877827d
--- /dev/null
+++ b/examples/lists/lists.ncl
@@ -0,0 +1,28 @@
+// Example list functions. This code is illustrative: prefer using the list
+// stdlib functions `lists.map` and `lists.fold` instead.
+let myListLib = {
+  // We have to wrap `head` and `tail` with the right annotations if we want our
+  // map and fold to be statically typed. This is due to a current issue with
+  // the how the stdlib is typed, see
+  // https://github.com/tweag/nickel/issues/226. It won't be needed eventually.
+  head | forall a. List a -> a = fun l => %head% l,
+  tail | forall a. List a -> List a = fun l => %tail% l,
+
+  map : forall a b. (a -> b) -> List a -> List b = fun f list =>
+    if list == [] then
+      []
+    else
+      let head = head list in
+      let tail = tail list in
+      [f head] @ map f tail,
+
+  fold : forall a b. (a -> b -> b) -> List a -> b -> b =
+      fun f list first =>
+        if list == [] then
+          first
+        else
+          f (head list) (fold f (tail list) first),
+} in
+// Compute `7!`
+let l = myListLib.map (fun x => x+1) [1, 2, 3, 4, 5, 6] in
+myListLib.fold (fun x acc => x * acc) l 1
diff --git a/examples/merge/README.md b/examples/merge/README.md
new file mode 100644
index 00000000..52f63688
--- /dev/null
+++ b/examples/merge/README.md
@@ -0,0 +1,11 @@
+# Merge
+
+This example shows how to merge separate modules into one configuration. The
+code to run lies in `main.ncl`. The default value `firewall.enabled` defined in
+`security.ncl` is overwritten in the final config.
+
+## Run
+
+```
+$ nickel -f main.ncl export
+```
diff --git a/examples/merge/main.ncl b/examples/merge/main.ncl
new file mode 100644
index 00000000..a097d287
--- /dev/null
+++ b/examples/merge/main.ncl
@@ -0,0 +1,6 @@
+// Merge several blocks into one final configuration. In a real world case, one
+// would also want contracts to validate the shape of the data.
+let server = import "server.ncl" in
+let security = import "security.ncl" in
+// Disabling firewall in the final result
+server & security & {firewall.enabled = false}
diff --git a/examples/merge/security.ncl b/examples/merge/security.ncl
new file mode 100644
index 00000000..2ad69e5d
--- /dev/null
+++ b/examples/merge/security.ncl
@@ -0,0 +1,7 @@
+{
+  server.host.options = "TLS",
+  
+  firewall.enabled | default = true,
+  firewall.type = "iptables",
+  firewall.openPorts = [21,80,443],
+}
diff --git a/examples/merge/server.ncl b/examples/merge/server.ncl
new file mode 100644
index 00000000..eb11b585
--- /dev/null
+++ b/examples/merge/server.ncl
@@ -0,0 +1,5 @@
+{
+  server.host.ip = "182.168.1.1",
+  server.host.port = 80,
+  server.host.name = "hello-world.net",
+}
diff --git a/examples/polymorphism/README.md b/examples/polymorphism/README.md
new file mode 100644
index 00000000..1ef7e6c1
--- /dev/null
+++ b/examples/polymorphism/README.md
@@ -0,0 +1,18 @@
+# Polymorphism
+
+This example shows the usage of (statically typed) polymorphism in Nickel
+through `forall`. Polymorphic functions are the one operating on generic values.
+Polymorphic types can be nested in Nickel, although it is rarely needed. Note
+that they must always be written explcitily: the typechecker never infers
+polymorphic types for you.
+
+## Run
+
+```
+$ nickel -f polymorphism.ncl
+```
+
+## Playground
+
+Try to swap some type variables, or to mess with the definition of the functions
+to see the program failing to typecheck.
diff --git a/examples/polymorphism/polymorphism.ncl b/examples/polymorphism/polymorphism.ncl
new file mode 100644
index 00000000..891f51fa
--- /dev/null
+++ b/examples/polymorphism/polymorphism.ncl
@@ -0,0 +1,6 @@
+// First projection, statically typed
+let fst : forall a b. a -> b -> a = fun x y => x in
+// Evaluation function, statically typed
+let ev : forall a b. (a -> b) -> a -> b = fun f x => f x in
+let id : forall a. a -> a = fun x => x in
+(ev id (fst 5 10) == 5 : Bool)
diff --git a/examples/record-contract/README.md b/examples/record-contract/README.md
new file mode 100644
index 00000000..b0fc5511
--- /dev/null
+++ b/examples/record-contract/README.md
@@ -0,0 +1,17 @@
+# Kubernetes-like record contract
+
+This is an illustrative (thus incomplete and maybe incorrect) example of a
+Kubernetes configuration. It demonstrates how you can write contracts for simple
+elements (such as `Port`), and assemble them into a schema for your end
+configuration.
+
+## Run
+
+```
+$ nickel -f record-contract.ncl export
+```
+
+## Playground
+
+You can try to break any of the previous contracts to see what happens: provide
+a non supported flag, a `pathLibC` that doesn't end in `".so"`, and so on.
diff --git a/examples/record-contract/record-contract.ncl b/examples/record-contract/record-contract.ncl
new file mode 100644
index 00000000..1e894fe2
--- /dev/null
+++ b/examples/record-contract/record-contract.ncl
@@ -0,0 +1,88 @@
+// An illustrative (thus incomplete and maybe incorrect) contract example for a
+// Kubernetes configuration.
+
+let Port | doc "A contract for a port number" =
+  fun label value =>
+    if builtins.isNum value &&
+      value % 1 == 0 &&
+      value >= 0 &&
+      value <= 65535
+    then
+      value
+    else
+      contracts.blame label in
+
+let PortElt
+  | doc "A contract for a port element of a Kubernetes configuration"
+  = {
+  name | Str,
+  containerPort | #Port,
+} in
+
+let Container = {
+  name | Str,
+  image | Str,
+  ports | List #PortElt,
+} in
+
+let KubernetesConfig = {
+  kind | <ReplicationController, ReplicaSet, Pod>
+       | doc "The kind of the element being configured."
+       | default = `Pod,
+
+  apiVersion | Str,
+
+  metadata = {
+    name | Str,
+    labels.app | Str,
+  },
+
+  spec = {
+    replicas | #(nums.PosNat)
+             | doc "The number of replicas"
+             | default = 1,
+
+    selector.matchLabels.app | Str,
+
+    template = {
+      metadata.labels.app | Str,
+      spec.containers | List #Container,
+    },
+  },
+} in
+
+let name_ = "myApp" in
+let metadata_ = {
+    name = name_,
+    labels.app = name_,
+  } in
+
+{
+  kind = `ReplicationController,
+  apiVersion = "1.1.0",
+  metadata = metadata_,
+  spec = {
+    replicas = 3,
+    selector = {
+      app.name = name_,
+      matchLabels.app = name_,
+    },
+    template = {
+       metadata = metadata_,
+       spec = {
+          containers = [
+            {
+              name = name_,
+              image = "k8s.gcr.io/#{name_}:v3",
+              ports = [
+                {
+                  name = "http-server",
+                  containerPort = 80,
+                }
+              ]
+            }
+          ]
+        }
+    }
+  }
+} | #KubernetesConfig
diff --git a/examples/simple-contracts/README.md b/examples/simple-contracts/README.md
new file mode 100644
index 00000000..869b6cf2
--- /dev/null
+++ b/examples/simple-contracts/README.md
@@ -0,0 +1,23 @@
+# Simple contracts
+
+This directory contain two examples of simple contrtacts.
+
+The first one, `simple-contract-bool.ncl`, defines simple contracts on booleans
+and illustrates their use as pre-conditions and post-conditions of a function.
+
+The second one, `simple-contract-div.ncl`, defines simple contracts on numbers
+and illustrates the application of several contracts to one value. It is
+expected to fail, to demonstrate basic error-reporting for contracts.
+
+## Run
+
+```
+$ nickel -f simple-contract-bool.ncl
+$ nickel -f simple-contract-div.ncl
+```
+
+## Playground
+
+You can try to break the one of the contracts of `simple-contract-bool.ncl` to
+see them fail. Conversely, you can provide a valid value in
+`simple-contract-div.ncl` to see the execution succeed.
diff --git a/examples/simple-contracts/simple-contract-bool.ncl b/examples/simple-contracts/simple-contract-bool.ncl
new file mode 100644
index 00000000..1baeb6dc
--- /dev/null
+++ b/examples/simple-contracts/simple-contract-bool.ncl
@@ -0,0 +1,16 @@
+// Example of simple custom contract, parametrized by a first argument.
+let EqualsTo = fun referenceValue label value =>
+  if referenceValue == value then
+    value 
+  else
+    contracts.blame label in
+
+let AlwaysTrue = EqualsTo true in
+let AlwaysFalse = EqualsTo false in
+
+// This contract says: `not` requires its argument to be true, and in return
+// promise that the return value is false.
+// Try passing `false` to `not`, or to use the identity function (replacing `!x`
+// by `x`) to see contract errors appear.
+let not | #AlwaysTrue -> #AlwaysFalse = fun x => !x in
+not true 
diff --git a/examples/simple-contracts/simple-contract-div.ncl b/examples/simple-contracts/simple-contract-div.ncl
new file mode 100644
index 00000000..e46b024e
--- /dev/null
+++ b/examples/simple-contracts/simple-contract-div.ncl
@@ -0,0 +1,16 @@
+// /!\ THIS EXAMPLE IS EXPECTED TO FAIL
+// Illustrate a basic contract violation.
+
+let Even = fun label value =>
+  if builtins.isNum value && value % 2 == 0 then
+    value
+  else
+    contracts.blame label in
+let DivBy3 = fun label value =>
+  if builtins.isNum value && value % 3 == 0 then
+    value
+  else
+    contracts.blame label in
+// Will cause an error! 4 is no divided by 3.
+(4 | #Even
+   | #DivBy3)
diff --git a/src/examples/fibo.ncl b/src/examples/fibo.ncl
deleted file mode 100644
index 40d04467..00000000
--- a/src/examples/fibo.ncl
+++ /dev/null
@@ -1,8 +0,0 @@
-let Y = Assume(((Num -> Num) -> Num -> Num) -> Num -> Num, fun f => (fun x => f (x x)) (fun x => f (x x))) in
-let dec : Num -> Num = fun x => x + (-1) in
-let or = Assume(Bool -> Bool -> Bool, fun x y => if x then x else y) in
-
-let fibo : Num -> Num = Y (fun fibo =>
-    (fun x => if or (isZero x) (isZero (dec x)) then 1 else (fibo (dec x)) + (fibo (dec (dec x))))) in
-let val : Num = 6 in
-fibo val
diff --git a/src/examples/flatPromise.ncl b/src/examples/flatPromise.ncl
deleted file mode 100644
index 6e648524..00000000
--- a/src/examples/flatPromise.ncl
+++ /dev/null
@@ -1,4 +0,0 @@
-let alwaysTrue = fun l t => let boolT = Assume(Bool, t) in
-    if boolT then boolT else blame l in
-let id : #alwaysTrue -> Bool -> #alwaysTrue = fun b x => if x then b else b in
-(id : #alwaysTrue -> Bool -> #alwaysTrue) false true
diff --git a/src/examples/flatPromise2.ncl b/src/examples/flatPromise2.ncl
deleted file mode 100644
index 7171fc3c..00000000
--- a/src/examples/flatPromise2.ncl
+++ /dev/null
@@ -1,4 +0,0 @@
-let alwaysTrue = fun l t => let boolT = Assume(Bool, t) in
-    if boolT then boolT else blame l in
-let id : #alwaysTrue -> Bool -> #alwaysTrue = fun b x => if x then Assume(#alwaysTrue, true) else b in
-(id Assume(#alwaysTrue, false) : Bool -> #alwaysTrue) true
diff --git a/src/examples/flatType.ncl b/src/examples/flatType.ncl
deleted file mode 100644
index 3238d030..00000000
--- a/src/examples/flatType.ncl
+++ /dev/null
@@ -1,3 +0,0 @@
-let alwaysTrue = fun l t => let boolT = Assume(Bool, t) in
-    if boolT then boolT else blame l in
-Assume(#alwaysTrue, true)
diff --git a/src/examples/flatType2.ncl b/src/examples/flatType2.ncl
deleted file mode 100644
index b6a15d8d..00000000
--- a/src/examples/flatType2.ncl
+++ /dev/null
@@ -1,6 +0,0 @@
-let alwaysTrue = fun l t => let boolT = Assume(Bool, t) in
-    if boolT then boolT else blame l in
-let alwaysFalse = fun l t => let boolT = Assume(Bool, t) in
-    if boolT then  blame l else boolT in
-let not = fun b => if b then false else true in
-Assume(#alwaysTrue -> #alwaysFalse, not ) true
diff --git a/src/examples/flatType3.ncl b/src/examples/flatType3.ncl
deleted file mode 100644
index f0043353..00000000
--- a/src/examples/flatType3.ncl
+++ /dev/null
@@ -1,27 +0,0 @@
-let Y = fun f => (fun x => f (x x)) (fun x => f (x x)) in
-let dec = fun x => x + (-1) in
-let or = fun x y => if x then x else y in
-let isEven = Y (fun f =>
-    (fun x =>
-        if (isZero x) then true
-        else (
-            if (isZero (dec x)) then false
-            else (f (dec (dec x)))
-        )
-    )
-) in
-let isDivBy3 = Y (fun f =>
-    (fun x =>
-        if (isZero x) then true
-        else (
-            if or (isZero (dec (dec x))) (isZero (dec x)) then false
-            else (f (dec (dec (dec x))))
-        )
-    )
-) in
-let toCtr = fun f l => fun x => (
-  if (isNum x) then (
-    if (f x) then x else blame l)
-  else blame l
-) in
-Assume(#(toCtr isDivBy3), 2)
diff --git a/src/examples/forall.ncl b/src/examples/forall.ncl
deleted file mode 100644
index 6d6fe7d8..00000000
--- a/src/examples/forall.ncl
+++ /dev/null
@@ -1,2 +0,0 @@
-let f : forall a. (forall b. a -> b ->  a) = fun x y => x in
-f
diff --git a/src/examples/forall2.ncl b/src/examples/forall2.ncl
deleted file mode 100644
index af200801..00000000
--- a/src/examples/forall2.ncl
+++ /dev/null
@@ -1,2 +0,0 @@
-let f : forall a. (forall b. b -> b) -> a -> a = fun f x => f x in
-(f (fun x => x : forall b. b -> b) 3) : Num
diff --git a/src/examples/forall3.ncl b/src/examples/forall3.ncl
deleted file mode 100644
index 003ede00..00000000
--- a/src/examples/forall3.ncl
+++ /dev/null
@@ -1,4 +0,0 @@
-let f0 : ((forall a. a -> a) -> Num) -> Num = fun f =>
-  f (fun y => y : forall b.  b -> b)
-in
-f0 (fun g => 3)
diff --git a/src/examples/forall4.ncl b/src/examples/forall4.ncl
deleted file mode 100644
index b49a5bec..00000000
--- a/src/examples/forall4.ncl
+++ /dev/null
@@ -1,3 +0,0 @@
-let g : forall a. a -> a = fun y => (fun x => x) y in
-let f : forall a. a -> a = fun y => g y in
-f
diff --git a/src/examples/higherOrder.ncl b/src/examples/higherOrder.ncl
deleted file mode 100644
index 3f2f89ed..00000000
--- a/src/examples/higherOrder.ncl
+++ /dev/null
@@ -1,4 +0,0 @@
-let f = fun g => Assume(Num, g true) in
-let id = fun x => x in
-let id2 = fun y => y in
-id2 (id (f id))
diff --git a/src/examples/higherOrder2.ncl b/src/examples/higherOrder2.ncl
deleted file mode 100644
index 45d1ca02..00000000
--- a/src/examples/higherOrder2.ncl
+++ /dev/null
@@ -1,4 +0,0 @@
-let f = fun g => Assume(Num, g true) in
-let id = fun x => x in
-let id2 = fun y => id y in
-id2 (f id)
diff --git a/src/examples/lists-all-any.ncl b/src/examples/lists-all-any.ncl
deleted file mode 100644
index e9d9ccf1..00000000
--- a/src/examples/lists-all-any.ncl
+++ /dev/null
@@ -1,17 +0,0 @@
-let Y = fun f => (fun x => f (x x)) (fun x => f (x x)) in
-let foldr_ =
-    fun self f acc l =>
-        if length l == 0 then acc
-        else
-            let h = head l in
-            let t = tail l in
-            let next_acc = self f acc t in
-            f next_acc h
-in
-let foldr = Y foldr_ in
-let and : Bool -> Bool -> Bool = fun x y => x && y in
-let or : Bool -> Bool -> Bool = fun x y => x || y in
-let all = fun pred l => foldr and true (map pred l) in
-let any = fun pred l => foldr or false (map pred l) in
-let isZ = fun x => x == 0 in
-or (any isZ [1, 1, 1, 1]) (all isZ [0, 0, 0, 0])
diff --git a/src/examples/lists-flatten.ncl b/src/examples/lists-flatten.ncl
deleted file mode 100644
index 2954d1b3..00000000
--- a/src/examples/lists-flatten.ncl
+++ /dev/null
@@ -1,14 +0,0 @@
-let Y = fun f => (fun x => f (x x)) (fun x => f (x x)) in
-let foldl_ =
-    fun self f acc l =>
-        if isZero (length l) then acc
-        else
-            let h = head l in
-            let t = tail l in
-            let next_acc = f acc h in
-            seq next_acc (self f next_acc t)
-in
-let foldl = Y foldl_ in
-let concat : List -> List -> List = fun x y => x @ y in
-let flatten = foldl concat [] in
-flatten [[1,2],[3,4],[]]
diff --git a/src/examples/lists-foldl.ncl b/src/examples/lists-foldl.ncl
deleted file mode 100644
index dbbf6d93..00000000
--- a/src/examples/lists-foldl.ncl
+++ /dev/null
@@ -1,12 +0,0 @@
-let Y = fun f => (fun x => f (x x)) (fun x => f (x x)) in
-let foldl_ =
-    fun self f acc l =>
-        if isZero (length l) then acc
-        else
-            let h = head l in
-            let t = tail l in
-            let next_acc = f acc h in
-            seq next_acc (self f next_acc t)
-in
-let foldl = Y foldl_ in
-foldl (fun x y => x + y) 0 ([1, 2, 3, 4, 5])
diff --git a/src/examples/lowerOrder.ncl b/src/examples/lowerOrder.ncl
deleted file mode 100644
index 7e82d8b0..00000000
--- a/src/examples/lowerOrder.ncl
+++ /dev/null
@@ -1,3 +0,0 @@
-let f = fun z => z + 3 in
-let check = fun y => Assume(Bool, y) in
-check (f 4)
diff --git a/src/examples/merge.ncl b/src/examples/merge.ncl
deleted file mode 100644
index ceff0946..00000000
--- a/src/examples/merge.ncl
+++ /dev/null
@@ -1,11 +0,0 @@
-let r1 = {
-    a = false;
-    b = if true then 1 + 1 else 2 + 0;
-    c = ((fun x => x) (fun y => y)) 2;
-} in
-let r2 = {
-    b = ((fun x => x) (fun y => y)) 2;
-    c = if true then 1 + 1 else 2 + 0;
-    d = true;
-} in
-(r1 & r2).b
diff --git a/src/examples/merge2.ncl b/src/examples/merge2.ncl
deleted file mode 100644
index dca8cf06..00000000
--- a/src/examples/merge2.ncl
+++ /dev/null
@@ -1,2 +0,0 @@
-let f = fun y => ((fun x => {a=y}) 0) & ({b=false}) in
-(f 1).a
diff --git a/src/examples/merge3.ncl b/src/examples/merge3.ncl
deleted file mode 100644
index e939df73..00000000
--- a/src/examples/merge3.ncl
+++ /dev/null
@@ -1,4 +0,0 @@
-let rNest1 = {b={c=10}} in
-let rNest2 = (fun x => {a=x; b={c=x}}) 10 in
-let r = rNest1 & rNest2 in
-r.b.c
diff --git a/src/examples/mergeContracts.ncl b/src/examples/mergeContracts.ncl
deleted file mode 100644
index c2937461..00000000
--- a/src/examples/mergeContracts.ncl
+++ /dev/null
@@ -1,29 +0,0 @@
-let Y = fun f => (fun x => f (x x)) (fun x => f (x x)) in
-let dec = fun x => x + (-1) in
-let or = fun x y => if x then x else y in
-let isEven_ = Y (fun f =>
-    (fun x =>
-        if (isZero x) then true
-        else (
-            if (isZero (dec x)) then false
-            else (f (dec (dec x)))
-        )
-    )
-) in
-let isDivBy3_ = Y (fun f =>
-    (fun x =>
-        if (isZero x) then true
-        else (
-            if or (isZero (dec (dec x))) (isZero (dec x)) then false
-            else (f (dec (dec (dec x))))
-        )
-    )
-) in
-let toCtr = fun f l x => (
-  if (isNum x) then (
-    if (f x) then x else blame l)
-  else blame l
-) in
-let isEven = toCtr isEven_ in
-let isDivBy3 = toCtr isDivBy3_ in
-12 & Contract(#isEven) & Contract(#isDivBy3)
diff --git a/src/examples/mergeEnriched.ncl b/src/examples/mergeEnriched.ncl
deleted file mode 100644
index b0dab628..00000000
--- a/src/examples/mergeEnriched.ncl
+++ /dev/null
@@ -1,28 +0,0 @@
-let or : Bool -> Bool -> Bool = fun a b =>
-    if a then true else b in
-let and : Bool -> Bool -> Bool = fun a b =>
-    if a then (if b then true else false) else false in
-let oneOrTwo = fun l x =>
-    if and (isNum x)
-           (or (isZero (x + (-1))) (isZero (x + (-2)))) then
-        x
-    else blame l in
-let confDefault = {
-    pathLibc = Docstring("Installation path of the libc",
-               Default("/lib/x86_64-linux-gnu/libc.so"));
-    gcc = Docstring("Gcc command",
-          Default("gcc -Werror -03"));
-    specialFlag = Docstring("Set 1 for default, 2 for optimized",
-                  Default(1));
-} in
-let confValidation = {
-    pathLibc = Contract(Str);
-    gcc = Contract(Str);
-    specialFlag = Contract(#oneOrTwo);
-} in
-let confValAndDef = confValidation & confDefault in
-let myConf = {
-    gcc = "gcc -02";
-} in
-let result = myConf & confValAndDef in
-result.specialFlag
diff --git a/src/examples/primBoolWrong.ncl b/src/examples/primBoolWrong.ncl
deleted file mode 100644
index 759e302f..00000000
--- a/src/examples/primBoolWrong.ncl
+++ /dev/null
@@ -1,2 +0,0 @@
-let g  = fun x => if x then 0 else false in
-g ((fun x => 32) 0 )
\ No newline at end of file
diff --git a/src/examples/std.ncl b/src/examples/std.ncl
deleted file mode 100644
index 035ab1a6..00000000
--- a/src/examples/std.ncl
+++ /dev/null
@@ -1,4 +0,0 @@
-let safePlus = fun x y => ((x : Num) + (y : Num) : Num) in
-let const = fun x y => if isNum y then y else 2 in
-let safeAppTwice = fun f y => (f : Num -> Num) ((f : Bool -> Num) y) in
-safeAppTwice (const 3) true
diff --git a/src/examples/std2.ncl b/src/examples/std2.ncl
deleted file mode 100644
index 9183c25b..00000000
--- a/src/examples/std2.ncl
+++ /dev/null
@@ -1,7 +0,0 @@
-let const = fun x y => x in
-let safeAppTwice = fun f y => f (f y) in
-let ma = (safeAppTwice : (Dyn -> Num) -> Dyn -> Num)
-         ((const : Dyn -> Dyn -> Dyn) (1 : Bool))
-         (true : Bool)
-         in
-ma : Dyn
diff --git a/src/examples/trace.ncl b/src/examples/trace.ncl
deleted file mode 100644
index 5f56d96d..00000000
--- a/src/examples/trace.ncl
+++ /dev/null
@@ -1,2 +0,0 @@
-let map = Assume((Num -> Num) -> Num, fun funky => (funky 3) + (funky false)) in
-map (fun yak => yak + 1)
diff --git a/src/examples/trace2.ncl b/src/examples/trace2.ncl
deleted file mode 100644
index 9f0bdc76..00000000
--- a/src/examples/trace2.ncl
+++ /dev/null
@@ -1,5 +0,0 @@
-let foo = fun x => Assume(Num, x) + 3 in
-let id = fun x => x in
-let bar = fun b y => if b (id true) then foo y else -1 in
-let k = fun y x => Assume(Num, y) + (foo x) in
-bar (k (id 4)) false
diff --git a/src/examples/trace3.ncl b/src/examples/trace3.ncl
deleted file mode 100644
index 604914c3..00000000
--- a/src/examples/trace3.ncl
+++ /dev/null
@@ -1,5 +0,0 @@
-let foo = 3 in
-let bar = foo in
-let bleh = bar in
-let f = 4 5 in
-24


Test patch:
diff --git a/tests/examples.rs b/tests/examples.rs
new file mode 100644
index 00000000..b36a956d
--- /dev/null
+++ b/tests/examples.rs
@@ -0,0 +1,56 @@
+use assert_matches::assert_matches;
+use nickel::error::{Error, EvalError};
+use nickel::program::Program;
+use nickel::term::Term;
+use std::path::PathBuf;
+
+fn eval_file(file: &str) -> Result<Term, Error> {
+    let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
+    path.push(format!("examples/{}", file));
+    let mut p = Program::new_from_file(path).expect("could not load file as a program");
+    p.eval()
+}
+
+#[test]
+fn config_gcc() {
+    eval_file("config-gcc/config-gcc.ncl").unwrap();
+}
+
+#[test]
+fn fibonacci() {
+    eval_file("fibonacci/fibonacci.ncl").unwrap();
+}
+
+#[test]
+fn lists() {
+    eval_file("lists/lists.ncl").unwrap();
+}
+
+#[test]
+fn merge_main() {
+    eval_file("merge/main.ncl").unwrap();
+}
+
+#[test]
+fn polymorphism() {
+    eval_file("polymorphism/polymorphism.ncl").unwrap();
+}
+
+#[test]
+fn record_contract() {
+    eval_file("record-contract/record-contract.ncl").unwrap();
+}
+
+#[test]
+fn simple_contract_bool() {
+    eval_file("simple-contracts/simple-contract-bool.ncl").unwrap();
+}
+
+/// This example is expected to fail.
+#[test]
+fn simple_contract_div() {
+    assert_matches!(
+        eval_file("simple-contracts/simple-contract-div.ncl"),
+        Err(Error::EvalError(EvalError::BlameError(..)))
+    );
+}