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 }