1 /** This module implements a variety of classes derived from Function. */
2 module generald.functions;
3 
4 import std.typecons, std.traits;
5 
6 alias Maybe = Nullable;
7 
8 /// A class representing a function.
9 abstract class Function(A, B)
10 {
11 	abstract B opCall(A);
12 	alias InputType = A;
13 	alias OutputType = B;
14 }
15 
16 /// Template for converting a function to Function instance.
17 class RealFunction(alias f, A=ParameterTypeTuple!f[0], B=ReturnType!f) : Function!(A, B)
18 {
19 	pragma(msg, "RealFunction(", A.stringof, " -> ", ParameterTypeTuple!f[0].stringof, " -> ", ReturnType!f.stringof, " -> ", B.stringof, ")");
20 	override B opCall(A x)
21 	{
22 		return f(x);
23 	}
24 	mixin Singleton;
25 }
26 
27 ///
28 unittest
29 {
30 	static int f(long x)
31 	{
32 		return cast(int)((x >> 32) ^ (x & ((1UL << 32) - 1)));
33 	}
34 	static assert (is (RealFunction!f : Function!(long, int)));
35 	static assert (is (RealFunction!(f, int, int) : Function!(int, int)));
36 	static assert (is (RealFunction!(f, int, long) : Function!(int, long)));
37 	static assert (is (RealFunction!(f, long, long) : Function!(long, long)));
38 }
39 
40 /// Identity function.
41 T id(T)(T x)
42 {
43 	return x;
44 }
45 
46 /// ditto
47 alias IdentityFunction(T) = RealFunction!(id!T);
48 
49 ///
50 unittest
51 {
52 	auto f = IdentityFunction!int.get;
53 	assert (f(0) == 0);
54 }
55 
56 /// Compose two Functions.
57 class ComposedFunction(A, B, C, D) : Function!(A, D)
58 	if (is (B : C))
59 {
60 	Function!(A, B) f;
61 	Function!(C, D) g;
62 	this (Function!(A, B) f, Function!(C, D) g)
63 	{
64 		this.f = f;
65 		this.g = g;
66 	}
67 	override D opCall(A x)
68 	{
69 		return g(f(x));
70 	}
71 }
72 
73 /// ditto
74 auto compose(F, G)(F f, G g)
75 	if (is (F : Function!(F.InputType, F.OutputType)) &&
76 		is (G : Function!(G.InputType, G.OutputType)) &&
77 		is (F.OutputType : G.InputType))
78 {
79 	return new ComposedFunction!(F.InputType, F.OutputType, G.InputType, G.OutputType)(f, g);
80 }
81 
82 ///
83 unittest
84 {
85 	auto f = RealFunction!(triple!int).get.compose(RealFunction!(increment!int).get);
86 	auto g = RealFunction!(increment!int).get.compose(RealFunction!(triple!int).get);
87 	assert (f(0) == 1);
88 	assert (f(1) == 4);
89 	assert (g(0) == 3);
90 	assert (g(1) == 6);
91 }
92 
93 /// Curry a function.
94 class CurriedFunction(A, B, C) : Function!(A, Function!(B, C))
95 {
96 	Function!(Tuple!(A, B), C) uncurried;
97 	this (Function!(Tuple!(A, B), C) uncurried)
98 	{
99 		this.uncurried = uncurried;
100 	}
101 	class Partial : Function!(B, C)
102 	{
103 		A x;
104 		this (A x)
105 		{
106 			this.x = x;
107 		}
108 		override C opCall(B x)
109 		{
110 			return uncurried(this.x.tuple(x));
111 		}
112 	}
113 	override Function!(B, C) opCall(A x)
114 	{
115 		return new Partial(x);
116 	}
117 }
118 
119 /// ditto
120 auto curry(F)(F f)
121 {
122 	static if (is (F.InputType : Tuple!(A, B), A, B))
123 		return new CurriedFunction!(A, B, F.OutputType)(f);
124 	else static assert (false);
125 }
126 
127 ///
128 unittest
129 {
130 	static class F : Function!(Tuple!(int, int), int)
131 	{
132 		override int opCall(Tuple!(int, int) x)
133 		{
134 			return x[0] + x[1];
135 		}
136 		mixin Singleton;
137 	}
138 	auto cf = F.get.curry;
139 	static assert (is (typeof (cf) : Function!(int, Function!(int, int))));
140 }
141 
142 /// Uncurry a function.
143 class UncurriedFunction(A, B, C) : Function!(Tuple!(A, B), C)
144 {
145 	Function!(A, Function!(B, C)) curried;
146 	this (Function!(A, Function!(B, C)) curried)
147 	{
148 		this.curried = curried;
149 	}
150 	override C opCall(Tuple!(A, B) x)
151 	{
152 		return curried(x[0])(x[1]);
153 	}
154 }
155 
156 /// ditto
157 auto uncurry(F)(F f)
158 {
159 	static if (is (F.OutputType == Function!(B, C), B, C))
160 		return new UncurriedFunction!(F.InputType, B, C)(f);
161 	else static assert (false);
162 }
163 
164 ///
165 unittest
166 {
167 	static class G : Function!(int, Function!(int, int))
168 	{
169 
170 		class g : Function!(int, int)
171 		{
172 			int x;
173 			this (int x)
174 			{
175 				this.x = x;
176 			}
177 			override int opCall(int x)
178 			{
179 				return this.x + x;
180 			}
181 		}
182 		override Function!(int, int) opCall(int x)
183 		{
184 			return new g(x);
185 		}
186 		mixin Singleton;
187 	}
188 	auto ug = G.get.uncurry;
189 	static assert (is (typeof (ug) : Function!(Tuple!(int, int), int)));
190 }
191 
192 /// Function which always return Null.
193 Maybe!A nothing(A)()
194 {
195 	return Maybe!A();
196 }
197 
198 /// Return function for Maybe.
199 Maybe!A just(A)(A x)
200 {
201 	return Maybe!A(x);
202 }
203 /// ditto
204 alias maybeReturn(A) = just!A;
205 /// ditto
206 alias MaybeReturn(A) = RealFunction!(maybeReturn!A);
207 
208 /// Bind function for Maybe: (a -> Maybe b) -> (Maybe a -> Maybe b).
209 class MaybeBind(A, B) : Function!(Maybe!A, Maybe!B)
210 {
211 	Function!(A, Maybe!B) f;
212 	this (Function!(A, Maybe!B) f)
213 	{
214 		this.f = f;
215 	}
216 	override Maybe!B opCall(Maybe!A x)
217 	{
218 		if (x.isNull)
219 			return nothing!B;
220 		return f(x.get);
221 	}
222 }
223 
224 /// ditto
225 auto maybeBind(F)(F f)
226 {
227 	static if (is (F.OutputType : Maybe!B, B))
228 		return new MaybeBind!(F.InputType, B)(f);
229 	else static assert (false);
230 }
231 
232 /// Map function for Maybe.
233 auto maybeMap(F)(F f)
234 	if (is (F : Function!(A, B), A, B))
235 {
236 	return f.compose(MaybeReturn!(F.OutputType).get).maybeBind;
237 }
238 
239 ///
240 unittest
241 {
242 	auto maybeId = maybeMap(RealFunction!(id!int).get);
243 	assert (maybeId(just(0)) == just(0));
244 }
245 
246 /// Compose two Functions f and g where f emits a Maybe!B and g takes a B.
247 auto maybeCompose(F, G)(F f, G g)
248 {
249 	return f.compose(g.maybeBind);
250 }
251 
252 ///
253 unittest
254 {
255 	auto c = RealFunction!(collatz!int).get;
256 	auto lc = c.maybeCompose(c).maybeCompose(c);
257 	auto rc = c.maybeCompose(c.maybeCompose(c));
258 	foreach (i; 0..10)
259 		assert (lc(i).maybeEqual(rc(i)));
260 }
261 
262 /// Function from Maybe to void can be constructed from a Function to void.
263 class MaybeSink(A) : Function!(Maybe!A, void)
264 {
265 	Function!(A, void) sink;
266 	this (Function!(A, void) sink)
267 	{
268 		this.sink = sink;
269 	}
270 	override void opCall(Maybe!A x)
271 	{
272 		if (x.isNull)
273 			return;
274 		sink(x);
275 	}
276 }
277 
278 /// ditto
279 auto maybeSink(S)(S sink)
280 	if (is (S : Function!(A, void), A))
281 {
282 	return new MaybeSink!(S.InputType)(sink);
283 }
284 
285 /// Maybe do something, and return null.
286 class MaybeNothing(A, B, C=void) : Function!(A, Maybe!B)
287 {
288 	Function!(A, C) sink;
289 	this (Function!(A, C) sink)
290 	{
291 		this.sink = sink;
292 	}
293 	override Maybe!B opCall(A x)
294 	{
295 		sink(x);
296 		return nothing!B;
297 	}
298 }
299 
300 /// ditto
301 auto maybeNothing(B, F)(F f)
302 {
303 	return new MaybeNothing!(F.InputType, B, F.OutputType)(f);
304 }
305 
306 
307 /// Either type.
308 struct Either(A, B)
309 	if (!is (A : B) && !is (B : A))
310 {
311 	this (A a)
312 	{
313 		this.nonNull = true;
314 		this.which = false;
315 		this._a = a;
316 	}
317 	this (B b)
318 	{
319 		this.nonNull = true;
320 		this.which = true;
321 		this._b = b;
322 	}
323 	typeof (this) opAssign(A a)
324 	{
325 		this.nonNull = true;
326 		this.which = false;
327 		this._a = a;
328 		return this;
329 	}
330 	typeof (this) opAssign(B b)
331 	{
332 		this.nonNull = true;
333 		this.which = true;
334 		this._b = b;
335 		return this;
336 	}
337 	string toString()
338 	{
339 		import std.conv;
340 		if (!which)
341 			return _a.to!string;
342 		else
343 			return _b.to!string;
344 	}
345 private:
346 	bool nonNull, which;
347 	A _a;
348 	B _b;
349 	@property A a()
350 	in
351 	{
352 		assert (this.nonNull);
353 		assert (!this.which);
354 	}
355 	body
356 	{
357 		return _a;
358 	}
359 	@property B b()
360 	in
361 	{
362 		assert (this.nonNull);
363 		assert ( this.which);
364 	}
365 	body
366 	{
367 		return _b;
368 	}
369 }
370 
371 /// Tuple of functions, which takes an Either.
372 class EitherFunction(A, B, C) : Function!(Either!(A, B), C)
373 {
374 	Function!(A, C) f;
375 	Function!(B, C) g;
376 	this (Function!(A, C) f, Function!(B, C) g)
377 	{
378 		this.f = f;
379 		this.g = g;
380 	}
381 	override C opCall(Either!(A, B) x)
382 	{
383 		if (!x.which)
384 			return f(x.a);
385 		else
386 			return g(x.b);
387 	}
388 }
389 
390 /// ditto
391 auto eitherFunction(F, G)(F f, G g)
392 	if (is (F.OutputType == G.OutputType))
393 {
394 	return new EitherFunction!(F.InputType, G.InputType, F.OutputType)(f, g);
395 }
396 
397 /// Function to Either.
398 alias LeftEither(A, B) = RealFunction!(left!(B, A));
399 
400 /// ditto
401 alias RightEither(A, B) = RealFunction!(right!(A, B));
402 
403 /// Either!(, B) functor at A.
404 Either!(A, B) left(B, A)(A a)
405 {
406 	Either!(A, B) x;
407 	x = a;
408 	return x;
409 }
410 
411 /// Either!(A, ) functor at B.
412 Either!(A, B) right(A, B)(B b)
413 {
414 	Either!(A, B) x;
415 	x = b;
416 	return x;
417 }
418 
419 /// Function from and to Either.
420 auto eitherEither(F, G)(F f, G g)
421 {
422 	return eitherFunction(
423 		f.compose(LeftEither!(F.OutputType, G.OutputType).get),
424 		g.compose(RightEither!(F.OutputType, G.OutputType).get));
425 }
426 
427 /// Tuple of functions, which returns a Tuple.
428 class FunctionTuple(A, B, C) : Function!(A, Tuple!(B, C))
429 {
430 	Function!(A, B) f;
431 	Function!(A, C) g;
432 	this (Function!(A, B) f, Function!(A, C) g)
433 	{
434 		this.f = f;
435 		this.g = g;
436 	}
437 	override Tuple!(B, C) opCall(A x)
438 	{
439 		return tuple(f(x), g(x));
440 	}
441 }
442 
443 /// ditto
444 auto functionTuple(F, G)(F f, G g)
445 	if (is (F.InputType == G.InputType))
446 {
447 	return new FunctionTuple!(F.InputType, F.OutputType, G.OutputType)(f, g);
448 }
449 
450 /// Function from Tuple.
451 auto tupleLeft(T)(T x)
452 {
453 	return x[0];
454 }
455 
456 /// ditto
457 auto tupleRight(T)(T x)
458 {
459 	return x[1];
460 }
461 
462 /// ditto
463 alias TupleLeft(A, B) = RealFunction!(tupleLeft!(Tuple!(A, B)));
464 
465 /// ditto
466 alias TupleRight(A, B) = RealFunction!(tupleRight!(Tuple!(A, B)));
467 
468 /// Function from and to Tuple.
469 auto tupleTuple(F, G)(F f, G g)
470 {
471 	return functionTuple(
472 		TupleLeft!(F.InputType, G.InputType).get.compose(f),
473 		TupleRight!(F.InputType, G.InputType).get.compose(g));
474 }
475 
476 /// Function from Tuple of Maybe to Maybe of Tuple.
477 auto maybeTuple(TM)(TM x)
478 {
479 	if (x[0].isNull || x[1].isNull)
480 		return nothing!(Tuple!(typeof (x[0].get), typeof (x[1].get)));
481 	return just(x[0].get.tuple(x[1].get));
482 }
483 
484 /// ditto
485 alias MaybeTuple(A, B) = RealFunction!(maybeTuple!(Tuple!(Maybe!A, Maybe!B)));
486 
487 /// Returns the function which swaps the components of the given tuple.
488 auto swapper(A, B)()
489 {
490 	return TupleRight!(A, B).get.functionTuple(TupleLeft!(A, B).get);
491 }
492 
493 /// Compose with swapper.
494 auto swapResult(F)(F f)
495 {
496 	return f.compose(swapper!(F.OutputType.Types[0], F.OutputType.Types[1])());
497 }
498 
499 ///
500 unittest
501 {
502 	auto x = new Maybe!int[4];
503 	auto y = new Maybe!int[4];
504 	auto z = new Maybe!(Tuple!(int, int))[4];
505 	x[0] = 2; x[1] = 3;
506 	y[0] = 5, y[2] = 7;
507 	z[0] = 5.tuple(2);
508 	auto p = IdentityFunction!(Tuple!(Maybe!int, Maybe!int)).get
509 	.swapResult.compose(MaybeTuple!(int, int).get);
510 	foreach (i; 0..4)
511 		assert (p(x[i].tuple(y[i])).maybeEqual(z[i]));
512 }
513 
514 /// (a -> b) -> (a -> (b, a))
515 auto functionTupleIdentity(F)(F f)
516 {
517 	return f.functionTuple(IdentityFunction!(F.InputType).get);
518 }
519 
520 /// (a -> b) -> ((a, c) -> (b, c))
521 auto tupleTupleIdentity(B, F)(F f)
522 {
523 	return f.tupleTuple(IdentityFunction!B.get);
524 }
525 
526 /// (a -> b) -> (a|b -> b)
527 auto eitherFunctionIdentity(F)(F f)
528 {
529 	return f.eitherFunction(IdentityFunction!(F.OutputType).get);
530 }
531 
532 /// (a -> b) -> (a|c -> b|c)
533 auto eitherEitherIdentity(B, F)(F f)
534 {
535 	return f.eitherEither(IdentityFunction!B.get);
536 }
537 
538 ///
539 unittest
540 {
541 	static class TestedFunction : Function!(int, string)
542 	{
543 		override string opCall(int x)
544 		{
545 			import std.math, std.conv;
546 			return sqrt(real(1) + x * x).to!string;
547 		}
548 		mixin Singleton;
549 	}
550 	auto t = TestedFunction.get;
551 	auto
552 		t0 = t.functionTupleIdentity,
553 		t1 = t.tupleTupleIdentity!(int[]),
554 		t2 = t.eitherFunctionIdentity,
555 		t3 = t.eitherEitherIdentity!(int[]);
556 	static assert (is (typeof (t0) : Function!(int, Tuple!(string, int))));
557 	static assert (is (typeof (t1) : Function!(Tuple!(int, int[]), Tuple!(string, int[]))));
558 	static assert (is (typeof (t2) : Function!(Either!(int, string), string)));
559 	static assert (is (typeof (t3) : Function!(Either!(int, int[]), Either!(string, int[]))));
560 }
561 
562 /// Either of functions, which returns an Either.
563 class FunctionEither(A, B, C) : Function!(A, Either!(B, C))
564 {
565 	Either!(Function!(A, B), Function!(A, C)) f;
566 	this (Either!(Function!(A, B), Function!(A, C)) f)
567 	{
568 		this.f = f;
569 	}
570 	this (Function!(A, B) f)
571 	{
572 		this.f = f;
573 	}
574 	this (Function!(A, C) f)
575 	{
576 		this.f = f;
577 	}
578 	override Either!(B, C) opCall(A x)
579 	{
580 		if (!f.which)
581 			return f.a()(x).left!C;
582 		else
583 			return f.b()(x).right!B;
584 	}
585 }
586 
587 /// ditto
588 auto functionEitherLeft(C, F)(F f)
589 {
590 	static if (is (F : Function!(A, B), A, B))
591 		return new FunctionEither!(A, B, C)(f);
592 	else static assert (false);
593 }
594 
595 /// ditto
596 auto functionEitherRight(B, F)(F f)
597 {
598 	static if (is (F : Function!(A, C), A, C))
599 		return new FunctionEither!(A, B, C)(f);
600 	else static assert (false);
601 }
602 
603 ///
604 unittest
605 {
606 	static real lreal(int x)
607 	{
608 		import std.math;
609 		return PI * x;
610 	}
611 	static string rstring(int x)
612 	{
613 		import std.conv;
614 		return x.to!string;
615 	}
616 	auto fl = RealFunction!lreal.get.functionEitherLeft!string;
617 	auto fr = RealFunction!rstring.get.functionEitherRight!real;
618 	static assert (is (typeof (fl) == typeof (fr)));
619 }
620 
621 /// Either of functions, which takes a Tuple.
622 class TupleFunction(A, B, C) : Function!(Tuple!(A, B), C)
623 {
624 	Either!(Function!(A, C), Function!(B, C)) f;
625 	this (Either!(Function!(A, C), Function!(B, C)) f)
626 	{
627 		this.f = f;
628 	}
629 	this (Function!(A, C) f)
630 	{
631 		this.f = f;
632 	}
633 	this (Function!(B, C) f)
634 	{
635 		this.f = f;
636 	}
637 	override C opCall(Tuple!(A, B) x)
638 	{
639 		if (!f.which)
640 			return f.a()(x[0]);
641 		else
642 			return f.b()(x[1]);
643 	}
644 }
645 
646 /// ditto
647 auto leftTupleFunction(B, F)(F f)
648 {
649 	return new TupleFunction!(F.InputType, B, F.OutputType)(f);
650 }
651 
652 /// ditto
653 auto rightTupleFunction(A, F)(F f)
654 {
655 	return new TupleFunction!(A, F.InputType, F.OutputType)(f);
656 }
657 
658 ///
659 unittest
660 {
661 	static string cts(char x)
662 	{
663 		import std.conv : to;
664 		return x.to!string;
665 	}
666 	static string rts(real x)
667 	{
668 		import std.conv : to;
669 		return x.to!string;
670 	}
671 	auto l = RealFunction!cts.get.leftTupleFunction!real;
672 	auto r = RealFunction!rts.get.rightTupleFunction!char;
673 	static assert (is (typeof (l) == typeof (r)));
674 }
675 
676 /// Takues an array of functions and return a function to array.
677 class FunctionArray(A, B) : Function!(A, B[])
678 {
679 	Function!(A, B)[] fs;
680 	this (Function!(A, B)[] fs)
681 	{
682 		this.fs = fs;
683 	}
684 	override B[] opCall(A x)
685 	{
686 		B[] ret;
687 		foreach (f; fs)
688 			ret ~= f(x);
689 		return ret;
690 	}
691 }
692 
693 /// ditto
694 auto functionArray(F)(F[] fs)
695 {
696 	return new FunctionArray!(F.InputType, F.OutputType)(fs);
697 }
698 
699 /// Map function for Array.
700 class ArrayMap(A, B) : Function!(A[], B[])
701 {
702 	Function!(A, B) f;
703 	this (Function!(A, B) f)
704 	{
705 		this.f = f;
706 	}
707 	override B[] opCall(A[] xs)
708 	{
709 		B[] ret;
710 		foreach (x; xs)
711 			ret ~= f(x);
712 		return ret;
713 	}
714 }
715 
716 /// ditto
717 auto arrayMap(F)(F f)
718 {
719 	return new ArrayMap!(F.InputType, F.OutputType)(f);
720 }
721 
722 /// Bind function for Array.
723 class ArrayBind(A, B) : Function!(A[], B[])
724 {
725 	Function!(A, B[]) f;
726 	this (Function!(A, B[]) f)
727 	{
728 		this.f = f;
729 	}
730 	override B[] opCall(A[] xs)
731 	{
732 		B[] ret;
733 		foreach (x; xs)
734 			ret ~= f(x);
735 		return ret;
736 	}
737 }
738 
739 /// ditto
740 auto arrayBind(F)(F f)
741 {
742 	return new ArrayBind!(F.InputType, ElementType!(F.OutputType));
743 }
744 
745 /// Return function for Array.
746 auto arrayOnly(A)(A x)
747 {
748 	return [x];
749 }
750 /// ditto
751 alias arrayReturn(A) = arrayOnly!A;
752 /// ditto
753 alias ArrayReturn(A) = RealFunction!(arrayReturn!A);
754 
755 /// Function from Array to void can be constructed from a Function to void.
756 class ArraySink(A) : Function!(A[], void)
757 {
758 	Function!(A, void) sink;
759 	this (Function!(A, void) sink)
760 	{
761 		this.sink = sink;
762 	}
763 	override void opCall(A[] xs)
764 	{
765 		foreach (x; xs)
766 			sink(x);
767 	}
768 }
769 
770 /// ditto
771 auto arraySink(S)(S sink)
772 	if (is (S : Function!(A, void), A))
773 {
774 	return new ArraySink!(S.InputType)(sink);
775 }
776 
777 ///
778 unittest
779 {
780 	import std.stdio, std.range, std.array;
781 	RealFunction!(writeln!int).get.arraySink()(10.iota.array);
782 }
783 
784 /// Take an array of functions and return a function from and to array.
785 class ArrayArray(A, B) : Function!(A[], B[])
786 {
787 	Function!(A, B)[] fs;
788 	this (Function!(A, B)[] fs)
789 	{
790 		this.fs = fs;
791 	}
792 	@property size_t length()
793 	{
794 		return fs.length;
795 	}
796 	override B[] opCall(A[] xs)
797 	in
798 	{
799 		assert (length == xs.length);
800 	}
801 	body
802 	{
803 		import std.range : zip;
804 		B[] ret;
805 		foreach (fx; fs.zip(xs))
806 			ret ~= fx[0](fx[1]);
807 		return ret;
808 	}
809 }
810 
811 /// ditto
812 auto arrayArray(FS)(FS fs)
813 {
814 	import std.range : ElementType;
815 	return new ArrayArray!(ElementType!FS.InputType, ElementType!FS.OutputType)(fs);
816 }
817 
818 ///
819 unittest
820 {
821 	static class Adder : Function!(int, int)
822 	{
823 		int added;
824 		this (int added)
825 		{
826 			this.added = added;
827 		}
828 		override int opCall(int x)
829 		{
830 			return x + added;
831 		}
832 	}
833 	import std.range : iota;
834 	import std.array : array;
835 	Function!(int, int)[] adders;
836 	foreach_reverse (i; 0..5)
837 		adders ~= new Adder(i + 1);
838 	assert (adders.arrayArray()(5.iota.array) == [5, 5, 5, 5, 5]);
839 }
840 
841 /// Singleton pattern.
842 mixin template Singleton(Flag!"hideConstructor" hideConstructor = Yes.hideConstructor)
843 {
844 	static typeof (this) get()
845 	{
846 		static typeof (this) instance;
847 		if (instance is null)
848 			return instance = new typeof (this)();
849 		return instance;
850 	}
851 	static if (hideConstructor)
852 	private this ()
853 	{
854 	}
855 }
856 
857 debug
858 {
859 	class Printer(T) : Function!(T, void)
860 	{
861 		override void opCall(T x)
862 		{
863 			import std.stdio;
864 			writeln(x);
865 		}
866 		mixin Singleton;
867 	}
868 	auto autoPrint(F)(F f)
869 	{
870 		return f.compose(Printer!(F.OutputType).get);
871 	}
872 	auto autoPrintJustOnly(F)(F f)
873 	{
874 		static if (is (F.OutputType : Maybe!B, B))
875 			return f.compose(Printer!B.get.maybeSink);
876 		else static assert (false);
877 	}
878 }
879 
880 version (unittest)
881 {
882 	T triple(T)(T x)
883 	{
884 		return x * 3;
885 	}
886 	T increment(T)(T x)
887 	{
888 		return x + 1;
889 	}
890 	Maybe!T collatz(T)(T x)
891 	{
892 		if (x <= 1)
893 			return nothing!T;
894 		if (x & 1)
895 			return just(x.triple.increment);
896 		return just(x / 2);
897 	}
898 	bool maybeEqual(T)(Maybe!T x, Maybe!T y)
899 	{
900 		if (x.isNull || y.isNull)
901 			return x.isNull && y.isNull;
902 		return x.get == y.get;
903 	}
904 }
905 
906 unittest
907 {
908 	import std.stdio;
909 	stderr.writeln("unittest passed!");
910 }