From 3d498c134a9c0ab61c86a233edd0b2eb76ef44eb Mon Sep 17 00:00:00 2001 From: stuebinm Date: Tue, 20 Jul 2021 20:43:16 +0200 Subject: playing around with IO in Nix --- nix-turing/game.nix | 72 +++++++++++++++++++++++ nix-turing/io-example.nix | 41 +++++++++++++ nix-turing/io.nix | 147 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 260 insertions(+) create mode 100644 nix-turing/game.nix create mode 100644 nix-turing/io-example.nix create mode 100644 nix-turing/io.nix 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 {}).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 {}.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 {}; + + 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 {}; + 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)); +} -- cgit v1.2.3