Ismail S
September 2018
Warning
This stuff is hard to learn, especially for the first time. If it doesn’t make sense, ask.
Sequencing operations and treating all failures in the same way
Lazy lists
Representation of a value or error that will be available at some future time
Representation of a value that will be available once this function is called (it’s weird)
Elevate a function (with one input) to operate on Functor objects
-- | Replace all locations in the input with the same value.
(<$) :: a -> f b -> f a
(<$) = fmap . const
<$
is equivalent to:
No library support. If there was, could look like:
Or:
No library support. If there was, could look like:
Or:
This can work in one of 2 ways:
Very limited library support (Only 2 arguments)
No library support. If there was, could look like:
Or:
where we have:
Essentially, Function.map2
would have a type
signature:
Sequential application means we can do things like:
where all the arguments are Applicative
objects
thingies
-- | Lift a binary function to actions.
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
liftA2 f x = (<*>) (fmap f x)
-- | Sequence actions, discarding the value of the first argument.
(*>) :: f a -> f b -> f b
a1 *> a2 = (id <$ a1) <*> a2
-- | Sequence actions, discarding the value of the second argument.
(<*) :: f a -> f b -> f a
(<*) = liftA2 const
public Applicative<C> map2(BiFunction<A, B, C> func, Applicative<A> a, Applicative<B> b) {
return a.ap(Applicative.pure(func)).ap(b);
// note that this relies on being able to curry BiFunction
}
public Applicative<B> andThen(Applicative<A> a, Applicative<B> b) {
return replaceWithConstant(Function.identity, a).ap(b);
}
public Applicative<A> thenAnd(Applicative<A> a, Applicative<B> b) {
return andThen(b, a);
}
Another example:
No library support. If there was, could look like:
No library support. If there was, could look like:
class Maybe<T> extends Monad<T> {
public T t;
public Maybe(T t) {
this.t = t;
}
@Override
public <U> Maybe<U> map(Function<T, U> func) {
if (t != null) {
return new Maybe<U>(func.apply(t));
}
return new Maybe<U>(null);
}
public static <U> Maybe<U> pure(U u) {
return new Maybe<>(u);
}
public static <U> Maybe<U> retturn(U u) {
return pure(u);
}
@Override
public <U> Maybe<U> ap(Applicative<Function<T, U>> func1) {
Maybe<Function<T, U>> func = (Maybe<Function<T, U>>) func1;
if (func.t != null && t != null) {
return new Maybe<>(func.t.apply(t));
}
return new Maybe<>(null);
}
@Override
public <U> Maybe<U> flatMap(Function<T, Monad<U>> func) {
if (t != null) {
return (Maybe<U>)func.apply(t);
}
return new Maybe<>(null);
}
}
class MonadList<T> extends Monad<T> {
public List<T> list;
public MonadList(List<T> list) {
this.list = list;
}
@Override
public <U> MonadList<U> map(Function<T, U> func) {
return new MonadList<>(
list.stream().map(func).collect(toList()));
}
public static <U> MonadList<U> pure(U u) {
List<U> list = new ArrayList<>();
list.add(u);
return new MonadList<>(list);
}
public static <U> MonadList<U> retturn(U u) {
return pure(u);
}
@Override
public <U> MonadList<U> ap(Applicative<Function<T, U>> func1) {
MonadList<Function<T, U>> func = (MonadList<Function<T, U>>) func1;
List<U> result = new ArrayList<>();
func.list.forEach(f -> {
list.forEach(l -> {
result.add(f.apply(l));
});
});
return new MonadList<>(result);
}
@Override
public <U> MonadList<U> flatMap(Function<T, Monad<U>> func) {
List<U> result = new ArrayList<>();
list.forEach(l -> {
result.addAll(((MonadList<U>)func.apply(l)).list);
});
return new MonadList<>(result);
}
}
class ApplicativeList<T> extends Applicative<T> {
public Stream<T> list;
public ApplicativeList(Stream<T> list) {
this.list = list;
}
@Override
public <U> ApplicativeList<U> map(Function<T, U> func) {
return new ApplicativeList<>(
list.map(func);
}
public static <U> ApplicativeList<U> pure(U u) {
// To satisfy rules for Applicative, we must create an infinite stream
return new ApplicativeList<>(Stream.iterate(u, Function.identity));
}
@Override
public <U> ApplicativeList<U> ap(Applicative<Function<T, U>> func1) {
ApplicativeList<Function<T, U>> func = (ApplicativeList<Function<T, U>>) func1;
return new ApplicativeList(zipStreams(list, func.list, a, b -> a.apply(b))); // TODO-zipStreams example implementations at https://stackoverflow.com/q/17640754
}
}