using System.Collections;
using System.Collections.Generic;
public static void Main()
var eta = Enumerable.Range(3, 2);
var etb = Enumerable.Range(15, 4);
Print(EnumerableApplicative.LiftA2(eta, etb, (a, b) => (a, b)));
Print(NullableApplicative.LiftA2(nta, nta, (a, b) => (a, b)));
Print(NullableApplicative.LiftA2(nta, ntb, (a, b) => (a, b)));
Print(ZipListApplicative.LiftA2(new ZipList<int>(eta), new ZipList<int>(etb), (a, b) => (a, b)));
private static void Print<T>(T value) where T : struct
Console.WriteLine(value);
private static void Print<T>(T? value) where T :struct
Console.WriteLine(value?.ToString() ?? "Null");
private static void Print<T>(IEnumerable<T> source)
Console.WriteLine($"[{string.Join(", ", source)}]");
public static class EnumerableApplicative
public static IEnumerable<A> Pure<A>(A a) => new[] { a };
public static IEnumerable<C> LiftA2<A, B, C>(IEnumerable<A> ta, IEnumerable<B> tb, Func<A, B, C> map2) =>
ta.SelectMany(a => tb.Select(b => map2(a, b)));
public static class NullableApplicative
public static A? Pure<A>(A a) where A : struct => a;
public static C? LiftA2<A, B, C>(A? ta, B? tb, Func<A, B, C> map2) where A : struct where B : struct where C : struct =>
(A a, B b) => map2(a, b),
public static class ZipListApplicative
public static IEnumerable<A> Pure<A>(A a) =>
Enumerable.Repeat(a, int.MaxValue);
public static ZipList<C> LiftA2<A, B, C>(ZipList<A> ta, ZipList<B> tb, Func<A, B, C> map2) =>
new ZipList<C>(ta.Zip(tb, map2));
public class ZipList<T> : IEnumerable<T>
private readonly IEnumerable<T> _inner;
public ZipList(IEnumerable<T> inner)
public IEnumerator<T> GetEnumerator()
return _inner.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
return ((IEnumerable)_inner).GetEnumerator();