001    package fj.control.db;
002    
003    import fj.F;
004    import fj.Function;
005    
006    import java.sql.Connection;
007    import java.sql.SQLException;
008    import java.util.concurrent.Callable;
009    
010    /**
011     * The DB monad represents a database action, or a value within the context of a database connection.
012     */
013    public abstract class DB<A> {
014    
015      /**
016       * Executes the database action, given a database connection.
017       *
018       * @param c The connection against which to execute the action.
019       * @return The result of the action.
020       * @throws SQLException if a database error occurred.
021       */
022      public abstract A run(final Connection c) throws SQLException;
023    
024      /**
025       * Constructs a database action as a function from a database connection to a value.
026       *
027       * @param f A function from a database connection to a value.
028       * @return A database action representing the given function.
029       */
030      public static <A> DB<A> db(final F<Connection, A> f) {
031        return new DB<A>() {
032          public A run(final Connection c) {
033            return f.f(c);
034          }
035        };
036      }
037    
038      /**
039       * Returns the callable-valued function projection of this database action.
040       *
041       * @return The callable-valued function which is isomorphic to this database action.
042       */
043      public F<Connection, Callable<A>> asFunction() {
044        return new F<Connection, Callable<A>>() {
045          public Callable<A> f(final Connection c) {
046            return new Callable<A>() {
047              public A call() throws Exception {
048                return run(c);
049              }
050            };
051          }
052        };
053      }
054    
055      /**
056       * Map a function over the result of this action.
057       *
058       * @param f The function to map over the result.
059       * @return A new database action that applies the given function to the result of this action.
060       */
061      public <B> DB<B> map(final F<A, B> f) {
062        return new DB<B>() {
063          public B run(final Connection c) throws SQLException {
064            return f.f(DB.this.run(c));
065          }
066        };
067      }
068    
069      /**
070       * Promotes any given function so that it transforms between values in the database.
071       *
072       * @param f The function to promote.
073       * @return A function equivalent to the given one, which operates on values in the database.
074       */
075      public static <A, B> F<DB<A>, DB<B>> liftM(final F<A, B> f) {
076        return new F<DB<A>, DB<B>>() {
077          public DB<B> f(final DB<A> a) {
078            return a.map(f);
079          }
080        };
081      }
082    
083      /**
084       * Constructs a database action that returns the given value completely intact.
085       *
086       * @param a A value to be wrapped in a database action.
087       * @return A new database action that returns the given value.
088       */
089      public static <A> DB<A> unit(final A a) {
090        return new DB<A>() {
091          public A run(final Connection c) {
092            return a;
093          }
094        };
095      }
096    
097      /**
098       * Binds the given action across the result of this database action.
099       *
100       * @param f The function to bind across the result of this database action.
101       * @return A new database action equivalent to applying the given function to the result of this action.
102       */
103      public <B> DB<B> bind(final F<A, DB<B>> f) {
104        return new DB<B>() {
105          public B run(final Connection c) throws SQLException {
106            return f.f(DB.this.run(c)).run(c);
107          }
108        };
109      }
110    
111      /**
112       * Removes one layer of monadic structure.
113       *
114       * @param a A database action that results in another.
115       * @return A new database action equivalent to the result of the given action.
116       */
117      public static <A> DB<A> join(final DB<DB<A>> a) {
118        return a.bind(Function.<DB<A>>identity());
119      }
120    }