Skip to content

Commit

Permalink
initial example commit
Browse files Browse the repository at this point in the history
  • Loading branch information
RichardWarburton committed Feb 10, 2012
0 parents commit c48a806
Show file tree
Hide file tree
Showing 15 changed files with 482 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.classpath
.project
.settings/
target/
33 changes: 33 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>monad4j</groupId>
<artifactId>monad4j-core</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Monad4J</name>
<description>implementation of monads in Java</description>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<debug>true</debug>
<debuglevel>source,lines,vars</debuglevel>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
7 changes: 7 additions & 0 deletions src/main/java/monad4j/Function.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package monad4j;

public interface Function<A,R> {

public R apply(A a);

}
13 changes: 13 additions & 0 deletions src/main/java/monad4j/InjectWith.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package monad4j;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectWith {
@SuppressWarnings("rawtypes")
Class<? extends Return> value();
}
7 changes: 7 additions & 0 deletions src/main/java/monad4j/Monad.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package monad4j;

public interface Monad<M, T> {

public <RA, R extends Monad<M, RA>> R chain(Function<T, R> f);

}
72 changes: 72 additions & 0 deletions src/main/java/monad4j/Monads.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package monad4j;

/**
* Static utility methods
*
* @author richard
*
*/
public class Monads {

/**
* In Haskell this is part of the type class, but here its a static method
*
*/
public static <T, W, M extends Monad<W, T>> M inject(T t, Class<W> cls) {
InjectWith injectWith = cls.getAnnotation(InjectWith.class);
if(injectWith == null) {
throw new IllegalArgumentException("Don't know what class to inject "+cls.getName()+" with");
}

try {
@SuppressWarnings("unchecked")
Return<T,M> injector = injectWith.value().newInstance();

return injector.inject(t);
} catch (InstantiationException | IllegalAccessException e) {
throw new IllegalStateException("Unable to create injector",e);
}
}

/**
* With JSR 335's default this could be in the monad interface (where it
* should be).
*
* In Haskell:
*
* m >> f = m >>= \_ -> f
*/
public static <A, RA, R extends Monad<M, RA>, M> R chainIgnore(
Monad<M, A> m, final Function<Void, R> f) {
return m.<RA, R> chain(new Function<A, R>() {
@Override
public R apply(A a) {
return f.apply(null);
}
});
}

/**
* Promote a function to a monad.
*
* In Haskell:
*
* liftM f m = m >>= \x -> return (f x)
*/
public static <M, R, A, RM extends Monad<M, R>, AM extends Monad<M, A>> Function<AM, RM> liftM(
final Function<A, R> f) {
return new Function<AM, RM>() {
@Override
public RM apply(final AM m) {
return m.chain(new Function<A, RM>() {
@Override
public RM apply(A a) {
// TODO: implement return
return null;
}
});
}
};
}

}
17 changes: 17 additions & 0 deletions src/main/java/monad4j/Return.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package monad4j;

/**
* Corresponds to the 'unit function' of the Monad. In haskell this is called 'return', thus the naming.
*
* We have to represent this using an interface, since Java doesn't have function types
*
* @author richard
*
* @see Monads#inject(Object, Class)
*
*/
public interface Return<T, M extends Monad<?, T>> {

public M inject(T t);

}
66 changes: 66 additions & 0 deletions src/main/java/monad4j/examples/maybe/Just.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package monad4j.examples.maybe;

import monad4j.Function;
import monad4j.Monad;

public class Just<T> extends Maybe<T> {

private final T t;

Just(T t) {
this.t = t;
}

public T get() {
return t;
}

@Override
public void doCase(MaybeCase<T> visitor) {
visitor.doJust(t);
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((t == null) ? 0 : t.hashCode());
return result;
}

@SuppressWarnings("rawtypes")
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Just other = (Just) obj;
if (t == null) {
if (other.t != null)
return false;
} else if (!t.equals(other.t))
return false;
return true;
}


@Override
public String toString() {
return "Just "+t;
}

/**
* Haskell Equivalent:
*
* (Just x) >>= k = k x
*/
@SuppressWarnings("rawtypes")
@Override
public <RA, R extends Monad<Maybe, RA>> R chain(Function<T, R> f) {
return f.apply(t);
}

}
20 changes: 20 additions & 0 deletions src/main/java/monad4j/examples/maybe/Maybe.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package monad4j.examples.maybe;

import monad4j.InjectWith;
import monad4j.Monad;

@SuppressWarnings("rawtypes")
@InjectWith(MaybeReturn.class)
public abstract class Maybe<T> implements Monad<Maybe, T> {

public static <T> Maybe<T> nothing() {
return new Nothing<T>();
}

public static <T> Maybe<T> just(T t) {
return new Just<T>(t);
}

public abstract void doCase(MaybeCase<T> visitor);

}
9 changes: 9 additions & 0 deletions src/main/java/monad4j/examples/maybe/MaybeCase.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package monad4j.examples.maybe;

public interface MaybeCase<T> {

public void doJust(T t);

public void doNothing();

}
18 changes: 18 additions & 0 deletions src/main/java/monad4j/examples/maybe/MaybeReturn.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package monad4j.examples.maybe;

import static monad4j.examples.maybe.Maybe.just;
import monad4j.Return;

/**
* Haskell equivalent:
*
* return = Just
*/
public class MaybeReturn<T> implements Return<T,Maybe<T>> {

@Override
public Maybe<T> inject(T t) {
return just(t);
}

}
44 changes: 44 additions & 0 deletions src/main/java/monad4j/examples/maybe/Nothing.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package monad4j.examples.maybe;

import monad4j.Function;
import monad4j.Monad;

class Nothing<T> extends Maybe<T> {

Nothing() {

}

@Override
public void doCase(MaybeCase<T> visitor) {
visitor.doNothing();
}

@Override
public boolean equals(Object obj) {
return obj instanceof Nothing;
}

@Override
public int hashCode() {
return super.hashCode();
}

/**
* Haskell equivalent:
*
* Nothing >>= _ = Nothing
*/

@Override
public String toString() {
return "Nothing";
}

@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public <RA, R extends Monad<Maybe, RA>> R chain(Function<T, R> f) {
return (R) Maybe.nothing();
}

}
31 changes: 31 additions & 0 deletions src/test/java/monad4j/TestMonads.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package monad4j;

import static junit.framework.Assert.assertEquals;
import static monad4j.Monads.chainIgnore;
import static monad4j.examples.maybe.Maybe.just;
import static monad4j.examples.maybe.Maybe.nothing;
import monad4j.Function;
import monad4j.examples.maybe.Maybe;

import org.junit.Test;

public class TestMonads {

Function<Void, Maybe<Integer>> f = new Function<Void, Maybe<Integer>>() {
@Override
public Maybe<Integer> apply(Void a) {
return just(5);
}
};

@Test
public void chainIgnoreNothing() {
assertEquals(nothing(),chainIgnore(nothing(),f));
}

@Test
public void chainIgnoreJust() {
assertEquals(just(5),chainIgnore(just(1),f));
}

}
Loading

0 comments on commit c48a806

Please sign in to comment.