Skip to content

Instantly share code, notes, and snippets.

@tonymorris
Last active April 2, 2022 16:23

Revisions

  1. tonymorris revised this gist Sep 2, 2017. 1 changed file with 1 addition and 67 deletions.
    68 changes: 1 addition & 67 deletions PureIO.cs
    Original file line number Diff line number Diff line change
    @@ -242,80 +242,14 @@ public static Terminal<C> SelectMany<A, B, C>(this Terminal<A> t, Func<A, Termin
    }
    }

    public static class TerminalInterpreter {
    /*
    CAUTION: This function is unsafe.
    It is the "end of the (Terminal) world" interpreter.
    Use this function to run the final terminal program.
    Ideally, this function would be hypothetical and unavailable
    to the programmer API (i.e. implemented in its own runtime).
    */
    // $ Haskell $
    // interpret (Done a) = a
    // interpret (More (WriteOut s tt)) = hPutStrLn stdout s >> interpret tt
    // interpret (More (WriteErr s tt)) = hPutStrLn stderr s >> interpret tt
    // interpret (More (ReadLine f)) = readLine >>= \s -> interpret (f s)
    // interpret (More (Read f)) = readChar >>= \i -> interpret (f i)
    public static A Interpret<A>(this Terminal<A> t) {
    return t.Fold<A>(
    a => a
    , a => a.Fold<A>(
    (s, tt) => {
    Console.WriteLine(s);
    return Interpret(tt);
    }
    , (s, tt) => {
    Console.Error.WriteLine(s);
    return Interpret(tt);
    }
    , f => {
    var s = Console.ReadLine();
    return Interpret(f(s));
    }
    , f => {
    var i = Console.Read();
    return Interpret(f(i));
    }
    )
    );
    }
    }

    /*
    A data structure with only one possible value.
    It is similar to `void` but this can be used as a regular data type.
    */
    public struct Unit {
    public static readonly Unit Value = new Unit();
    }

    public class Previously {
    static void WriteOut(string s) { Console.WriteLine(s); }
    static void WriteOut() { WriteOut(""); }
    static void WriteErr(string s) { Console.Error.WriteLine(s); }
    static string ReadLine() { return Console.ReadLine(); }
    static int Read() { return Console.Read(); }

    public static int FormerlyMain() {
    WriteOut("Hello, let us begin");
    WriteOut("Please enter your name");
    var name = ReadLine();
    WriteOut("How old are you?");
    var age = ReadLine();
    WriteOut("Okey dokey, ready to tell the world?");
    WriteOut("0. No");
    WriteOut("1. Yes");
    var r = Read();
    WriteOut();
    if(r == '0')
    WriteErr(name + " is modest");
    else
    WriteOut(name + " is " + age + " years old");
    return r - 48;
    }
    }


    public class Demonstration {
    // $ Haskell $
    // do _1 <- WriteOut "Hello, let us begin"
  2. tonymorris revised this gist Jun 28, 2017. 1 changed file with 46 additions and 0 deletions.
    46 changes: 46 additions & 0 deletions PureIO.cs
    Original file line number Diff line number Diff line change
    @@ -15,6 +15,13 @@ It gives rise to a functor. See `Select` method.
    The Fold function deconstructs the data type into its one of 4 possibilities.
    The 4 static functions construct into one of the possibilities.
    */

    // $ Haskell $
    // data TerminalOperation a =
    // WriteOut string a
    // | WriteError string a
    // | ReadLine (string -> a)
    // | Read (int -> a)
    public abstract class TerminalOperation<A> {
    public abstract X Fold<X>(
    Func<string, A, X> writeOut
    @@ -102,6 +109,12 @@ Func<string, A, X> writeOut
    }
    }

    // $ Haskell $
    // instance Functor TerminalOperation where
    // fmap f (WriteOut s a) = WriteOut s (f a)
    // fmap f (WriteErr s a) = WriteErr s (f a)
    // fmap f (ReadLine g) = ReadLine (\s -> f (g s))
    // fmap f (Read g) = Read (\i -> f (g i))
    public static class TerminalOperationFunctor {
    public static TerminalOperation<B> Select<A, B>(this TerminalOperation<A> o, Func<A, B> f) {
    /*
    @@ -119,6 +132,10 @@ Note that `Terminal` uses only `Select`
    }
    }

    // $ Haskell $
    // data Terminal a =
    // Done a
    // | More (TerminalOperation (Terminal a))
    public abstract class Terminal<A> {
    public abstract X Fold<X>(
    Func<A, X> done
    @@ -190,6 +207,10 @@ public static Terminal<Unit> WriteOut() {
    }
    }

    // $ Haskell $
    // instance Functor Terminal where
    // fmap f (Done a) = Done (f a)
    // fmap f (More a) = More (fmap (\k -> fmap f k) a)
    public static class TerminalFunctor {
    public static Terminal<B> Select<A, B>(this Terminal<A> t, Func<A, B> f) {
    return t.Fold<Terminal<B>>(
    @@ -205,6 +226,10 @@ Note that `TerminalOperation#Select` is the only method that is specific to `Ter
    More to the point, some other structure with a `Select` method could be
    substituted here to give rise to a different kind of behaviour.
    */
    // $ Haskell $
    // instance Monad Terminal where
    // f >>= Done a = f a
    // f >>= More a = More (fmap (\k -> k >>= f) a)
    public static Terminal<B> SelectMany<A, B>(this Terminal<A> t, Func<A, Terminal<B>> f) {
    return t.Fold<Terminal<B>>(
    f
    @@ -226,6 +251,12 @@ Use this function to run the final terminal program.
    Ideally, this function would be hypothetical and unavailable
    to the programmer API (i.e. implemented in its own runtime).
    */
    // $ Haskell $
    // interpret (Done a) = a
    // interpret (More (WriteOut s tt)) = hPutStrLn stdout s >> interpret tt
    // interpret (More (WriteErr s tt)) = hPutStrLn stderr s >> interpret tt
    // interpret (More (ReadLine f)) = readLine >>= \s -> interpret (f s)
    // interpret (More (Read f)) = readChar >>= \i -> interpret (f i)
    public static A Interpret<A>(this Terminal<A> t) {
    return t.Fold<A>(
    a => a
    @@ -286,6 +317,21 @@ public static int FormerlyMain() {
    }

    public class Demonstration {
    // $ Haskell $
    // do _1 <- WriteOut "Hello, let us begin"
    // _2 <- WriteOut("Please enter your name")
    // _ <- ReadLine
    // _3 <- WriteOut("How old are you?")
    // _ <- ReadLine
    // _4 <- WriteOut("Okey dokey, ready to tell the world?")
    // _5 <- WriteOut("0. No")
    // _6 <- WriteOut("1. Yes")
    // _ <- Read
    // _7 <- WriteOut()
    // _8 <- if r == '0'
    // then WriteErr(name ++ " is modest")
    // else WriteOut(name ++ " is " ++ age ++ " years old")
    // pure (r - 48)
    public static int Main() {
    Terminal<int> Program =
    from _1 in Terminal.WriteOut("Hello, let us begin")
  3. tonymorris revised this gist Jun 29, 2016. 1 changed file with 274 additions and 96 deletions.
    370 changes: 274 additions & 96 deletions PureIO.cs
    Original file line number Diff line number Diff line change
    @@ -1,131 +1,309 @@
    import java.util.function.Function;
    import java.util.function.BiFunction;
    using System;

    abstract class TerminalOperation<A> {
    // this ensures that there are no subclasses of TerminalOperation
    // outside of this class
    private TerminalOperation() {
    namespace PureIO {
    /*
    C# does not have proper sum types. They must be emulated.
    This data type is one of 4 possible values:
    - WriteOut, being a pair of a string and A
    - WriteErr, being a pair of a string and A
    - readLine, being a function from string to A
    - read, being a function from int to A
    It gives rise to a functor. See `Select` method.
    The Fold function deconstructs the data type into its one of 4 possibilities.
    The 4 static functions construct into one of the possibilities.
    */
    public abstract class TerminalOperation<A> {
    public abstract X Fold<X>(
    Func<string, A, X> writeOut
    , Func<string, A, X> writeErr
    , Func<Func<string, A>, X> readLine
    , Func<Func<int, A>, X> read
    );

    public Terminal<A> Lift {
    get {
    return Terminal<A>.more(this.Select<A, Terminal<A>>(Terminal<A>.done));
    }
    }

    internal class WriteOut : TerminalOperation<A> {
    private readonly string s;
    private readonly A a;

    public WriteOut(string s, A a) {
    this.s = s;
    this.a = a;
    }

    public override X Fold<X>(
    Func<string, A, X> writeOut
    , Func<string, A, X> writeErr
    , Func<Func<string, A>, X> readLine
    , Func<Func<int, A>, X> read
    ) {
    return writeOut(s, a);
    }
    }

    internal class WriteErr : TerminalOperation<A> {
    private readonly string s;
    private readonly A a;

    public WriteErr(string s, A a) {
    this.s = s;
    this.a = a;
    }

    public override X Fold<X>(
    Func<string, A, X> writeOut
    , Func<string, A, X> writeErr
    , Func<Func<string, A>, X> readLine
    , Func<Func<int, A>, X> read
    ) {
    return writeErr(s, a);
    }
    }

    internal class ReadLine : TerminalOperation<A> {
    private Func<string, A> f;

    public ReadLine(Func<string, A> f) {
    this.f = f;
    }

    public override X Fold<X>(
    Func<string, A, X> writeOut
    , Func<string, A, X> writeErr
    , Func<Func<string, A>, X> readLine
    , Func<Func<int, A>, X> read
    ) {
    return readLine(f);
    }
    }

    internal class Read : TerminalOperation<A> {
    private readonly Func<int, A> f;

    public Read(Func<int, A> f) {
    this.f = f;
    }

    public override X Fold<X>(
    Func<string, A, X> writeOut
    , Func<string, A, X> writeErr
    , Func<Func<string, A>, X> readLine
    , Func<Func<int, A>, X> read
    ) {
    return read(f);
    }
    }
    }

    // There are exactly four subclasses of TerminalOperation:
    // * WriteOut
    // * WriteErr
    // * ReadLine
    // * Read

    // Pattern-match the four cases.
    public abstract <X> X fold(
    BiFunction<String, A, X> writeOut
    , BiFunction<String, A, X> writeErr
    , Function<Function<String, A>, X> readLine
    , Function<Function<Integer, A>, X> read
    );

    // WriteOut<A> is a pair of String and A
    public final static class WriteOut<A> extends TerminalOperation<A> {
    public final String str;
    public final A value;

    public WriteOut(final String str, final A value) {
    this.str = str;
    this.value = value;
    }

    public <X> X fold(
    final BiFunction<String, A, X> writeOut
    , final BiFunction<String, A, X> writeErr
    , final Function<Function<String, A>, X> readLine
    , final Function<Function<Integer, A>, X> read
    ) {
    return writeOut.apply(str, value);
    public static class TerminalOperationFunctor {
    public static TerminalOperation<B> Select<A, B>(this TerminalOperation<A> o, Func<A, B> f) {
    /*
    The `TerminalOperation` data type is a functor.
    This is all that is necessary to provide the grammar (`Terminal`).
    Note that `Terminal` uses only `Select`
    (and no other `TerminalOperation` methods) to method to implement `SelectMany`
    */
    return o.Fold<TerminalOperation<B>>(
    (s, a) => new TerminalOperation<B>.WriteOut(s, f(a))
    , (s, a) => new TerminalOperation<B>.WriteErr(s, f(a))
    , g => new TerminalOperation<B>.ReadLine(s => f(g(s)))
    , g => new TerminalOperation<B>.Read(i => f(g(i)))
    );
    }
    }

    // WriteErr<A> is a pair of String and A
    public final static class WriteErr<A> extends TerminalOperation<A> {
    public final String str;
    public final A value;
    public abstract class Terminal<A> {
    public abstract X Fold<X>(
    Func<A, X> done
    , Func<TerminalOperation<Terminal<A>>, X> more
    );

    internal class Done : Terminal<A> {
    public readonly A a;

    public WriteErr(final String str, final A value) {
    this.str = str;
    this.value = value;
    public Done(A a) {
    this.a = a;
    }

    override public X Fold<X>(
    Func<A, X> done
    , Func<TerminalOperation<Terminal<A>>, X> more
    ) {
    return done(a);
    }
    }

    public <X> X fold(
    final BiFunction<String, A, X> writeOut
    , final BiFunction<String, A, X> writeErr
    , final Function<Function<String, A>, X> readLine
    , final Function<Function<Integer, A>, X> read
    ) {
    return writeErr.apply(str, value);
    public static Terminal<A> done(A a) {
    return new Done(a);
    }
    }

    // ReadLine<A> is a function of String to A
    public final static class ReadLine<A> extends TerminalOperation<A> {
    public final Function<String, A> func;
    internal class More : Terminal<A> {
    public readonly TerminalOperation<Terminal<A>> a;

    public More(TerminalOperation<Terminal<A>> a) {
    this.a = a;
    }

    public ReadLine(final Function<String, A> func) {
    this.func = func;
    override public X Fold<X>(
    Func<A, X> done
    , Func<TerminalOperation<Terminal<A>>, X> more
    ) {
    return more(a);
    }
    }

    public <X> X fold(
    final BiFunction<String, A, X> writeOut
    , final BiFunction<String, A, X> writeErr
    , final Function<Function<String, A>, X> readLine
    , final Function<Function<Integer, A>, X> read
    ) {
    return readLine.apply(func);
    public static Terminal<A> more(TerminalOperation<Terminal<A>> a) {
    return new More(a);
    }
    }

    // Read<A> is a function of Int to A
    public final static class Read<A> extends TerminalOperation<A> {
    public final Function<Integer, A> func;
    public static class Terminal {
    public static Terminal<Unit> WriteOut(string s) {
    return new TerminalOperation<Unit>.WriteOut(s, Unit.Value).Lift;
    }

    public static Terminal<Unit> WriteErr(string s) {
    return new TerminalOperation<Unit>.WriteErr(s, Unit.Value).Lift;
    }

    public static Terminal<string> ReadLine {
    get {
    return new TerminalOperation<string>.ReadLine(s => s).Lift;
    }
    }

    public Read(final Function<Integer, A> func) {
    this.func = func;
    public static Terminal<int> Read {
    get {
    return new TerminalOperation<int>.Read(i => i).Lift;
    }
    }

    public <X> X fold(
    final BiFunction<String, A, X> writeOut
    , final BiFunction<String, A, X> writeErr
    , final Function<Function<String, A>, X> readLine
    , final Function<Function<Integer, A>, X> read
    ) {
    return read.apply(func);
    public static Terminal<Unit> WriteOut() {
    return WriteOut("");
    }
    }

    // TerminalOperation is a functor (can be mapped).
    public static class TerminalFunctor {
    public static Terminal<B> Select<A, B>(this Terminal<A> t, Func<A, B> f) {
    return t.Fold<Terminal<B>>(
    a => Terminal<B>.done(f(a))
    , a => Terminal<B>.more(a.Select(k => k.Select(f)))
    );
    }

    /*
    The monad for Terminal.
    public final <B> TerminalOperation<B> map(final Function<A, B> f) {
    return fold(
    (s, a) -> new WriteOut<B>(s, f.apply(a))
    , (s, a) -> new WriteErr<B>(s, f.apply(a))
    , k -> new ReadLine<B>(f.compose(k))
    , k -> new Read<B>(f.compose(k))
    );
    Note that `TerminalOperation#Select` is the only method that is specific to `TerminalOperation`.
    More to the point, some other structure with a `Select` method could be
    substituted here to give rise to a different kind of behaviour.
    */
    public static Terminal<B> SelectMany<A, B>(this Terminal<A> t, Func<A, Terminal<B>> f) {
    return t.Fold<Terminal<B>>(
    f
    , a => Terminal<B>.more(a.Select(k => k.SelectMany(f)))
    );
    }

    public static Terminal<C> SelectMany<A, B, C>(this Terminal<A> t, Func<A, Terminal<B>> u, Func<A, B, C> f) {
    return SelectMany(t, a => Select(u(a), b => f(a, b)));
    }
    }
    }

    abstract class Terminal<A> {
    // this ensures that there are no subclasses of Terminal
    // outside of this class
    private Terminal() {
    public static class TerminalInterpreter {
    /*
    CAUTION: This function is unsafe.
    It is the "end of the (Terminal) world" interpreter.
    Use this function to run the final terminal program.
    Ideally, this function would be hypothetical and unavailable
    to the programmer API (i.e. implemented in its own runtime).
    */
    public static A Interpret<A>(this Terminal<A> t) {
    return t.Fold<A>(
    a => a
    , a => a.Fold<A>(
    (s, tt) => {
    Console.WriteLine(s);
    return Interpret(tt);
    }
    , (s, tt) => {
    Console.Error.WriteLine(s);
    return Interpret(tt);
    }
    , f => {
    var s = Console.ReadLine();
    return Interpret(f(s));
    }
    , f => {
    var i = Console.Read();
    return Interpret(f(i));
    }
    )
    );
    }
    }

    /*
    A data structure with only one possible value.
    It is similar to `void` but this can be used as a regular data type.
    */
    public struct Unit {
    public static readonly Unit Value = new Unit();
    }

    // There are exactly two subclasses of Terminal:
    // * Done
    // * More
    public class Previously {
    static void WriteOut(string s) { Console.WriteLine(s); }
    static void WriteOut() { WriteOut(""); }
    static void WriteErr(string s) { Console.Error.WriteLine(s); }
    static string ReadLine() { return Console.ReadLine(); }
    static int Read() { return Console.Read(); }

    public static int FormerlyMain() {
    WriteOut("Hello, let us begin");
    WriteOut("Please enter your name");
    var name = ReadLine();
    WriteOut("How old are you?");
    var age = ReadLine();
    WriteOut("Okey dokey, ready to tell the world?");
    WriteOut("0. No");
    WriteOut("1. Yes");
    var r = Read();
    WriteOut();
    if(r == '0')
    WriteErr(name + " is modest");
    else
    WriteOut(name + " is " + age + " years old");
    return r - 48;
    }
    }

    public final static class Done<A> extends Terminal<A> {
    public final A value;
    public class Demonstration {
    public static int Main() {
    Terminal<int> Program =
    from _1 in Terminal.WriteOut("Hello, let us begin")
    from _2 in Terminal.WriteOut("Please enter your name")
    from name in Terminal.ReadLine
    from _3 in Terminal.WriteOut("How old are you?")
    from age in Terminal.ReadLine
    from _4 in Terminal.WriteOut("Okey dokey, ready to tell the world?")
    from _5 in Terminal.WriteOut("0. No")
    from _6 in Terminal.WriteOut("1. Yes")
    from r in Terminal.Read
    from _7 in Terminal.WriteOut()
    from _8 in r == '0' ?
    Terminal.WriteErr(name + " is modest") :
    Terminal.WriteOut(name + " is " + age + " years old")
    select r - 48;

    public Done(final A value) {
    this.value = value;
    return Program.Interpret();
    }
    }

  4. tonymorris revised this gist Jun 29, 2016. 1 changed file with 96 additions and 274 deletions.
    370 changes: 96 additions & 274 deletions PureIO.cs
    Original file line number Diff line number Diff line change
    @@ -1,309 +1,131 @@
    using System;
    import java.util.function.Function;
    import java.util.function.BiFunction;

    namespace PureIO {
    /*
    C# does not have proper sum types. They must be emulated.
    abstract class TerminalOperation<A> {
    // this ensures that there are no subclasses of TerminalOperation
    // outside of this class
    private TerminalOperation() {

    This data type is one of 4 possible values:
    - WriteOut, being a pair of a string and A
    - WriteErr, being a pair of a string and A
    - readLine, being a function from string to A
    - read, being a function from int to A
    It gives rise to a functor. See `Select` method.
    The Fold function deconstructs the data type into its one of 4 possibilities.
    The 4 static functions construct into one of the possibilities.
    */
    public abstract class TerminalOperation<A> {
    public abstract X Fold<X>(
    Func<string, A, X> writeOut
    , Func<string, A, X> writeErr
    , Func<Func<string, A>, X> readLine
    , Func<Func<int, A>, X> read
    );

    public Terminal<A> Lift {
    get {
    return Terminal<A>.more(this.Select<A, Terminal<A>>(Terminal<A>.done));
    }
    }

    internal class WriteOut : TerminalOperation<A> {
    private readonly string s;
    private readonly A a;

    public WriteOut(string s, A a) {
    this.s = s;
    this.a = a;
    }

    public override X Fold<X>(
    Func<string, A, X> writeOut
    , Func<string, A, X> writeErr
    , Func<Func<string, A>, X> readLine
    , Func<Func<int, A>, X> read
    ) {
    return writeOut(s, a);
    }
    }

    internal class WriteErr : TerminalOperation<A> {
    private readonly string s;
    private readonly A a;

    public WriteErr(string s, A a) {
    this.s = s;
    this.a = a;
    }

    public override X Fold<X>(
    Func<string, A, X> writeOut
    , Func<string, A, X> writeErr
    , Func<Func<string, A>, X> readLine
    , Func<Func<int, A>, X> read
    ) {
    return writeErr(s, a);
    }
    }

    internal class ReadLine : TerminalOperation<A> {
    private Func<string, A> f;

    public ReadLine(Func<string, A> f) {
    this.f = f;
    }

    public override X Fold<X>(
    Func<string, A, X> writeOut
    , Func<string, A, X> writeErr
    , Func<Func<string, A>, X> readLine
    , Func<Func<int, A>, X> read
    ) {
    return readLine(f);
    }
    }

    internal class Read : TerminalOperation<A> {
    private readonly Func<int, A> f;

    public Read(Func<int, A> f) {
    this.f = f;
    }

    public override X Fold<X>(
    Func<string, A, X> writeOut
    , Func<string, A, X> writeErr
    , Func<Func<string, A>, X> readLine
    , Func<Func<int, A>, X> read
    ) {
    return read(f);
    }
    }
    }

    public static class TerminalOperationFunctor {
    public static TerminalOperation<B> Select<A, B>(this TerminalOperation<A> o, Func<A, B> f) {
    /*
    The `TerminalOperation` data type is a functor.
    This is all that is necessary to provide the grammar (`Terminal`).
    Note that `Terminal` uses only `Select`
    (and no other `TerminalOperation` methods) to method to implement `SelectMany`
    */
    return o.Fold<TerminalOperation<B>>(
    (s, a) => new TerminalOperation<B>.WriteOut(s, f(a))
    , (s, a) => new TerminalOperation<B>.WriteErr(s, f(a))
    , g => new TerminalOperation<B>.ReadLine(s => f(g(s)))
    , g => new TerminalOperation<B>.Read(i => f(g(i)))
    );
    // There are exactly four subclasses of TerminalOperation:
    // * WriteOut
    // * WriteErr
    // * ReadLine
    // * Read

    // Pattern-match the four cases.
    public abstract <X> X fold(
    BiFunction<String, A, X> writeOut
    , BiFunction<String, A, X> writeErr
    , Function<Function<String, A>, X> readLine
    , Function<Function<Integer, A>, X> read
    );

    // WriteOut<A> is a pair of String and A
    public final static class WriteOut<A> extends TerminalOperation<A> {
    public final String str;
    public final A value;

    public WriteOut(final String str, final A value) {
    this.str = str;
    this.value = value;
    }

    public <X> X fold(
    final BiFunction<String, A, X> writeOut
    , final BiFunction<String, A, X> writeErr
    , final Function<Function<String, A>, X> readLine
    , final Function<Function<Integer, A>, X> read
    ) {
    return writeOut.apply(str, value);
    }
    }

    public abstract class Terminal<A> {
    public abstract X Fold<X>(
    Func<A, X> done
    , Func<TerminalOperation<Terminal<A>>, X> more
    );

    internal class Done : Terminal<A> {
    public readonly A a;
    // WriteErr<A> is a pair of String and A
    public final static class WriteErr<A> extends TerminalOperation<A> {
    public final String str;
    public final A value;

    public Done(A a) {
    this.a = a;
    }

    override public X Fold<X>(
    Func<A, X> done
    , Func<TerminalOperation<Terminal<A>>, X> more
    ) {
    return done(a);
    }
    }

    public static Terminal<A> done(A a) {
    return new Done(a);
    }

    internal class More : Terminal<A> {
    public readonly TerminalOperation<Terminal<A>> a;

    public More(TerminalOperation<Terminal<A>> a) {
    this.a = a;
    }

    override public X Fold<X>(
    Func<A, X> done
    , Func<TerminalOperation<Terminal<A>>, X> more
    ) {
    return more(a);
    }
    public WriteErr(final String str, final A value) {
    this.str = str;
    this.value = value;
    }

    public static Terminal<A> more(TerminalOperation<Terminal<A>> a) {
    return new More(a);
    public <X> X fold(
    final BiFunction<String, A, X> writeOut
    , final BiFunction<String, A, X> writeErr
    , final Function<Function<String, A>, X> readLine
    , final Function<Function<Integer, A>, X> read
    ) {
    return writeErr.apply(str, value);
    }
    }

    public static class Terminal {
    public static Terminal<Unit> WriteOut(string s) {
    return new TerminalOperation<Unit>.WriteOut(s, Unit.Value).Lift;
    }

    public static Terminal<Unit> WriteErr(string s) {
    return new TerminalOperation<Unit>.WriteErr(s, Unit.Value).Lift;
    }

    public static Terminal<string> ReadLine {
    get {
    return new TerminalOperation<string>.ReadLine(s => s).Lift;
    }
    }
    // ReadLine<A> is a function of String to A
    public final static class ReadLine<A> extends TerminalOperation<A> {
    public final Function<String, A> func;

    public static Terminal<int> Read {
    get {
    return new TerminalOperation<int>.Read(i => i).Lift;
    }
    public ReadLine(final Function<String, A> func) {
    this.func = func;
    }

    public static Terminal<Unit> WriteOut() {
    return WriteOut("");
    public <X> X fold(
    final BiFunction<String, A, X> writeOut
    , final BiFunction<String, A, X> writeErr
    , final Function<Function<String, A>, X> readLine
    , final Function<Function<Integer, A>, X> read
    ) {
    return readLine.apply(func);
    }
    }

    public static class TerminalFunctor {
    public static Terminal<B> Select<A, B>(this Terminal<A> t, Func<A, B> f) {
    return t.Fold<Terminal<B>>(
    a => Terminal<B>.done(f(a))
    , a => Terminal<B>.more(a.Select(k => k.Select(f)))
    );
    }
    // Read<A> is a function of Int to A
    public final static class Read<A> extends TerminalOperation<A> {
    public final Function<Integer, A> func;

    /*
    The monad for Terminal.
    Note that `TerminalOperation#Select` is the only method that is specific to `TerminalOperation`.
    More to the point, some other structure with a `Select` method could be
    substituted here to give rise to a different kind of behaviour.
    */
    public static Terminal<B> SelectMany<A, B>(this Terminal<A> t, Func<A, Terminal<B>> f) {
    return t.Fold<Terminal<B>>(
    f
    , a => Terminal<B>.more(a.Select(k => k.SelectMany(f)))
    );
    public Read(final Function<Integer, A> func) {
    this.func = func;
    }

    public static Terminal<C> SelectMany<A, B, C>(this Terminal<A> t, Func<A, Terminal<B>> u, Func<A, B, C> f) {
    return SelectMany(t, a => Select(u(a), b => f(a, b)));
    public <X> X fold(
    final BiFunction<String, A, X> writeOut
    , final BiFunction<String, A, X> writeErr
    , final Function<Function<String, A>, X> readLine
    , final Function<Function<Integer, A>, X> read
    ) {
    return read.apply(func);
    }
    }

    public static class TerminalInterpreter {
    /*
    CAUTION: This function is unsafe.
    It is the "end of the (Terminal) world" interpreter.
    Use this function to run the final terminal program.
    Ideally, this function would be hypothetical and unavailable
    to the programmer API (i.e. implemented in its own runtime).
    */
    public static A Interpret<A>(this Terminal<A> t) {
    return t.Fold<A>(
    a => a
    , a => a.Fold<A>(
    (s, tt) => {
    Console.WriteLine(s);
    return Interpret(tt);
    }
    , (s, tt) => {
    Console.Error.WriteLine(s);
    return Interpret(tt);
    }
    , f => {
    var s = Console.ReadLine();
    return Interpret(f(s));
    }
    , f => {
    var i = Console.Read();
    return Interpret(f(i));
    }
    )
    );
    }
    }
    // TerminalOperation is a functor (can be mapped).

    /*
    A data structure with only one possible value.
    It is similar to `void` but this can be used as a regular data type.
    */
    public struct Unit {
    public static readonly Unit Value = new Unit();
    public final <B> TerminalOperation<B> map(final Function<A, B> f) {
    return fold(
    (s, a) -> new WriteOut<B>(s, f.apply(a))
    , (s, a) -> new WriteErr<B>(s, f.apply(a))
    , k -> new ReadLine<B>(f.compose(k))
    , k -> new Read<B>(f.compose(k))
    );
    }
    }

    public class Previously {
    static void WriteOut(string s) { Console.WriteLine(s); }
    static void WriteOut() { WriteOut(""); }
    static void WriteErr(string s) { Console.Error.WriteLine(s); }
    static string ReadLine() { return Console.ReadLine(); }
    static int Read() { return Console.Read(); }
    abstract class Terminal<A> {
    // this ensures that there are no subclasses of Terminal
    // outside of this class
    private Terminal() {

    public static int FormerlyMain() {
    WriteOut("Hello, let us begin");
    WriteOut("Please enter your name");
    var name = ReadLine();
    WriteOut("How old are you?");
    var age = ReadLine();
    WriteOut("Okey dokey, ready to tell the world?");
    WriteOut("0. No");
    WriteOut("1. Yes");
    var r = Read();
    WriteOut();
    if(r == '0')
    WriteErr(name + " is modest");
    else
    WriteOut(name + " is " + age + " years old");
    return r - 48;
    }
    }

    public class Demonstration {
    public static int Main() {
    Terminal<int> Program =
    from _1 in Terminal.WriteOut("Hello, let us begin")
    from _2 in Terminal.WriteOut("Please enter your name")
    from name in Terminal.ReadLine
    from _3 in Terminal.WriteOut("How old are you?")
    from age in Terminal.ReadLine
    from _4 in Terminal.WriteOut("Okey dokey, ready to tell the world?")
    from _5 in Terminal.WriteOut("0. No")
    from _6 in Terminal.WriteOut("1. Yes")
    from r in Terminal.Read
    from _7 in Terminal.WriteOut()
    from _8 in r == '0' ?
    Terminal.WriteErr(name + " is modest") :
    Terminal.WriteOut(name + " is " + age + " years old")
    select r - 48;
    // There are exactly two subclasses of Terminal:
    // * Done
    // * More

    public final static class Done<A> extends Terminal<A> {
    public final A value;

    return Program.Interpret();
    public Done(final A value) {
    this.value = value;
    }
    }

  5. tonymorris revised this gist Dec 6, 2013. 1 changed file with 43 additions and 16 deletions.
    59 changes: 43 additions & 16 deletions PureIO.cs
    Original file line number Diff line number Diff line change
    @@ -259,25 +259,52 @@ public struct Unit {
    public static readonly Unit Value = new Unit();
    }

    public class Demonstration {
    static Terminal<int> Program =
    from _1 in Terminal.WriteOut("Hello, let us begin")
    from _2 in Terminal.WriteOut("Please enter your name")
    from name in Terminal.ReadLine
    from _3 in Terminal.WriteOut ("How old are you?")
    from age in Terminal.ReadLine
    from _4 in Terminal.WriteOut("Okey dokey, ready to tell the world?")
    from _5 in Terminal.WriteOut("0. No")
    from _6 in Terminal.WriteOut("1. Yes")
    from r in Terminal.Read
    from _7 in Terminal.WriteOut()
    from _8 in r == '0' ?
    Terminal.WriteErr(name + " is modest") :
    Terminal.WriteOut(name + " is " + age + " years old")
    select r;
    public class Previously {
    static void WriteOut(string s) { Console.WriteLine(s); }
    static void WriteOut() { WriteOut(""); }
    static void WriteErr(string s) { Console.Error.WriteLine(s); }
    static string ReadLine() { return Console.ReadLine(); }
    static int Read() { return Console.Read(); }

    public static int FormerlyMain() {
    WriteOut("Hello, let us begin");
    WriteOut("Please enter your name");
    var name = ReadLine();
    WriteOut("How old are you?");
    var age = ReadLine();
    WriteOut("Okey dokey, ready to tell the world?");
    WriteOut("0. No");
    WriteOut("1. Yes");
    var r = Read();
    WriteOut();
    if(r == '0')
    WriteErr(name + " is modest");
    else
    WriteOut(name + " is " + age + " years old");
    return r - 48;
    }
    }

    public class Demonstration {
    public static int Main() {
    Terminal<int> Program =
    from _1 in Terminal.WriteOut("Hello, let us begin")
    from _2 in Terminal.WriteOut("Please enter your name")
    from name in Terminal.ReadLine
    from _3 in Terminal.WriteOut("How old are you?")
    from age in Terminal.ReadLine
    from _4 in Terminal.WriteOut("Okey dokey, ready to tell the world?")
    from _5 in Terminal.WriteOut("0. No")
    from _6 in Terminal.WriteOut("1. Yes")
    from r in Terminal.Read
    from _7 in Terminal.WriteOut()
    from _8 in r == '0' ?
    Terminal.WriteErr(name + " is modest") :
    Terminal.WriteOut(name + " is " + age + " years old")
    select r - 48;

    return Program.Interpret();
    }
    }

    }
  6. tonymorris revised this gist Dec 6, 2013. 1 changed file with 120 additions and 54 deletions.
    174 changes: 120 additions & 54 deletions PureIO.cs
    Original file line number Diff line number Diff line change
    @@ -10,6 +10,8 @@ namespace PureIO {
    - readLine, being a function from string to A
    - read, being a function from int to A
    It gives rise to a functor. See `Select` method.
    The Fold function deconstructs the data type into its one of 4 possibilities.
    The 4 static functions construct into one of the possibilities.
    */
    @@ -21,23 +23,15 @@ Func<string, A, X> writeOut
    , Func<Func<int, A>, X> read
    );

    /*
    This data type is a functor.
    This is all that is necessary to provide the grammar (Terminal).
    Note that Terminal uses only this `Select` method to implement `SelectMany`
    */
    public TerminalOperation<B> Select<B>(Func<A, B> f) {
    return Fold<TerminalOperation<B>>(
    (s, a) => new TerminalOperation<B>.WriteOut(s, f(a))
    , (s, a) => new TerminalOperation<B>.WriteErr(s, f(a))
    , g => new TerminalOperation<B>.ReadLine(s => f(g(s)))
    , g => new TerminalOperation<B>.Read(i => f(g(i)))
    );
    public Terminal<A> Lift {
    get {
    return Terminal<A>.more(this.Select<A, Terminal<A>>(Terminal<A>.done));
    }
    }

    private class WriteOut : TerminalOperation<A> {
    private string s;
    private A a;
    internal class WriteOut : TerminalOperation<A> {
    private readonly string s;
    private readonly A a;

    public WriteOut(string s, A a) {
    this.s = s;
    @@ -54,13 +48,9 @@ Func<string, A, X> writeOut
    }
    }

    public static TerminalOperation<A> writeOut(string s, A a) {
    return new WriteOut(s, a);
    }

    private class WriteErr : TerminalOperation<A> {
    private string s;
    private A a;
    internal class WriteErr : TerminalOperation<A> {
    private readonly string s;
    private readonly A a;

    public WriteErr(string s, A a) {
    this.s = s;
    @@ -77,11 +67,7 @@ Func<string, A, X> writeOut
    }
    }

    public static TerminalOperation<A> writeErr(string s, A a) {
    return new WriteErr(s, a);
    }

    private class ReadLine : TerminalOperation<A> {
    internal class ReadLine : TerminalOperation<A> {
    private Func<string, A> f;

    public ReadLine(Func<string, A> f) {
    @@ -98,12 +84,8 @@ Func<string, A, X> writeOut
    }
    }

    public static TerminalOperation<A> readLine(Func<string, A> f) {
    return new ReadLine(f);
    }

    private class Read : TerminalOperation<A> {
    private Func<int, A> f;
    internal class Read : TerminalOperation<A> {
    private readonly Func<int, A> f;

    public Read(Func<int, A> f) {
    this.f = f;
    @@ -118,9 +100,22 @@ Func<string, A, X> writeOut
    return read(f);
    }
    }
    }

    public static TerminalOperation<A> read(Func<int, A> f) {
    return new Read(f);
    public static class TerminalOperationFunctor {
    public static TerminalOperation<B> Select<A, B>(this TerminalOperation<A> o, Func<A, B> f) {
    /*
    The `TerminalOperation` data type is a functor.
    This is all that is necessary to provide the grammar (`Terminal`).
    Note that `Terminal` uses only `Select`
    (and no other `TerminalOperation` methods) to method to implement `SelectMany`
    */
    return o.Fold<TerminalOperation<B>>(
    (s, a) => new TerminalOperation<B>.WriteOut(s, f(a))
    , (s, a) => new TerminalOperation<B>.WriteErr(s, f(a))
    , g => new TerminalOperation<B>.ReadLine(s => f(g(s)))
    , g => new TerminalOperation<B>.Read(i => f(g(i)))
    );
    }
    }

    @@ -130,22 +125,8 @@ Func<A, X> done
    , Func<TerminalOperation<Terminal<A>>, X> more
    );

    public Terminal<B> Select<B>(Func<A, B> f) {
    return Fold<Terminal<B>>(
    a => Terminal<B>.done(f(a))
    , a => Terminal<B>.more(a.Select(t => t.Select(f)))
    );
    }

    public Terminal<B> SelectMany<B>(Func<A, Terminal<B>> f) {
    return Fold<Terminal<B>>(
    f
    , a => Terminal<B>.more(a.Select(t => t.SelectMany(f)))
    );
    }

    private class Done : Terminal<A> {
    public A a;
    internal class Done : Terminal<A> {
    public readonly A a;

    public Done(A a) {
    this.a = a;
    @@ -163,8 +144,8 @@ public static Terminal<A> done(A a) {
    return new Done(a);
    }

    private class More : Terminal<A> {
    public TerminalOperation<Terminal<A>> a;
    internal class More : Terminal<A> {
    public readonly TerminalOperation<Terminal<A>> a;

    public More(TerminalOperation<Terminal<A>> a) {
    this.a = a;
    @@ -181,7 +162,62 @@ Func<A, X> done
    public static Terminal<A> more(TerminalOperation<Terminal<A>> a) {
    return new More(a);
    }
    }

    public static class Terminal {
    public static Terminal<Unit> WriteOut(string s) {
    return new TerminalOperation<Unit>.WriteOut(s, Unit.Value).Lift;
    }

    public static Terminal<Unit> WriteErr(string s) {
    return new TerminalOperation<Unit>.WriteErr(s, Unit.Value).Lift;
    }

    public static Terminal<string> ReadLine {
    get {
    return new TerminalOperation<string>.ReadLine(s => s).Lift;
    }
    }

    public static Terminal<int> Read {
    get {
    return new TerminalOperation<int>.Read(i => i).Lift;
    }
    }

    public static Terminal<Unit> WriteOut() {
    return WriteOut("");
    }
    }

    public static class TerminalFunctor {
    public static Terminal<B> Select<A, B>(this Terminal<A> t, Func<A, B> f) {
    return t.Fold<Terminal<B>>(
    a => Terminal<B>.done(f(a))
    , a => Terminal<B>.more(a.Select(k => k.Select(f)))
    );
    }

    /*
    The monad for Terminal.
    Note that `TerminalOperation#Select` is the only method that is specific to `TerminalOperation`.
    More to the point, some other structure with a `Select` method could be
    substituted here to give rise to a different kind of behaviour.
    */
    public static Terminal<B> SelectMany<A, B>(this Terminal<A> t, Func<A, Terminal<B>> f) {
    return t.Fold<Terminal<B>>(
    f
    , a => Terminal<B>.more(a.Select(k => k.SelectMany(f)))
    );
    }

    public static Terminal<C> SelectMany<A, B, C>(this Terminal<A> t, Func<A, Terminal<B>> u, Func<A, B, C> f) {
    return SelectMany(t, a => Select(u(a), b => f(a, b)));
    }
    }

    public static class TerminalInterpreter {
    /*
    CAUTION: This function is unsafe.
    It is the "end of the (Terminal) world" interpreter.
    @@ -190,7 +226,7 @@ Use this function to run the final terminal program.
    Ideally, this function would be hypothetical and unavailable
    to the programmer API (i.e. implemented in its own runtime).
    */
    public static A Interpret(Terminal<A> t) {
    public static A Interpret<A>(this Terminal<A> t) {
    return t.Fold<A>(
    a => a
    , a => a.Fold<A>(
    @@ -214,4 +250,34 @@ public static A Interpret(Terminal<A> t) {
    );
    }
    }

    /*
    A data structure with only one possible value.
    It is similar to `void` but this can be used as a regular data type.
    */
    public struct Unit {
    public static readonly Unit Value = new Unit();
    }

    public class Demonstration {
    static Terminal<int> Program =
    from _1 in Terminal.WriteOut("Hello, let us begin")
    from _2 in Terminal.WriteOut("Please enter your name")
    from name in Terminal.ReadLine
    from _3 in Terminal.WriteOut ("How old are you?")
    from age in Terminal.ReadLine
    from _4 in Terminal.WriteOut("Okey dokey, ready to tell the world?")
    from _5 in Terminal.WriteOut("0. No")
    from _6 in Terminal.WriteOut("1. Yes")
    from r in Terminal.Read
    from _7 in Terminal.WriteOut()
    from _8 in r == '0' ?
    Terminal.WriteErr(name + " is modest") :
    Terminal.WriteOut(name + " is " + age + " years old")
    select r;

    public static int Main() {
    return Program.Interpret();
    }
    }
    }
  7. tonymorris renamed this gist Dec 6, 2013. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  8. tonymorris created this gist Dec 6, 2013.
    217 changes: 217 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,217 @@
    using System;

    namespace PureIO {
    /*
    C# does not have proper sum types. They must be emulated.

    This data type is one of 4 possible values:
    - WriteOut, being a pair of a string and A
    - WriteErr, being a pair of a string and A
    - readLine, being a function from string to A
    - read, being a function from int to A

    The Fold function deconstructs the data type into its one of 4 possibilities.
    The 4 static functions construct into one of the possibilities.
    */
    public abstract class TerminalOperation<A> {
    public abstract X Fold<X>(
    Func<string, A, X> writeOut
    , Func<string, A, X> writeErr
    , Func<Func<string, A>, X> readLine
    , Func<Func<int, A>, X> read
    );

    /*
    This data type is a functor.
    This is all that is necessary to provide the grammar (Terminal).
    Note that Terminal uses only this `Select` method to implement `SelectMany`
    */
    public TerminalOperation<B> Select<B>(Func<A, B> f) {
    return Fold<TerminalOperation<B>>(
    (s, a) => new TerminalOperation<B>.WriteOut(s, f(a))
    , (s, a) => new TerminalOperation<B>.WriteErr(s, f(a))
    , g => new TerminalOperation<B>.ReadLine(s => f(g(s)))
    , g => new TerminalOperation<B>.Read(i => f(g(i)))
    );
    }

    private class WriteOut : TerminalOperation<A> {
    private string s;
    private A a;

    public WriteOut(string s, A a) {
    this.s = s;
    this.a = a;
    }

    public override X Fold<X>(
    Func<string, A, X> writeOut
    , Func<string, A, X> writeErr
    , Func<Func<string, A>, X> readLine
    , Func<Func<int, A>, X> read
    ) {
    return writeOut(s, a);
    }
    }

    public static TerminalOperation<A> writeOut(string s, A a) {
    return new WriteOut(s, a);
    }

    private class WriteErr : TerminalOperation<A> {
    private string s;
    private A a;

    public WriteErr(string s, A a) {
    this.s = s;
    this.a = a;
    }

    public override X Fold<X>(
    Func<string, A, X> writeOut
    , Func<string, A, X> writeErr
    , Func<Func<string, A>, X> readLine
    , Func<Func<int, A>, X> read
    ) {
    return writeErr(s, a);
    }
    }

    public static TerminalOperation<A> writeErr(string s, A a) {
    return new WriteErr(s, a);
    }

    private class ReadLine : TerminalOperation<A> {
    private Func<string, A> f;

    public ReadLine(Func<string, A> f) {
    this.f = f;
    }

    public override X Fold<X>(
    Func<string, A, X> writeOut
    , Func<string, A, X> writeErr
    , Func<Func<string, A>, X> readLine
    , Func<Func<int, A>, X> read
    ) {
    return readLine(f);
    }
    }

    public static TerminalOperation<A> readLine(Func<string, A> f) {
    return new ReadLine(f);
    }

    private class Read : TerminalOperation<A> {
    private Func<int, A> f;

    public Read(Func<int, A> f) {
    this.f = f;
    }

    public override X Fold<X>(
    Func<string, A, X> writeOut
    , Func<string, A, X> writeErr
    , Func<Func<string, A>, X> readLine
    , Func<Func<int, A>, X> read
    ) {
    return read(f);
    }
    }

    public static TerminalOperation<A> read(Func<int, A> f) {
    return new Read(f);
    }
    }

    public abstract class Terminal<A> {
    public abstract X Fold<X>(
    Func<A, X> done
    , Func<TerminalOperation<Terminal<A>>, X> more
    );

    public Terminal<B> Select<B>(Func<A, B> f) {
    return Fold<Terminal<B>>(
    a => Terminal<B>.done(f(a))
    , a => Terminal<B>.more(a.Select(t => t.Select(f)))
    );
    }

    public Terminal<B> SelectMany<B>(Func<A, Terminal<B>> f) {
    return Fold<Terminal<B>>(
    f
    , a => Terminal<B>.more(a.Select(t => t.SelectMany(f)))
    );
    }

    private class Done : Terminal<A> {
    public A a;

    public Done(A a) {
    this.a = a;
    }

    override public X Fold<X>(
    Func<A, X> done
    , Func<TerminalOperation<Terminal<A>>, X> more
    ) {
    return done(a);
    }
    }

    public static Terminal<A> done(A a) {
    return new Done(a);
    }

    private class More : Terminal<A> {
    public TerminalOperation<Terminal<A>> a;

    public More(TerminalOperation<Terminal<A>> a) {
    this.a = a;
    }

    override public X Fold<X>(
    Func<A, X> done
    , Func<TerminalOperation<Terminal<A>>, X> more
    ) {
    return more(a);
    }
    }

    public static Terminal<A> more(TerminalOperation<Terminal<A>> a) {
    return new More(a);
    }

    /*
    CAUTION: This function is unsafe.
    It is the "end of the (Terminal) world" interpreter.

    Use this function to run the final terminal program.
    Ideally, this function would be hypothetical and unavailable
    to the programmer API (i.e. implemented in its own runtime).
    */
    public static A Interpret(Terminal<A> t) {
    return t.Fold<A>(
    a => a
    , a => a.Fold<A>(
    (s, tt) => {
    Console.WriteLine(s);
    return Interpret(tt);
    }
    , (s, tt) => {
    Console.Error.WriteLine(s);
    return Interpret(tt);
    }
    , f => {
    var s = Console.ReadLine();
    return Interpret(f(s));
    }
    , f => {
    var i = Console.Read();
    return Interpret(f(i));
    }
    )
    );
    }
    }
    }