summaryrefslogtreecommitdiff
path: root/src/main/java/bjc/data/internals/HalfBoundLazyPair.java
blob: 4f28012f1bcb020e00e330c480cea7ee11418146 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
package bjc.data.internals;

import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;

import bjc.data.IHolder;
import bjc.data.IPair;
import bjc.data.Identity;
import bjc.data.LazyPair;

/*
 * @NOTE
 * 	I am not convinced that this code works correctly. Tests should be
 * 	written to make sure things only ever get instantiated once.
 *
 * 	Namely, my main concern is to whether the places that bind the pair
 * 	without setting pairBound are doing the right thing.
 */
/**
 * A lazy pair, with only one side bound.
 *
 * @author Ben Culkin
 */
@SuppressWarnings("javadoc")
public class HalfBoundLazyPair<OldType, NewLeft, NewRight> implements IPair<NewLeft, NewRight> {
	/* The supplier of the old value. */
	private final Supplier<OldType> oldSupplier;

	/* The function to transform the old value into a new pair. */
	private final Function<OldType, IPair<NewLeft, NewRight>> binder;

	/* The new bound pair. */
	private IPair<NewLeft, NewRight> boundPair;
	/* Has the pair been bound yet or not? */
	private boolean pairBound;

	/**
	 * Create a new half-bound lazy pair.
	 *
	 * @param oldSupp
	 *        The supplier of the old value.
	 *
	 * @param bindr
	 *        The function to use to create the pair from the old value.
	 */
	public HalfBoundLazyPair(final Supplier<OldType> oldSupp,
			final Function<OldType, IPair<NewLeft, NewRight>> bindr) {
		oldSupplier = oldSupp;
		binder = bindr;
	}

	@Override
	public <BoundLeft, BoundRight> IPair<BoundLeft, BoundRight> bind(
			final BiFunction<NewLeft, NewRight, IPair<BoundLeft, BoundRight>> bindr) {
		final IHolder<IPair<NewLeft, NewRight>> newPair = new Identity<>(boundPair);
		final IHolder<Boolean> newPairMade = new Identity<>(pairBound);

		final Supplier<NewLeft> leftSupp = () -> {
			if(!newPairMade.getValue()) {
				/* Bind the pair if it hasn't been bound yet. */
				newPair.replace(binder.apply(oldSupplier.get()));
				newPairMade.replace(true);
			}

			return newPair.unwrap((pair) -> pair.getLeft());
		};

		final Supplier<NewRight> rightSupp = () -> {
			if(!newPairMade.getValue()) {
				/* Bind the pair if it hasn't been bound yet. */
				newPair.replace(binder.apply(oldSupplier.get()));
				newPairMade.replace(true);
			}

			return newPair.unwrap((pair) -> pair.getRight());
		};

		return new BoundLazyPair<>(leftSupp, rightSupp, bindr);
	}

	@Override
	public <BoundLeft> IPair<BoundLeft, NewRight> bindLeft(
			final Function<NewLeft, IPair<BoundLeft, NewRight>> leftBinder) {
		final Supplier<NewLeft> leftSupp = () -> {
			IPair<NewLeft, NewRight> newPair = boundPair;

			if(!pairBound) {
				newPair = binder.apply(oldSupplier.get());
			}

			return newPair.getLeft();
		};

		return new HalfBoundLazyPair<>(leftSupp, leftBinder);
	}

	@Override
	public <BoundRight> IPair<NewLeft, BoundRight> bindRight(
			final Function<NewRight, IPair<NewLeft, BoundRight>> rightBinder) {
		final Supplier<NewRight> rightSupp = () -> {
			IPair<NewLeft, NewRight> newPair = boundPair;

			if(!pairBound) {
				newPair = binder.apply(oldSupplier.get());
			}

			return newPair.getRight();
		};

		return new HalfBoundLazyPair<>(rightSupp, rightBinder);
	}

	@Override
	public <OtherLeft, OtherRight, CombinedLeft, CombinedRight> IPair<CombinedLeft, CombinedRight> combine(
			final IPair<OtherLeft, OtherRight> otherPair,
			final BiFunction<NewLeft, OtherLeft, CombinedLeft> leftCombiner,
			final BiFunction<NewRight, OtherRight, CombinedRight> rightCombiner) {
		return otherPair.bind((otherLeft, otherRight) -> {
			return bind((leftVal, rightVal) -> {
				CombinedLeft cLeft = leftCombiner.apply(leftVal, otherLeft);
				CombinedRight cRight = rightCombiner.apply(rightVal, otherRight);

				return new LazyPair<>(cLeft, cRight);
			});
		});
	}

	@Override
	public <NewLeftType> IPair<NewLeftType, NewRight> mapLeft(final Function<NewLeft, NewLeftType> mapper) {
		final Supplier<NewLeftType> leftSupp = () -> {
			if(pairBound) return mapper.apply(boundPair.getLeft());

			final NewLeft leftVal = binder.apply(oldSupplier.get()).getLeft();

			return mapper.apply(leftVal);
		};

		final Supplier<NewRight> rightSupp = () -> {
			if(pairBound) return boundPair.getRight();

			return binder.apply(oldSupplier.get()).getRight();
		};

		return new LazyPair<>(leftSupp, rightSupp);
	}

	@Override
	public <NewRightType> IPair<NewLeft, NewRightType> mapRight(final Function<NewRight, NewRightType> mapper) {
		final Supplier<NewLeft> leftSupp = () -> {
			if(pairBound) return boundPair.getLeft();

			return binder.apply(oldSupplier.get()).getLeft();
		};

		final Supplier<NewRightType> rightSupp = () -> {
			if(pairBound) return mapper.apply(boundPair.getRight());

			final NewRight rightVal = binder.apply(oldSupplier.get()).getRight();

			return mapper.apply(rightVal);
		};

		return new LazyPair<>(leftSupp, rightSupp);
	}

	@Override
	public <MergedType> MergedType merge(final BiFunction<NewLeft, NewRight, MergedType> merger) {
		if(!pairBound) {
			boundPair = binder.apply(oldSupplier.get());

			pairBound = true;
		}

		return boundPair.merge(merger);
	}
}