summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorstuebinm2021-07-20 20:43:16 +0200
committerstuebinm2021-07-20 20:43:16 +0200
commit3d498c134a9c0ab61c86a233edd0b2eb76ef44eb (patch)
tree944d1735a932ecabe3d07a01b0dcb22d7b12adc8
parentb160fc2033cfa8a356098d962bab790ec273ec03 (diff)
playing around with IO in Nix
-rw-r--r--nix-turing/game.nix72
-rw-r--r--nix-turing/io-example.nix41
-rw-r--r--nix-turing/io.nix147
3 files changed, 260 insertions, 0 deletions
diff --git a/nix-turing/game.nix b/nix-turing/game.nix
new file mode 100644
index 0000000..742a7f7
--- /dev/null
+++ b/nix-turing/game.nix
@@ -0,0 +1,72 @@
+with (import <nixpkgs> {}).lib;
+
+let
+ # all possible winning rows
+ winning = [
+ ["tl" "tm" "tr"]
+ ["ml" "mm" "mr"]
+ ["bl" "bm" "br"]
+ ["tl" "ml" "bl"]
+ ["tm" "mm" "bm"]
+ ["tr" "mr" "br"]
+ ["tl" "mm" "br"]
+ ["bl" "mm" "tr"]
+ ];
+
+ # has this player won the game?
+ hasWon = player: grid:
+ any (row: all (pos: grid.${pos} == player) row)
+ winning;
+
+ showGrid = grid: with grid; ''
+ ===========GRID===========
+ t ${tl} | ${tm} | ${tr}
+ -----------
+ m ${ml} | ${mm} | ${mr}
+ -----------
+ b ${bl} | ${bm} | ${br}
+ l m r
+ '';
+
+ # the inital empty grid
+ emptyGrid = listToAttrs
+ (map (name: {inherit name; value = " ";})
+ ["tl" "tm" "tr" "ml" "mm" "mr" "bl" "bm" "br"]);
+
+ testGrid = emptyGrid // {
+ tl = "x";
+ tm = "x";
+ #tr = "x";
+ };
+
+ stripWhitespace = input: foldl
+ (a: b: if b == " " || b == "\n" || b == "\t" then a else a+b)
+ ""
+ (strings.stringToCharacters input);
+
+in {seed}:
+with import ./io.nix {s = seed;};
+
+do initialWorld [
+ # start with the empty grid
+ (v: assign "grid" emptyGrid)
+ (v: assign "turn" "x")
+ (v: print "Welcome to the game! \n${(showGrid v.grid)}")
+
+ # has someone won yet?
+ (while (v: !((hasWon "x" v.grid) || (hasWon "o" v.grid)))
+ (ifThenElse (v: v.turn == "x")
+ # ask player for next move
+ (v: doMonadic [
+ (v: read_input "Xs: your move, please")
+ (v: assign "grid"
+ (v.grid // { ${stripWhitespace v.input} = "x"; }))
+ (v: print (showGrid v.grid))
+ ])
+ # somehow come up with our own move?
+ (v: print "my move!")))
+
+ (ifThenElse (v: hasWon "x" v.grid)
+ (v: print "Congratulations, you won the game!")
+ (v: print "Yay, I won the game!"))
+]
diff --git a/nix-turing/io-example.nix b/nix-turing/io-example.nix
new file mode 100644
index 0000000..9692629
--- /dev/null
+++ b/nix-turing/io-example.nix
@@ -0,0 +1,41 @@
+with import <nixpkgs> {}.lib;
+
+let
+ # all possible winning rows
+ winning = [
+ ["tl" "tm" "tr"]
+ ["ml" "mm" "mr"]
+ ["bl" "bm" "br"]
+ ["tl" "ml" "bl"]
+ ["tm" "mm" "bm"]
+ ["tr" "mr" "br"]
+ ["tl" "mm" "br"]
+ ["bl" "mm" "tr"]
+ ];
+
+ showGrid = grid: with grid; ''
+ ===========GRID===========
+ ${tl} | ${tm} | ${tr}
+ ---------
+ ${ml} | ${mm} | ${mr}
+ ---------
+ ${bl} | ${bm} | ${br}
+ '';
+
+ # the inital empty grid
+ emptyGrid = listToAttrs
+ (map (name: {inherit name; value = " ";})
+ ["tl" "tm" "tr" "ml" "mm" "mr" "bl" "bm" "br"]);
+
+in {seed}:
+with import ./io.nix {s = seed;};
+
+do initialWorld [
+ (v: read_input "hello!")
+ (log)
+ (v: read_input "lalala second round!")
+ (log)
+ (ifThenElse (v: v == "condition\n")
+ (v: log "condition was met!")
+ (v: log "condition did not hold!"))
+]
diff --git a/nix-turing/io.nix b/nix-turing/io.nix
new file mode 100644
index 0000000..7f904d6
--- /dev/null
+++ b/nix-turing/io.nix
@@ -0,0 +1,147 @@
+#
+# General purpose programming Nix: Plan of attack
+# ===============================================
+#
+# 1. define some nice attrset that all these functions
+# pass to each other implicitely
+# 2. minimise unnecessary build output
+# 3. a monadic do notation taking a list of monad operations?
+# (how to deal with variables?)
+# 4. implement tic tac toe
+#
+# TODO
+# - nicer syntax
+# - put this into a library
+# - make the seed configurable via cli
+# - tic tac toe
+# let
+{ s, ... }: rec {
+ pkgs = import <nixpkgs> {};
+
+ seed = toString s;
+ eval = code: import (pkgs.writeText "code" code);
+
+ pause = idx: prompt: pkgs.stdenv.mkDerivation {
+ name = "sleep-${seed}";
+ src = pkgs.hello;
+ phases = [ "buildPhase" ];
+ buildPhase = ''
+ echo
+ echo
+ echo ${pkgs.lib.escapeShellArg prompt}
+ sleep 2
+ echo waiting 3 ...
+ sleep 2
+ echo waiting 2 ...
+ sleep 2
+ echo waiting 1 ...
+ mkdir -p $out
+ '';
+ };
+
+ pause_and = { prev, idx, inputCounter, ... }: prompt: code: {
+ prev = pkgs.stdenv.mkDerivation {
+ name = "pause_and-${toString idx}";
+ buildInputs = [ ( pause idx prompt ) ];
+ phases = [ "buildPhase" ];
+ src = pkgs.writeText "code" code;
+ buildPhase = ''
+ echo ${if prev != null then prev.outPath else ""}
+ # ${seed}
+ cp $src $out
+ '';
+ };
+ idx = idx;
+ inherit inputCounter;
+ };
+
+ read_input = prompt: realWorld:
+ let nextWorld = pause_and
+ realWorld
+ ''
+ ${prompt}
+ please enter your input in file /tmp/input-${toString realWorld.inputCounter}
+ ''
+ ''
+ with import <nixpkgs> {};
+ lib.readFile
+ "/tmp/input-${toString realWorld.inputCounter}"
+ # ${seed}
+ # ${if realWorld.prev != null
+ then realWorld.prev.outPath
+ else ""}
+ '';
+ in
+ {
+ prev = nextWorld.prev;
+ value = realWorld.value // {
+ input = import nextWorld.prev.outPath;
+ };
+ inputCounter = nextWorld.inputCounter + 1;
+ idx = nextWorld.idx + 1;
+ };
+
+ print = msg: realWorld: {
+ prev = pkgs.stdenv.mkDerivation {
+ name = "log-derivation";
+ phases = [ "print" ];
+ print = ''
+ echo ${pkgs.lib.escapeShellArg msg}
+ echo ${pkgs.lib.escapeShellArg msg} > $out
+ # ${toString realWorld.idx}
+ # ${seed}
+ # ${if realWorld.prev != null
+ then realWorld.prev.outPath
+ else ""}
+ '';
+ };
+ idx = realWorld.idx + 1;
+ inherit (realWorld) inputCounter value;
+ };
+
+ log = msg:
+ print "==============LOG===============\n${msg}";
+
+ initialWorld = {
+ prev = null;
+ idx = 0;
+ inputCounter = 0;
+ value = {};
+ };
+
+ call = realWorld:
+ realWorld.prev;
+
+ assign = name: value: realWorld:
+ realWorld // {value = realWorld.value // {${name} = value;};};
+
+ # the IO monad
+ bind = monadic: operation:
+ monadic // ((operation monadic.value) monadic);
+
+ bindMonadic = operation: monadic:
+ bind monadic operation;
+
+ unit = realWorld: realWorld;
+
+ # basic do notation
+ do = monadic: operations:
+ pkgs.lib.foldl bind monadic operations;
+
+ # flipped version of do for usage in bound
+ doMonadic = operations: monadic:
+ do monadic operations;
+
+ ifThenElse = cond: ifTrue: ifFalse:
+ (v: if cond v
+ then ifTrue v
+ else ifFalse v);
+
+ while = cond: body:
+ (ifThenElse cond
+ (v: doMonadic [
+ body
+ (while cond body)
+ ])
+ (v: unit));
+}