ファンクタからモナドを通って矢印まで簡単に歩く



Pointed、Functor、Applicative Functor、Monad、Category、Arrowのチェーンに沿って歩きましょう。その間、これらすべてが脳を爆破するために発明されたのではなく、さらにhaskellに存在する非常に現実的な問題を解決するために発明されたことを示します。 ほとんどのコードはC#で記述されていますが、彼の知識がなくても、何が何であるかを理解できると思います。





例ではintでプリミティブ操作を使用していますが、たとえば、適用可能なファンクターでは、このオプションを検討することができます。

Maybe<int> userId = UserService.GetUserId(email)
Maybe<int> productId = ProductService.GetProductId(productCode)
Maybe<int> discount = Maybe<int>.Nothing;
if(userId.HasValue && porductId.HasValue)
    discount = new Maybe(DiscountService.GetDiscount(userId.Value, productId.Value));

      
      









, , Maybe, , null .



	public class Maybe<T> {
		public static readonly Maybe<T> Nothing = new Maybe<T>();
		public T Value { get; private set; }
		public bool HasValue { get; private set; }

		private Maybe() { }

		public Maybe(T value) {
			Value = value;
			HasValue = true;
		}

		public override string ToString() {
			if (HasValue)
				return Value.ToString();
			return "Nothing";
		}
	}


      
      







Maybe<User> GetUserById(int id){...}

      
      





Maybe<A>



? , A Maybe<A>, int -> Maybe<int>



, , — .



Pointed



: A -> Maybe<A>



, .


	static class Pointed {
		public static Maybe<A> Pure<A>(this A value) {
			return new Maybe<A>(value);
		}
	}

      
      







C#, this





	void Pointed() {
		int x = 1;
		Maybe<int> y = x.Pure();
	}

      
      







Maybe.





, , , A → B, . ? , Maybe , , , , Nothing, null. DRY .



	static class Functor {
		public static Maybe<B> Map<A,B>(this Maybe<A> maybe, Func<A,B> f) {
			if(maybe.HasValue) return f(maybe.Value).Pure();
			return Maybe<B>.Nothing;
		}
	}

      
      









	void Functor() {
		Func<int, int> inc = y => y + 1;//  int -> int
		var x = 1.Pure();//    Maybe
		var r = x.Map(inc); //       , r == Some(2)
	}


      
      







.





, (A,B) → C? map . , — . add(x,y) = x + y; inc = add(1); inc(10) == 11;

Func .



	static class CurryFunc {
		public static Func<T1,Func<T2,R>> Curry<T1,T2,R>(this Func<T1,T2,R> f) {
			return x => y => f(x, y);
		}
	}

      
      







	void Curry() {
		Func<int,int, int> add = (y,z) => y + z;
		var inc = add.Curry()(1);
		var r = inc(1); // r == 2
	}

      
      







apply .



	static class Applicative {
		public static Maybe<B> Apply<A,B>(this Maybe<Func<A,B>> f, Maybe<A> maybe) {
			if(f.HasValue) return maybe.Map(f.Value); //   ,      Map
			return Maybe<B>.Nothing;
		}
	}

      
      









	void Applicative() {
		Func<int, int, int> addF = (y,z) => y + z;
		var add = addF.Curry();
		var x1 = 1.Pure();
		var x2 = 2.Pure();
		var r = add.Pure().Apply(x1).Apply(x2); //       Maybe    ,  r == Some(3)
		Func<int,int,int> f = DiscountService.GetDiscount;
		var discount = f.Curry().Pure().Apply(userId).Apply(productId); //   
	}

      
      







, curry.

Func<int, int, int, int, int> addF = (a,b,c,d) => a + b + c + d;
addF.Curry().Apply(x1).Apply(x2).Apply(x3).Apply(x4);

      
      









, , A → Maybe GetUserById :: int -> Maybe<User>





map . Maybe<Maybe<B>>



,



	static class Monad {
		public static Maybe<B> Bind<A,B>(this Maybe<A> maybe, Func<A,Maybe<B>> f) {
			if(maybe.HasValue) return f(maybe.Value);
			return Maybe<B>.Nothing;
		}
	}

      
      







	void Monad() {
		Func<int, Maybe<int>> solve = y => y == 0 ? Maybe<int>.Nothing : (y + 1).Pure();
		var x = 1.Pure();
		var y = 0.Pure();
		var r1 = x.Bind(solve); // r1 == Some(2)
		var r2 = y.Bind(solve); // r2 == Nothing
	}


      
      







, , Maybe.





. Maybe .

? , ,

f = length . filter (>3) . map (+1) . skip 3  

      
      





(.) .

f - , 1 , .

, .



, . . , . . , , . , f: A → B, g:B → C f compose g: A → C. , , .



. .



	static class Category {
		public static Func<A,C> Compose<A,B,C>(this Func<B,C>f1, Func<A,B> f2) {
			return x => f1(f2(x));
		}
	}

      
      







	void Category() {
		Func<int, int> f1 = y => y + 1;
		Func<int, int> f2 = y => y * 2;
		var fr = f2.Compose(f1);
		var r = fr(1);// r == 4
	}

      
      







A → Maybe<B>, B → Maybe<C>



.



A → Maybe<B>



. , . - A → M<B>







	class Kleisli<A,B> {
		public Func<A,Maybe<B>> Func { get; set; }
		public Kleisli(Func<A,Maybe<B>> f) {
			Func = f;
		}
	}



      
      





.



static class Category {
		public static Kleisli<A,B> ToKleisli<A,B>(this Func<A,Maybe<B>> f) {
			return new Kleisli<A, B>(f);
		}

		public static Kleisli<A,C> Compose<A,B,C>(this Kleisli<B,C> f1, 
			Kleisli<A,B> f2) {
			return ToKleisli<A,C>(x => f2.Func(x).Bind(f1.Func));
		}
	}


      
      








	void Category2() {
		Func<int, Maybe<int>> f1 = y => (y + 1).Pure();
		Func<int, Maybe<int>> f2 = y => (y * 2).Pure();
		var fr = f2.ToKleisli().Compose(f1.ToKleisli());
		var r = fr.Func(1); //r == Some(4)
	}

      
      





, f2.Compose(f1)

.





. -> B, B -> Maybe<C>



. ,


	static class Arrow {
		public static Kleisli<A,B> Arr<A,B>(this Func<A,B> f) {
			return Category.ToKleisli<A,B>(x => f(x).Pure());//     
		}
	}


      
      







	void Arrow() {
		Func<int, int> f1 = y => y + 1;
		Func<int, int> f2 = y => y * 10;

		Func<int, Maybe<int>> fm1 = y => (y * 2).Pure();
		Func<int, Maybe<int>> fm2 = y => (y - 5).Pure();
		var fa1 = f1.Arr();
		var fa2 = f2.Arr();
		var fk1 = fm1.ToKleisli();
		var fk2 = fm2.ToKleisli();
		var fr = fk2.Compose(fa1).Compose(fk1).Compose(fa2);
		var r1 = fr.Func(2); // r1 == Some(36)
		//         var r1 = f2.Compose(f1.Arr()).Compose(f1).Compose(f2.Arr())(2)
	}

      
      





: , .



, . , compose , : (&&&) - , (***) - . .





, , . , C#. , A -> List<A>



, ~90% , . , , Maybe M<A>



M Maybe<A> List<A>



.



, http://anton-k.github.com/ru-haskell-book/book/toc.html, , , ( ) .



haskell





import Prelude hiding (Functor,map,Monad)

class Pointed f where
	pure :: a -> f a

instance Pointed Maybe where
	pure = Just

class Functor f where
	map :: (a -> b) -> f a -> f b

instance Functor Maybe where  
	map f (Just x) = Just (f x)  
	map f Nothing = Nothing  

class (Functor f, Pointed f) => Applicative f where
	apply :: f (a -> b) -> f a -> f b

instance Applicative Maybe where  
	apply Nothing  _ = Nothing  
	apply (Just f) something = map f something  

class Applicative f => Monad f where
	bind :: f a -> (a -> f b) -> f b

instance Monad Maybe  where
	bind (Just x)  k      = k x
	bind Nothing   _      = Nothing

newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }

class Category cat where
	compose :: cat b c -> cat a b -> cat a c

instance Category (->) where
	compose f g = \x -> f (g x) 

instance Monad m => Category (Kleisli m) where
	compose (Kleisli f) (Kleisli g) = Kleisli (\b -> bind (g b) f)

class Category a => Arrow a where
	arr :: (b -> c) -> a b c

instance Arrow (->) where
	arr f = f

instance Monad m => Arrow (Kleisli m) where
	arr f = Kleisli (compose pure f)


      
      







Upd: .

:

Maybe<int> userId = UserService.GetUserId(email);
Maybe<int> productId = ProductService.GetProductId(productCode);
Maybe<int> discount = Maybe<int>.Nothing;
if(userId.HasValue && porductId.HasValue)
    discount = new Maybe(DiscountService.GetDiscount(userId.Value, productId.Value));

      
      





? if, .

:

Func<int,int,int> f = DiscountService.GetDiscount;
Maybe<int> discount = f.Curry().Pure().Apply(userId).Apply(productId);

      
      





? , userId productId, .







All Articles